@stackmemoryai/stackmemory 0.5.33 → 0.5.35

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 (111) hide show
  1. package/dist/agents/core/agent-task-manager.js.map +1 -1
  2. package/dist/cli/commands/clear.js +1 -1
  3. package/dist/cli/commands/clear.js.map +1 -1
  4. package/dist/cli/commands/context.js +1 -1
  5. package/dist/cli/commands/context.js.map +1 -1
  6. package/dist/cli/commands/dashboard.js.map +1 -1
  7. package/dist/cli/commands/discovery.js +1 -1
  8. package/dist/cli/commands/discovery.js.map +1 -1
  9. package/dist/cli/commands/handoff.js +1 -1
  10. package/dist/cli/commands/handoff.js.map +1 -1
  11. package/dist/cli/commands/monitor.js +1 -1
  12. package/dist/cli/commands/monitor.js.map +1 -1
  13. package/dist/cli/commands/quality.js +1 -1
  14. package/dist/cli/commands/quality.js.map +1 -1
  15. package/dist/cli/commands/skills.js +1 -1
  16. package/dist/cli/commands/skills.js.map +1 -1
  17. package/dist/cli/commands/workflow.js +1 -1
  18. package/dist/cli/commands/workflow.js.map +1 -1
  19. package/dist/cli/commands/worktree.js +1 -1
  20. package/dist/cli/commands/worktree.js.map +1 -1
  21. package/dist/cli/index.js +1 -1
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/core/context/auto-context.js.map +1 -1
  24. package/dist/core/context/compaction-handler.js.map +2 -2
  25. package/dist/core/context/context-bridge.js.map +2 -2
  26. package/dist/core/context/dual-stack-manager.js +1 -1
  27. package/dist/core/context/dual-stack-manager.js.map +1 -1
  28. package/dist/core/context/enhanced-rehydration.js.map +1 -1
  29. package/dist/core/context/frame-database.js +43 -10
  30. package/dist/core/context/frame-database.js.map +2 -2
  31. package/dist/core/context/frame-handoff-manager.js.map +1 -1
  32. package/dist/core/context/frame-lifecycle-hooks.js +119 -0
  33. package/dist/core/context/frame-lifecycle-hooks.js.map +7 -0
  34. package/dist/core/context/frame-stack.js +36 -7
  35. package/dist/core/context/frame-stack.js.map +2 -2
  36. package/dist/core/context/incremental-gc.js.map +2 -2
  37. package/dist/core/context/index.js +4 -22
  38. package/dist/core/context/index.js.map +2 -2
  39. package/dist/core/context/refactored-frame-manager.js +170 -37
  40. package/dist/core/context/refactored-frame-manager.js.map +3 -3
  41. package/dist/core/context/shared-context-layer.js.map +1 -1
  42. package/dist/core/context/stack-merge-resolver.js.map +1 -1
  43. package/dist/core/database/database-adapter.js.map +1 -1
  44. package/dist/core/database/paradedb-adapter.js.map +1 -1
  45. package/dist/core/database/query-router.js.map +1 -1
  46. package/dist/core/database/sqlite-adapter.js.map +1 -1
  47. package/dist/core/digest/frame-digest-integration.js.map +1 -1
  48. package/dist/core/digest/hybrid-digest-generator.js.map +1 -1
  49. package/dist/core/digest/types.js.map +1 -1
  50. package/dist/core/errors/index.js +249 -0
  51. package/dist/core/errors/index.js.map +2 -2
  52. package/dist/core/frame/workflow-templates.js.map +2 -2
  53. package/dist/core/merge/conflict-detector.js.map +1 -1
  54. package/dist/core/merge/resolution-engine.js.map +1 -1
  55. package/dist/core/merge/stack-diff.js.map +1 -1
  56. package/dist/core/models/model-router.js +10 -1
  57. package/dist/core/models/model-router.js.map +2 -2
  58. package/dist/core/monitoring/error-handler.js +37 -270
  59. package/dist/core/monitoring/error-handler.js.map +3 -3
  60. package/dist/core/monitoring/session-monitor.js.map +1 -1
  61. package/dist/core/performance/lazy-context-loader.js.map +1 -1
  62. package/dist/core/performance/optimized-frame-context.js.map +1 -1
  63. package/dist/core/retrieval/context-retriever.js.map +1 -1
  64. package/dist/core/retrieval/graph-retrieval.js.map +1 -1
  65. package/dist/core/retrieval/hierarchical-retrieval.js.map +1 -1
  66. package/dist/core/retrieval/llm-context-retrieval.js.map +1 -1
  67. package/dist/core/retrieval/retrieval-benchmarks.js.map +1 -1
  68. package/dist/core/retrieval/summary-generator.js.map +1 -1
  69. package/dist/core/retrieval/types.js.map +1 -1
  70. package/dist/core/storage/chromadb-adapter.js.map +1 -1
  71. package/dist/core/storage/infinite-storage.js.map +1 -1
  72. package/dist/core/storage/two-tier-storage.js.map +1 -1
  73. package/dist/features/tasks/task-aware-context.js.map +1 -1
  74. package/dist/features/web/server/index.js +1 -1
  75. package/dist/features/web/server/index.js.map +1 -1
  76. package/dist/hooks/schemas.js +50 -0
  77. package/dist/hooks/schemas.js.map +2 -2
  78. package/dist/hooks/sms-action-runner.js +47 -1
  79. package/dist/hooks/sms-action-runner.js.map +2 -2
  80. package/dist/hooks/sms-notify.js +63 -1
  81. package/dist/hooks/sms-notify.js.map +2 -2
  82. package/dist/hooks/sms-webhook.js +10 -3
  83. package/dist/hooks/sms-webhook.js.map +2 -2
  84. package/dist/hooks/whatsapp-commands.js +172 -69
  85. package/dist/hooks/whatsapp-commands.js.map +2 -2
  86. package/dist/hooks/whatsapp-sync.js +34 -0
  87. package/dist/hooks/whatsapp-sync.js.map +2 -2
  88. package/dist/index.js +1 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/integrations/mcp/handlers/context-handlers.js.map +1 -1
  91. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +1 -1
  92. package/dist/integrations/mcp/server.js +1 -1
  93. package/dist/integrations/mcp/server.js.map +1 -1
  94. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +1 -1
  95. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +1 -1
  96. package/dist/integrations/ralph/context/stackmemory-context-loader.js +1 -1
  97. package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +1 -1
  98. package/dist/integrations/ralph/learning/pattern-learner.js +1 -1
  99. package/dist/integrations/ralph/learning/pattern-learner.js.map +1 -1
  100. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +1 -1
  101. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +1 -1
  102. package/dist/integrations/ralph/swarm/swarm-coordinator.js +1 -1
  103. package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +1 -1
  104. package/dist/integrations/ralph/visualization/ralph-debugger.js +1 -1
  105. package/dist/integrations/ralph/visualization/ralph-debugger.js.map +1 -1
  106. package/dist/mcp/stackmemory-mcp-server.js +1 -1
  107. package/dist/mcp/stackmemory-mcp-server.js.map +1 -1
  108. package/dist/skills/claude-skills.js.map +1 -1
  109. package/dist/skills/recursive-agent-orchestrator.js.map +1 -1
  110. package/dist/skills/unified-rlm-orchestrator.js.map +1 -1
  111. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/hooks/whatsapp-sync.ts"],
4
- "sourcesContent": ["/**\n * WhatsApp Context Sync Engine\n * Push frame digests and context updates to WhatsApp\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport {\n sendNotification,\n loadSMSConfig,\n saveSMSConfig,\n type NotificationPayload,\n} from './sms-notify.js';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport { SyncOptionsSchema, parseConfigSafe } from './schemas.js';\n\nexport interface SyncOptions {\n autoSyncOnClose: boolean;\n minFrameDuration: number; // seconds, skip short frames\n includeDecisions: boolean;\n includeFiles: boolean;\n includeTests: boolean;\n maxDigestLength: number; // chars, default 400\n}\n\nexport interface SyncResult {\n success: boolean;\n messageId?: string;\n channel?: 'whatsapp' | 'sms';\n error?: string;\n digestLength?: number;\n}\n\nexport interface FrameDigestData {\n frameId: string;\n name: string;\n type: string;\n status: 'success' | 'failure' | 'partial' | 'ongoing';\n durationSeconds: number;\n filesModified: Array<{ path: string; operation: string }>;\n testsRun: Array<{ name: string; status: string }>;\n decisions: string[];\n risks: string[];\n toolCallCount: number;\n errors: Array<{ type: string; message: string; resolved: boolean }>;\n}\n\nconst SYNC_CONFIG_PATH = join(homedir(), '.stackmemory', 'whatsapp-sync.json');\n\nconst DEFAULT_SYNC_OPTIONS: SyncOptions = {\n autoSyncOnClose: false,\n minFrameDuration: 30, // Skip frames shorter than 30 seconds\n includeDecisions: true,\n includeFiles: true,\n includeTests: true,\n maxDigestLength: 400,\n};\n\n/**\n * Load sync options from config file\n */\nexport function loadSyncOptions(): SyncOptions {\n try {\n if (existsSync(SYNC_CONFIG_PATH)) {\n const data = JSON.parse(readFileSync(SYNC_CONFIG_PATH, 'utf8'));\n return parseConfigSafe(\n SyncOptionsSchema,\n { ...DEFAULT_SYNC_OPTIONS, ...data },\n DEFAULT_SYNC_OPTIONS,\n 'whatsapp-sync'\n );\n }\n } catch {\n // Use defaults\n }\n return { ...DEFAULT_SYNC_OPTIONS };\n}\n\n/**\n * Save sync options to config file\n */\nexport function saveSyncOptions(options: SyncOptions): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(SYNC_CONFIG_PATH, JSON.stringify(options, null, 2));\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Format duration for mobile display\n */\nfunction formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${seconds}s`;\n } else if (seconds < 3600) {\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return secs > 0 ? `${mins}m${secs}s` : `${mins}m`;\n } else {\n const hours = Math.floor(seconds / 3600);\n const mins = Math.floor((seconds % 3600) / 60);\n return mins > 0 ? `${hours}h${mins}m` : `${hours}h`;\n }\n}\n\n/**\n * Truncate text to max length with ellipsis\n */\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 3) + '...';\n}\n\n/**\n * Get status symbol for mobile display\n */\nfunction getStatusSymbol(status: string): string {\n switch (status) {\n case 'success':\n return 'OK';\n case 'failure':\n return 'FAIL';\n case 'partial':\n return 'PARTIAL';\n case 'ongoing':\n return 'ACTIVE';\n default:\n return '?';\n }\n}\n\n/**\n * Generate WhatsApp-friendly digest (300-400 chars max)\n * Optimized for mobile readability\n */\nexport function generateMobileDigest(\n data: FrameDigestData,\n options: SyncOptions = DEFAULT_SYNC_OPTIONS\n): string {\n const parts: string[] = [];\n const maxLen = options.maxDigestLength;\n\n // Header: FRAME: Name [type] - duration status\n const header = `FRAME: ${truncate(data.name, 30)} [${data.type}] - ${formatDuration(data.durationSeconds)} ${getStatusSymbol(data.status)}`;\n parts.push(header);\n\n // Activity summary line\n const activityParts: string[] = [];\n\n if (options.includeFiles && data.filesModified.length > 0) {\n activityParts.push(`FILES: ${data.filesModified.length}`);\n }\n\n if (data.toolCallCount > 0) {\n activityParts.push(`TOOLS: ${data.toolCallCount}`);\n }\n\n if (options.includeTests && data.testsRun.length > 0) {\n const passed = data.testsRun.filter((t) => t.status === 'passed').length;\n const failed = data.testsRun.filter((t) => t.status === 'failed').length;\n if (failed > 0) {\n activityParts.push(`TESTS: ${passed}ok/${failed}fail`);\n } else {\n activityParts.push(`TESTS: ${passed} pass`);\n }\n }\n\n if (activityParts.length > 0) {\n parts.push(activityParts.join(' | '));\n }\n\n // Files modified (compact)\n if (options.includeFiles && data.filesModified.length > 0) {\n const fileList = data.filesModified\n .slice(0, 3)\n .map((f) => {\n const basename = f.path.split('/').pop() || f.path;\n const op = f.operation.charAt(0).toUpperCase();\n return `${op}:${truncate(basename, 20)}`;\n })\n .join(', ');\n const more =\n data.filesModified.length > 3 ? ` +${data.filesModified.length - 3}` : '';\n parts.push(` ${fileList}${more}`);\n }\n\n // Decisions (high value)\n if (options.includeDecisions && data.decisions.length > 0) {\n parts.push('');\n parts.push('DECISIONS:');\n data.decisions.slice(0, 3).forEach((d) => {\n parts.push(` ${truncate(d, 60)}`);\n });\n if (data.decisions.length > 3) {\n parts.push(` +${data.decisions.length - 3} more`);\n }\n }\n\n // Risks (important to surface)\n if (data.risks.length > 0) {\n parts.push('');\n parts.push('RISKS:');\n data.risks.slice(0, 2).forEach((r) => {\n parts.push(` ${truncate(r, 50)}`);\n });\n }\n\n // Errors (unresolved only)\n const unresolvedErrors = data.errors.filter((e) => !e.resolved);\n if (unresolvedErrors.length > 0) {\n parts.push('');\n parts.push(`ERRORS: ${unresolvedErrors.length} unresolved`);\n unresolvedErrors.slice(0, 2).forEach((e) => {\n parts.push(` ${truncate(e.message, 50)}`);\n });\n }\n\n // Next action suggestion\n parts.push('');\n if (data.status === 'success') {\n parts.push('NEXT: commit & test');\n } else if (data.status === 'failure') {\n parts.push('NEXT: fix errors');\n } else if (data.status === 'partial') {\n parts.push('NEXT: review & continue');\n } else {\n parts.push('NEXT: check status');\n }\n\n // Join and truncate final result\n let result = parts.join('\\n');\n\n // If too long, trim aggressively\n if (result.length > maxLen) {\n // Remove less important sections\n const essentialParts = [header];\n\n if (activityParts.length > 0) {\n essentialParts.push(activityParts.join(' | '));\n }\n\n if (options.includeDecisions && data.decisions.length > 0) {\n essentialParts.push('');\n essentialParts.push(`DECISIONS: ${data.decisions.length}`);\n essentialParts.push(` ${truncate(data.decisions[0], 50)}`);\n }\n\n if (unresolvedErrors.length > 0) {\n essentialParts.push('');\n essentialParts.push(`ERRORS: ${unresolvedErrors.length} unresolved`);\n }\n\n essentialParts.push('');\n essentialParts.push(\n data.status === 'success' ? 'NEXT: commit' : 'NEXT: review'\n );\n\n result = essentialParts.join('\\n');\n }\n\n return result.slice(0, maxLen);\n}\n\n/**\n * Get frame digest data from stackmemory database\n * Returns null if frame not found or context unavailable\n */\nexport async function getFrameDigestData(\n frameId?: string\n): Promise<FrameDigestData | null> {\n try {\n // Try to load from recent frame digests\n const digestPath = join(\n homedir(),\n '.stackmemory',\n 'latest-frame-digest.json'\n );\n\n if (existsSync(digestPath)) {\n const data = JSON.parse(readFileSync(digestPath, 'utf8'));\n\n // If frameId specified, check if it matches\n if (frameId && data.frameId !== frameId) {\n return null;\n }\n\n return data as FrameDigestData;\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Store frame digest for sync\n */\nexport function storeFrameDigest(data: FrameDigestData): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n const digestPath = join(\n homedir(),\n '.stackmemory',\n 'latest-frame-digest.json'\n );\n writeFileSecure(digestPath, JSON.stringify(data, null, 2));\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Push current context to WhatsApp\n */\nexport async function syncContext(): Promise<SyncResult> {\n const options = loadSyncOptions();\n const data = await getFrameDigestData();\n\n if (!data) {\n return {\n success: false,\n error: 'No context data available. Run a task first.',\n };\n }\n\n return syncFrameData(data, options);\n}\n\n/**\n * Sync specific frame by ID\n */\nexport async function syncFrame(frameId: string): Promise<SyncResult> {\n const options = loadSyncOptions();\n const data = await getFrameDigestData(frameId);\n\n if (!data) {\n return {\n success: false,\n error: `Frame not found: ${frameId}`,\n };\n }\n\n return syncFrameData(data, options);\n}\n\n/**\n * Sync frame data to WhatsApp\n */\nasync function syncFrameData(\n data: FrameDigestData,\n options: SyncOptions\n): Promise<SyncResult> {\n const config = loadSMSConfig();\n\n if (!config.enabled) {\n return { success: false, error: 'Notifications disabled' };\n }\n\n // Check minimum duration\n if (data.durationSeconds < options.minFrameDuration) {\n return {\n success: false,\n error: `Frame too short (${data.durationSeconds}s < ${options.minFrameDuration}s min)`,\n };\n }\n\n // Generate mobile digest\n const digest = generateMobileDigest(data, options);\n\n // Send notification\n const payload: NotificationPayload = {\n type: 'custom',\n title: 'Context Sync',\n message: digest,\n prompt: {\n type: 'options',\n options: [\n { key: '1', label: 'Commit', action: 'git add -A && git commit' },\n { key: '2', label: 'Status', action: 'stackmemory status' },\n { key: '3', label: 'Continue', action: 'echo \"Continuing...\"' },\n ],\n question: 'Action?',\n },\n };\n\n const result = await sendNotification(payload);\n\n return {\n success: result.success,\n messageId: result.promptId,\n channel: result.channel,\n error: result.error,\n digestLength: digest.length,\n };\n}\n\n/**\n * Enable auto-sync on frame close\n */\nexport function enableAutoSync(options?: Partial<SyncOptions>): void {\n const current = loadSyncOptions();\n const updated: SyncOptions = {\n ...current,\n ...options,\n autoSyncOnClose: true,\n };\n saveSyncOptions(updated);\n\n // Update SMS config to enable context_sync notifications\n const smsConfig = loadSMSConfig();\n if (!smsConfig.notifyOn.custom) {\n smsConfig.notifyOn.custom = true;\n saveSMSConfig(smsConfig);\n }\n}\n\n/**\n * Disable auto-sync\n */\nexport function disableAutoSync(): void {\n const current = loadSyncOptions();\n current.autoSyncOnClose = false;\n saveSyncOptions(current);\n}\n\n/**\n * Check if auto-sync is enabled\n */\nexport function isAutoSyncEnabled(): boolean {\n const options = loadSyncOptions();\n return options.autoSyncOnClose;\n}\n\n/**\n * Callback for frame manager to trigger auto-sync\n * Call this when a frame closes\n */\nexport async function onFrameClosed(\n frameData: FrameDigestData\n): Promise<SyncResult | null> {\n if (!isAutoSyncEnabled()) {\n return null;\n }\n\n // Store for potential manual sync later\n storeFrameDigest(frameData);\n\n // Auto-sync\n const options = loadSyncOptions();\n return syncFrameData(frameData, options);\n}\n\n/**\n * Create frame digest data from raw frame info\n * Helper for integration with frame-manager\n */\nexport function createFrameDigestData(\n frame: {\n frame_id: string;\n name: string;\n type: string;\n created_at: number;\n closed_at?: number;\n },\n events: Array<{\n event_type: string;\n payload: Record<string, unknown>;\n }>,\n anchors: Array<{\n type: string;\n text: string;\n }>\n): FrameDigestData {\n const now = Math.floor(Date.now() / 1000);\n const duration = (frame.closed_at || now) - frame.created_at;\n\n // Extract files from tool_call events\n const filesModified: Array<{ path: string; operation: string }> = [];\n const filesSeen = new Set<string>();\n\n events\n .filter((e) => e.event_type === 'tool_call')\n .forEach((e) => {\n const path = e.payload['path'] as string | undefined;\n if (path && !filesSeen.has(path)) {\n filesSeen.add(path);\n const toolName = (e.payload['tool_name'] as string) || '';\n let operation = 'modify';\n if (toolName.includes('Write') || toolName.includes('Create')) {\n operation = 'create';\n } else if (toolName.includes('Read')) {\n operation = 'read';\n } else if (toolName.includes('Delete')) {\n operation = 'delete';\n }\n filesModified.push({ path, operation });\n }\n });\n\n // Extract tests\n const testsRun: Array<{ name: string; status: string }> = [];\n events\n .filter(\n (e) =>\n e.event_type === 'tool_result' &&\n String(e.payload['output'] || '').includes('test')\n )\n .forEach((e) => {\n const output = String(e.payload['output'] || '');\n // Simple test result extraction\n const passMatch = output.match(/(\\d+) pass/i);\n const failMatch = output.match(/(\\d+) fail/i);\n if (passMatch) {\n testsRun.push({ name: 'Tests', status: 'passed' });\n }\n if (failMatch && parseInt(failMatch[1]) > 0) {\n testsRun.push({ name: 'Tests', status: 'failed' });\n }\n });\n\n // Extract decisions and risks from anchors\n const decisions = anchors\n .filter((a) => a.type === 'DECISION')\n .map((a) => a.text);\n\n const risks = anchors.filter((a) => a.type === 'RISK').map((a) => a.text);\n\n // Extract errors\n const errors: Array<{ type: string; message: string; resolved: boolean }> =\n [];\n events\n .filter((e) => e.payload['error'] || e.payload['status'] === 'error')\n .forEach((e) => {\n const errorMsg =\n (e.payload['error'] as string) ||\n (e.payload['message'] as string) ||\n 'Unknown error';\n errors.push({\n type: (e.payload['type'] as string) || 'error',\n message: errorMsg,\n resolved: false,\n });\n });\n\n // Determine status\n let status: 'success' | 'failure' | 'partial' | 'ongoing' = 'ongoing';\n if (frame.closed_at) {\n if (errors.filter((e) => !e.resolved).length > 0) {\n status = 'failure';\n } else if (\n testsRun.some((t) => t.status === 'failed') ||\n filesModified.length === 0\n ) {\n status = 'partial';\n } else {\n status = 'success';\n }\n }\n\n // Count tool calls\n const toolCallCount = events.filter(\n (e) => e.event_type === 'tool_call'\n ).length;\n\n return {\n frameId: frame.frame_id,\n name: frame.name,\n type: frame.type,\n status,\n durationSeconds: duration,\n filesModified: filesModified.filter((f) => f.operation !== 'read'),\n testsRun,\n decisions,\n risks,\n toolCallCount,\n errors,\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * WhatsApp Context Sync Engine\n * Push frame digests and context updates to WhatsApp\n *\n * Uses the frame lifecycle hooks system to receive frame close events.\n * Call `registerWhatsAppSyncHook()` to enable automatic sync on frame close.\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport {\n sendNotification,\n loadSMSConfig,\n saveSMSConfig,\n type NotificationPayload,\n} from './sms-notify.js';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport { SyncOptionsSchema, parseConfigSafe } from './schemas.js';\nimport {\n frameLifecycleHooks,\n type FrameCloseData,\n} from '../core/context/frame-lifecycle-hooks.js';\n\nexport interface SyncOptions {\n autoSyncOnClose: boolean;\n minFrameDuration: number; // seconds, skip short frames\n includeDecisions: boolean;\n includeFiles: boolean;\n includeTests: boolean;\n maxDigestLength: number; // chars, default 400\n}\n\nexport interface SyncResult {\n success: boolean;\n messageId?: string;\n channel?: 'whatsapp' | 'sms';\n error?: string;\n digestLength?: number;\n}\n\nexport interface FrameDigestData {\n frameId: string;\n name: string;\n type: string;\n status: 'success' | 'failure' | 'partial' | 'ongoing';\n durationSeconds: number;\n filesModified: Array<{ path: string; operation: string }>;\n testsRun: Array<{ name: string; status: string }>;\n decisions: string[];\n risks: string[];\n toolCallCount: number;\n errors: Array<{ type: string; message: string; resolved: boolean }>;\n}\n\nconst SYNC_CONFIG_PATH = join(homedir(), '.stackmemory', 'whatsapp-sync.json');\n\nconst DEFAULT_SYNC_OPTIONS: SyncOptions = {\n autoSyncOnClose: false,\n minFrameDuration: 30, // Skip frames shorter than 30 seconds\n includeDecisions: true,\n includeFiles: true,\n includeTests: true,\n maxDigestLength: 400,\n};\n\n/**\n * Load sync options from config file\n */\nexport function loadSyncOptions(): SyncOptions {\n try {\n if (existsSync(SYNC_CONFIG_PATH)) {\n const data = JSON.parse(readFileSync(SYNC_CONFIG_PATH, 'utf8'));\n return parseConfigSafe(\n SyncOptionsSchema,\n { ...DEFAULT_SYNC_OPTIONS, ...data },\n DEFAULT_SYNC_OPTIONS,\n 'whatsapp-sync'\n );\n }\n } catch {\n // Use defaults\n }\n return { ...DEFAULT_SYNC_OPTIONS };\n}\n\n/**\n * Save sync options to config file\n */\nexport function saveSyncOptions(options: SyncOptions): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(SYNC_CONFIG_PATH, JSON.stringify(options, null, 2));\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Format duration for mobile display\n */\nfunction formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${seconds}s`;\n } else if (seconds < 3600) {\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return secs > 0 ? `${mins}m${secs}s` : `${mins}m`;\n } else {\n const hours = Math.floor(seconds / 3600);\n const mins = Math.floor((seconds % 3600) / 60);\n return mins > 0 ? `${hours}h${mins}m` : `${hours}h`;\n }\n}\n\n/**\n * Truncate text to max length with ellipsis\n */\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - 3) + '...';\n}\n\n/**\n * Get status symbol for mobile display\n */\nfunction getStatusSymbol(status: string): string {\n switch (status) {\n case 'success':\n return 'OK';\n case 'failure':\n return 'FAIL';\n case 'partial':\n return 'PARTIAL';\n case 'ongoing':\n return 'ACTIVE';\n default:\n return '?';\n }\n}\n\n/**\n * Generate WhatsApp-friendly digest (300-400 chars max)\n * Optimized for mobile readability\n */\nexport function generateMobileDigest(\n data: FrameDigestData,\n options: SyncOptions = DEFAULT_SYNC_OPTIONS\n): string {\n const parts: string[] = [];\n const maxLen = options.maxDigestLength;\n\n // Header: FRAME: Name [type] - duration status\n const header = `FRAME: ${truncate(data.name, 30)} [${data.type}] - ${formatDuration(data.durationSeconds)} ${getStatusSymbol(data.status)}`;\n parts.push(header);\n\n // Activity summary line\n const activityParts: string[] = [];\n\n if (options.includeFiles && data.filesModified.length > 0) {\n activityParts.push(`FILES: ${data.filesModified.length}`);\n }\n\n if (data.toolCallCount > 0) {\n activityParts.push(`TOOLS: ${data.toolCallCount}`);\n }\n\n if (options.includeTests && data.testsRun.length > 0) {\n const passed = data.testsRun.filter((t) => t.status === 'passed').length;\n const failed = data.testsRun.filter((t) => t.status === 'failed').length;\n if (failed > 0) {\n activityParts.push(`TESTS: ${passed}ok/${failed}fail`);\n } else {\n activityParts.push(`TESTS: ${passed} pass`);\n }\n }\n\n if (activityParts.length > 0) {\n parts.push(activityParts.join(' | '));\n }\n\n // Files modified (compact)\n if (options.includeFiles && data.filesModified.length > 0) {\n const fileList = data.filesModified\n .slice(0, 3)\n .map((f) => {\n const basename = f.path.split('/').pop() || f.path;\n const op = f.operation.charAt(0).toUpperCase();\n return `${op}:${truncate(basename, 20)}`;\n })\n .join(', ');\n const more =\n data.filesModified.length > 3 ? ` +${data.filesModified.length - 3}` : '';\n parts.push(` ${fileList}${more}`);\n }\n\n // Decisions (high value)\n if (options.includeDecisions && data.decisions.length > 0) {\n parts.push('');\n parts.push('DECISIONS:');\n data.decisions.slice(0, 3).forEach((d) => {\n parts.push(` ${truncate(d, 60)}`);\n });\n if (data.decisions.length > 3) {\n parts.push(` +${data.decisions.length - 3} more`);\n }\n }\n\n // Risks (important to surface)\n if (data.risks.length > 0) {\n parts.push('');\n parts.push('RISKS:');\n data.risks.slice(0, 2).forEach((r) => {\n parts.push(` ${truncate(r, 50)}`);\n });\n }\n\n // Errors (unresolved only)\n const unresolvedErrors = data.errors.filter((e) => !e.resolved);\n if (unresolvedErrors.length > 0) {\n parts.push('');\n parts.push(`ERRORS: ${unresolvedErrors.length} unresolved`);\n unresolvedErrors.slice(0, 2).forEach((e) => {\n parts.push(` ${truncate(e.message, 50)}`);\n });\n }\n\n // Next action suggestion\n parts.push('');\n if (data.status === 'success') {\n parts.push('NEXT: commit & test');\n } else if (data.status === 'failure') {\n parts.push('NEXT: fix errors');\n } else if (data.status === 'partial') {\n parts.push('NEXT: review & continue');\n } else {\n parts.push('NEXT: check status');\n }\n\n // Join and truncate final result\n let result = parts.join('\\n');\n\n // If too long, trim aggressively\n if (result.length > maxLen) {\n // Remove less important sections\n const essentialParts = [header];\n\n if (activityParts.length > 0) {\n essentialParts.push(activityParts.join(' | '));\n }\n\n if (options.includeDecisions && data.decisions.length > 0) {\n essentialParts.push('');\n essentialParts.push(`DECISIONS: ${data.decisions.length}`);\n essentialParts.push(` ${truncate(data.decisions[0], 50)}`);\n }\n\n if (unresolvedErrors.length > 0) {\n essentialParts.push('');\n essentialParts.push(`ERRORS: ${unresolvedErrors.length} unresolved`);\n }\n\n essentialParts.push('');\n essentialParts.push(\n data.status === 'success' ? 'NEXT: commit' : 'NEXT: review'\n );\n\n result = essentialParts.join('\\n');\n }\n\n return result.slice(0, maxLen);\n}\n\n/**\n * Get frame digest data from stackmemory database\n * Returns null if frame not found or context unavailable\n */\nexport async function getFrameDigestData(\n frameId?: string\n): Promise<FrameDigestData | null> {\n try {\n // Try to load from recent frame digests\n const digestPath = join(\n homedir(),\n '.stackmemory',\n 'latest-frame-digest.json'\n );\n\n if (existsSync(digestPath)) {\n const data = JSON.parse(readFileSync(digestPath, 'utf8'));\n\n // If frameId specified, check if it matches\n if (frameId && data.frameId !== frameId) {\n return null;\n }\n\n return data as FrameDigestData;\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Store frame digest for sync\n */\nexport function storeFrameDigest(data: FrameDigestData): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n const digestPath = join(\n homedir(),\n '.stackmemory',\n 'latest-frame-digest.json'\n );\n writeFileSecure(digestPath, JSON.stringify(data, null, 2));\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Push current context to WhatsApp\n */\nexport async function syncContext(): Promise<SyncResult> {\n const options = loadSyncOptions();\n const data = await getFrameDigestData();\n\n if (!data) {\n return {\n success: false,\n error: 'No context data available. Run a task first.',\n };\n }\n\n return syncFrameData(data, options);\n}\n\n/**\n * Sync specific frame by ID\n */\nexport async function syncFrame(frameId: string): Promise<SyncResult> {\n const options = loadSyncOptions();\n const data = await getFrameDigestData(frameId);\n\n if (!data) {\n return {\n success: false,\n error: `Frame not found: ${frameId}`,\n };\n }\n\n return syncFrameData(data, options);\n}\n\n/**\n * Sync frame data to WhatsApp\n */\nasync function syncFrameData(\n data: FrameDigestData,\n options: SyncOptions\n): Promise<SyncResult> {\n const config = loadSMSConfig();\n\n if (!config.enabled) {\n return { success: false, error: 'Notifications disabled' };\n }\n\n // Check minimum duration\n if (data.durationSeconds < options.minFrameDuration) {\n return {\n success: false,\n error: `Frame too short (${data.durationSeconds}s < ${options.minFrameDuration}s min)`,\n };\n }\n\n // Generate mobile digest\n const digest = generateMobileDigest(data, options);\n\n // Send notification\n const payload: NotificationPayload = {\n type: 'custom',\n title: 'Context Sync',\n message: digest,\n prompt: {\n type: 'options',\n options: [\n { key: '1', label: 'Commit', action: 'git add -A && git commit' },\n { key: '2', label: 'Status', action: 'stackmemory status' },\n { key: '3', label: 'Continue', action: 'echo \"Continuing...\"' },\n ],\n question: 'Action?',\n },\n };\n\n const result = await sendNotification(payload);\n\n return {\n success: result.success,\n messageId: result.promptId,\n channel: result.channel,\n error: result.error,\n digestLength: digest.length,\n };\n}\n\n/**\n * Enable auto-sync on frame close\n */\nexport function enableAutoSync(options?: Partial<SyncOptions>): void {\n const current = loadSyncOptions();\n const updated: SyncOptions = {\n ...current,\n ...options,\n autoSyncOnClose: true,\n };\n saveSyncOptions(updated);\n\n // Update SMS config to enable context_sync notifications\n const smsConfig = loadSMSConfig();\n if (!smsConfig.notifyOn.custom) {\n smsConfig.notifyOn.custom = true;\n saveSMSConfig(smsConfig);\n }\n}\n\n/**\n * Disable auto-sync\n */\nexport function disableAutoSync(): void {\n const current = loadSyncOptions();\n current.autoSyncOnClose = false;\n saveSyncOptions(current);\n}\n\n/**\n * Check if auto-sync is enabled\n */\nexport function isAutoSyncEnabled(): boolean {\n const options = loadSyncOptions();\n return options.autoSyncOnClose;\n}\n\n/**\n * Callback for frame manager to trigger auto-sync\n * Call this when a frame closes\n */\nexport async function onFrameClosed(\n frameData: FrameDigestData\n): Promise<SyncResult | null> {\n if (!isAutoSyncEnabled()) {\n return null;\n }\n\n // Store for potential manual sync later\n storeFrameDigest(frameData);\n\n // Auto-sync\n const options = loadSyncOptions();\n return syncFrameData(frameData, options);\n}\n\n/**\n * Internal hook handler that receives FrameCloseData from lifecycle hooks\n */\nasync function handleFrameCloseHook(data: FrameCloseData): Promise<void> {\n const digestData = createFrameDigestData(\n data.frame,\n data.events,\n data.anchors\n );\n await onFrameClosed(digestData);\n}\n\n// Track if hook is registered to avoid duplicates\nlet hookUnregister: (() => void) | null = null;\n\n/**\n * Register WhatsApp sync as a frame lifecycle hook\n * This enables automatic sync when frames are closed\n * Call this during app initialization to enable the integration\n *\n * @returns Unregister function to disable the hook\n */\nexport function registerWhatsAppSyncHook(): () => void {\n // Avoid duplicate registration\n if (hookUnregister) {\n return hookUnregister;\n }\n\n hookUnregister = frameLifecycleHooks.onFrameClosed(\n 'whatsapp-sync',\n handleFrameCloseHook,\n -10 // Low priority - run after other hooks\n );\n\n return () => {\n if (hookUnregister) {\n hookUnregister();\n hookUnregister = null;\n }\n };\n}\n\n/**\n * Check if the WhatsApp sync hook is currently registered\n */\nexport function isHookRegistered(): boolean {\n return hookUnregister !== null;\n}\n\n/**\n * Create frame digest data from raw frame info\n * Helper for integration with frame-manager\n */\nexport function createFrameDigestData(\n frame: {\n frame_id: string;\n name: string;\n type: string;\n created_at: number;\n closed_at?: number;\n },\n events: Array<{\n event_type: string;\n payload: Record<string, unknown>;\n }>,\n anchors: Array<{\n type: string;\n text: string;\n }>\n): FrameDigestData {\n const now = Math.floor(Date.now() / 1000);\n const duration = (frame.closed_at || now) - frame.created_at;\n\n // Extract files from tool_call events\n const filesModified: Array<{ path: string; operation: string }> = [];\n const filesSeen = new Set<string>();\n\n events\n .filter((e) => e.event_type === 'tool_call')\n .forEach((e) => {\n const path = e.payload['path'] as string | undefined;\n if (path && !filesSeen.has(path)) {\n filesSeen.add(path);\n const toolName = (e.payload['tool_name'] as string) || '';\n let operation = 'modify';\n if (toolName.includes('Write') || toolName.includes('Create')) {\n operation = 'create';\n } else if (toolName.includes('Read')) {\n operation = 'read';\n } else if (toolName.includes('Delete')) {\n operation = 'delete';\n }\n filesModified.push({ path, operation });\n }\n });\n\n // Extract tests\n const testsRun: Array<{ name: string; status: string }> = [];\n events\n .filter(\n (e) =>\n e.event_type === 'tool_result' &&\n String(e.payload['output'] || '').includes('test')\n )\n .forEach((e) => {\n const output = String(e.payload['output'] || '');\n // Simple test result extraction\n const passMatch = output.match(/(\\d+) pass/i);\n const failMatch = output.match(/(\\d+) fail/i);\n if (passMatch) {\n testsRun.push({ name: 'Tests', status: 'passed' });\n }\n if (failMatch && parseInt(failMatch[1]) > 0) {\n testsRun.push({ name: 'Tests', status: 'failed' });\n }\n });\n\n // Extract decisions and risks from anchors\n const decisions = anchors\n .filter((a) => a.type === 'DECISION')\n .map((a) => a.text);\n\n const risks = anchors.filter((a) => a.type === 'RISK').map((a) => a.text);\n\n // Extract errors\n const errors: Array<{ type: string; message: string; resolved: boolean }> =\n [];\n events\n .filter((e) => e.payload['error'] || e.payload['status'] === 'error')\n .forEach((e) => {\n const errorMsg =\n (e.payload['error'] as string) ||\n (e.payload['message'] as string) ||\n 'Unknown error';\n errors.push({\n type: (e.payload['type'] as string) || 'error',\n message: errorMsg,\n resolved: false,\n });\n });\n\n // Determine status\n let status: 'success' | 'failure' | 'partial' | 'ongoing' = 'ongoing';\n if (frame.closed_at) {\n if (errors.filter((e) => !e.resolved).length > 0) {\n status = 'failure';\n } else if (\n testsRun.some((t) => t.status === 'failed') ||\n filesModified.length === 0\n ) {\n status = 'partial';\n } else {\n status = 'success';\n }\n }\n\n // Count tool calls\n const toolCallCount = events.filter(\n (e) => e.event_type === 'tool_call'\n ).length;\n\n return {\n frameId: frame.frame_id,\n name: frame.name,\n type: frame.type,\n status,\n durationSeconds: duration,\n filesModified: filesModified.filter((f) => f.operation !== 'read'),\n testsRun,\n decisions,\n risks,\n toolCallCount,\n errors,\n };\n}\n"],
5
+ "mappings": ";;;;AAQA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,iBAAiB,uBAAuB;AACjD,SAAS,mBAAmB,uBAAuB;AACnD;AAAA,EACE;AAAA,OAEK;AAiCP,MAAM,mBAAmB,KAAK,QAAQ,GAAG,gBAAgB,oBAAoB;AAE7E,MAAM,uBAAoC;AAAA,EACxC,iBAAiB;AAAA,EACjB,kBAAkB;AAAA;AAAA,EAClB,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,iBAAiB;AACnB;AAKO,SAAS,kBAA+B;AAC7C,MAAI;AACF,QAAI,WAAW,gBAAgB,GAAG;AAChC,YAAM,OAAO,KAAK,MAAM,aAAa,kBAAkB,MAAM,CAAC;AAC9D,aAAO;AAAA,QACL;AAAA,QACA,EAAE,GAAG,sBAAsB,GAAG,KAAK;AAAA,QACnC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,GAAG,qBAAqB;AACnC;AAKO,SAAS,gBAAgB,SAA4B;AAC1D,MAAI;AACF,oBAAgB,KAAK,QAAQ,GAAG,cAAc,CAAC;AAC/C,oBAAgB,kBAAkB,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,EACpE,QAAQ;AAAA,EAER;AACF;AAKA,SAAS,eAAe,SAAyB;AAC/C,MAAI,UAAU,IAAI;AAChB,WAAO,GAAG,OAAO;AAAA,EACnB,WAAW,UAAU,MAAM;AACzB,UAAM,OAAO,KAAK,MAAM,UAAU,EAAE;AACpC,UAAM,OAAO,UAAU;AACvB,WAAO,OAAO,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI;AAAA,EAChD,OAAO;AACL,UAAM,QAAQ,KAAK,MAAM,UAAU,IAAI;AACvC,UAAM,OAAO,KAAK,MAAO,UAAU,OAAQ,EAAE;AAC7C,WAAO,OAAO,IAAI,GAAG,KAAK,IAAI,IAAI,MAAM,GAAG,KAAK;AAAA,EAClD;AACF;AAKA,SAAS,SAAS,MAAc,QAAwB;AACtD,MAAI,KAAK,UAAU,OAAQ,QAAO;AAClC,SAAO,KAAK,MAAM,GAAG,SAAS,CAAC,IAAI;AACrC;AAKA,SAAS,gBAAgB,QAAwB;AAC/C,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAMO,SAAS,qBACd,MACA,UAAuB,sBACf;AACR,QAAM,QAAkB,CAAC;AACzB,QAAM,SAAS,QAAQ;AAGvB,QAAM,SAAS,UAAU,SAAS,KAAK,MAAM,EAAE,CAAC,KAAK,KAAK,IAAI,OAAO,eAAe,KAAK,eAAe,CAAC,IAAI,gBAAgB,KAAK,MAAM,CAAC;AACzI,QAAM,KAAK,MAAM;AAGjB,QAAM,gBAA0B,CAAC;AAEjC,MAAI,QAAQ,gBAAgB,KAAK,cAAc,SAAS,GAAG;AACzD,kBAAc,KAAK,UAAU,KAAK,cAAc,MAAM,EAAE;AAAA,EAC1D;AAEA,MAAI,KAAK,gBAAgB,GAAG;AAC1B,kBAAc,KAAK,UAAU,KAAK,aAAa,EAAE;AAAA,EACnD;AAEA,MAAI,QAAQ,gBAAgB,KAAK,SAAS,SAAS,GAAG;AACpD,UAAM,SAAS,KAAK,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE;AAClE,UAAM,SAAS,KAAK,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE;AAClE,QAAI,SAAS,GAAG;AACd,oBAAc,KAAK,UAAU,MAAM,MAAM,MAAM,MAAM;AAAA,IACvD,OAAO;AACL,oBAAc,KAAK,UAAU,MAAM,OAAO;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,KAAK,cAAc,KAAK,KAAK,CAAC;AAAA,EACtC;AAGA,MAAI,QAAQ,gBAAgB,KAAK,cAAc,SAAS,GAAG;AACzD,UAAM,WAAW,KAAK,cACnB,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM;AACV,YAAM,WAAW,EAAE,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE;AAC9C,YAAM,KAAK,EAAE,UAAU,OAAO,CAAC,EAAE,YAAY;AAC7C,aAAO,GAAG,EAAE,IAAI,SAAS,UAAU,EAAE,CAAC;AAAA,IACxC,CAAC,EACA,KAAK,IAAI;AACZ,UAAM,OACJ,KAAK,cAAc,SAAS,IAAI,KAAK,KAAK,cAAc,SAAS,CAAC,KAAK;AACzE,UAAM,KAAK,KAAK,QAAQ,GAAG,IAAI,EAAE;AAAA,EACnC;AAGA,MAAI,QAAQ,oBAAoB,KAAK,UAAU,SAAS,GAAG;AACzD,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,YAAY;AACvB,SAAK,UAAU,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM;AACxC,YAAM,KAAK,KAAK,SAAS,GAAG,EAAE,CAAC,EAAE;AAAA,IACnC,CAAC;AACD,QAAI,KAAK,UAAU,SAAS,GAAG;AAC7B,YAAM,KAAK,MAAM,KAAK,UAAU,SAAS,CAAC,OAAO;AAAA,IACnD;AAAA,EACF;AAGA,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,QAAQ;AACnB,SAAK,MAAM,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM;AACpC,YAAM,KAAK,KAAK,SAAS,GAAG,EAAE,CAAC,EAAE;AAAA,IACnC,CAAC;AAAA,EACH;AAGA,QAAM,mBAAmB,KAAK,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AAC9D,MAAI,iBAAiB,SAAS,GAAG;AAC/B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,WAAW,iBAAiB,MAAM,aAAa;AAC1D,qBAAiB,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM;AAC1C,YAAM,KAAK,KAAK,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE;AAAA,IAC3C,CAAC;AAAA,EACH;AAGA,QAAM,KAAK,EAAE;AACb,MAAI,KAAK,WAAW,WAAW;AAC7B,UAAM,KAAK,qBAAqB;AAAA,EAClC,WAAW,KAAK,WAAW,WAAW;AACpC,UAAM,KAAK,kBAAkB;AAAA,EAC/B,WAAW,KAAK,WAAW,WAAW;AACpC,UAAM,KAAK,yBAAyB;AAAA,EACtC,OAAO;AACL,UAAM,KAAK,oBAAoB;AAAA,EACjC;AAGA,MAAI,SAAS,MAAM,KAAK,IAAI;AAG5B,MAAI,OAAO,SAAS,QAAQ;AAE1B,UAAM,iBAAiB,CAAC,MAAM;AAE9B,QAAI,cAAc,SAAS,GAAG;AAC5B,qBAAe,KAAK,cAAc,KAAK,KAAK,CAAC;AAAA,IAC/C;AAEA,QAAI,QAAQ,oBAAoB,KAAK,UAAU,SAAS,GAAG;AACzD,qBAAe,KAAK,EAAE;AACtB,qBAAe,KAAK,cAAc,KAAK,UAAU,MAAM,EAAE;AACzD,qBAAe,KAAK,KAAK,SAAS,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE;AAAA,IAC5D;AAEA,QAAI,iBAAiB,SAAS,GAAG;AAC/B,qBAAe,KAAK,EAAE;AACtB,qBAAe,KAAK,WAAW,iBAAiB,MAAM,aAAa;AAAA,IACrE;AAEA,mBAAe,KAAK,EAAE;AACtB,mBAAe;AAAA,MACb,KAAK,WAAW,YAAY,iBAAiB;AAAA,IAC/C;AAEA,aAAS,eAAe,KAAK,IAAI;AAAA,EACnC;AAEA,SAAO,OAAO,MAAM,GAAG,MAAM;AAC/B;AAMA,eAAsB,mBACpB,SACiC;AACjC,MAAI;AAEF,UAAM,aAAa;AAAA,MACjB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAEA,QAAI,WAAW,UAAU,GAAG;AAC1B,YAAM,OAAO,KAAK,MAAM,aAAa,YAAY,MAAM,CAAC;AAGxD,UAAI,WAAW,KAAK,YAAY,SAAS;AACvC,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,iBAAiB,MAA6B;AAC5D,MAAI;AACF,oBAAgB,KAAK,QAAQ,GAAG,cAAc,CAAC;AAC/C,UAAM,aAAa;AAAA,MACjB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF;AACA,oBAAgB,YAAY,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,EAC3D,QAAQ;AAAA,EAER;AACF;AAKA,eAAsB,cAAmC;AACvD,QAAM,UAAU,gBAAgB;AAChC,QAAM,OAAO,MAAM,mBAAmB;AAEtC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,cAAc,MAAM,OAAO;AACpC;AAKA,eAAsB,UAAU,SAAsC;AACpE,QAAM,UAAU,gBAAgB;AAChC,QAAM,OAAO,MAAM,mBAAmB,OAAO;AAE7C,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,oBAAoB,OAAO;AAAA,IACpC;AAAA,EACF;AAEA,SAAO,cAAc,MAAM,OAAO;AACpC;AAKA,eAAe,cACb,MACA,SACqB;AACrB,QAAM,SAAS,cAAc;AAE7B,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,EAAE,SAAS,OAAO,OAAO,yBAAyB;AAAA,EAC3D;AAGA,MAAI,KAAK,kBAAkB,QAAQ,kBAAkB;AACnD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,oBAAoB,KAAK,eAAe,OAAO,QAAQ,gBAAgB;AAAA,IAChF;AAAA,EACF;AAGA,QAAM,SAAS,qBAAqB,MAAM,OAAO;AAGjD,QAAM,UAA+B;AAAA,IACnC,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,QACP,EAAE,KAAK,KAAK,OAAO,UAAU,QAAQ,2BAA2B;AAAA,QAChE,EAAE,KAAK,KAAK,OAAO,UAAU,QAAQ,qBAAqB;AAAA,QAC1D,EAAE,KAAK,KAAK,OAAO,YAAY,QAAQ,uBAAuB;AAAA,MAChE;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,iBAAiB,OAAO;AAE7C,SAAO;AAAA,IACL,SAAS,OAAO;AAAA,IAChB,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,IACd,cAAc,OAAO;AAAA,EACvB;AACF;AAKO,SAAS,eAAe,SAAsC;AACnE,QAAM,UAAU,gBAAgB;AAChC,QAAM,UAAuB;AAAA,IAC3B,GAAG;AAAA,IACH,GAAG;AAAA,IACH,iBAAiB;AAAA,EACnB;AACA,kBAAgB,OAAO;AAGvB,QAAM,YAAY,cAAc;AAChC,MAAI,CAAC,UAAU,SAAS,QAAQ;AAC9B,cAAU,SAAS,SAAS;AAC5B,kBAAc,SAAS;AAAA,EACzB;AACF;AAKO,SAAS,kBAAwB;AACtC,QAAM,UAAU,gBAAgB;AAChC,UAAQ,kBAAkB;AAC1B,kBAAgB,OAAO;AACzB;AAKO,SAAS,oBAA6B;AAC3C,QAAM,UAAU,gBAAgB;AAChC,SAAO,QAAQ;AACjB;AAMA,eAAsB,cACpB,WAC4B;AAC5B,MAAI,CAAC,kBAAkB,GAAG;AACxB,WAAO;AAAA,EACT;AAGA,mBAAiB,SAAS;AAG1B,QAAM,UAAU,gBAAgB;AAChC,SAAO,cAAc,WAAW,OAAO;AACzC;AAKA,eAAe,qBAAqB,MAAqC;AACvE,QAAM,aAAa;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,QAAM,cAAc,UAAU;AAChC;AAGA,IAAI,iBAAsC;AASnC,SAAS,2BAAuC;AAErD,MAAI,gBAAgB;AAClB,WAAO;AAAA,EACT;AAEA,mBAAiB,oBAAoB;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA;AAAA,EACF;AAEA,SAAO,MAAM;AACX,QAAI,gBAAgB;AAClB,qBAAe;AACf,uBAAiB;AAAA,IACnB;AAAA,EACF;AACF;AAKO,SAAS,mBAA4B;AAC1C,SAAO,mBAAmB;AAC5B;AAMO,SAAS,sBACd,OAOA,QAIA,SAIiB;AACjB,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,YAAY,MAAM,aAAa,OAAO,MAAM;AAGlD,QAAM,gBAA4D,CAAC;AACnE,QAAM,YAAY,oBAAI,IAAY;AAElC,SACG,OAAO,CAAC,MAAM,EAAE,eAAe,WAAW,EAC1C,QAAQ,CAAC,MAAM;AACd,UAAM,OAAO,EAAE,QAAQ,MAAM;AAC7B,QAAI,QAAQ,CAAC,UAAU,IAAI,IAAI,GAAG;AAChC,gBAAU,IAAI,IAAI;AAClB,YAAM,WAAY,EAAE,QAAQ,WAAW,KAAgB;AACvD,UAAI,YAAY;AAChB,UAAI,SAAS,SAAS,OAAO,KAAK,SAAS,SAAS,QAAQ,GAAG;AAC7D,oBAAY;AAAA,MACd,WAAW,SAAS,SAAS,MAAM,GAAG;AACpC,oBAAY;AAAA,MACd,WAAW,SAAS,SAAS,QAAQ,GAAG;AACtC,oBAAY;AAAA,MACd;AACA,oBAAc,KAAK,EAAE,MAAM,UAAU,CAAC;AAAA,IACxC;AAAA,EACF,CAAC;AAGH,QAAM,WAAoD,CAAC;AAC3D,SACG;AAAA,IACC,CAAC,MACC,EAAE,eAAe,iBACjB,OAAO,EAAE,QAAQ,QAAQ,KAAK,EAAE,EAAE,SAAS,MAAM;AAAA,EACrD,EACC,QAAQ,CAAC,MAAM;AACd,UAAM,SAAS,OAAO,EAAE,QAAQ,QAAQ,KAAK,EAAE;AAE/C,UAAM,YAAY,OAAO,MAAM,aAAa;AAC5C,UAAM,YAAY,OAAO,MAAM,aAAa;AAC5C,QAAI,WAAW;AACb,eAAS,KAAK,EAAE,MAAM,SAAS,QAAQ,SAAS,CAAC;AAAA,IACnD;AACA,QAAI,aAAa,SAAS,UAAU,CAAC,CAAC,IAAI,GAAG;AAC3C,eAAS,KAAK,EAAE,MAAM,SAAS,QAAQ,SAAS,CAAC;AAAA,IACnD;AAAA,EACF,CAAC;AAGH,QAAM,YAAY,QACf,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EACnC,IAAI,CAAC,MAAM,EAAE,IAAI;AAEpB,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAGxE,QAAM,SACJ,CAAC;AACH,SACG,OAAO,CAAC,MAAM,EAAE,QAAQ,OAAO,KAAK,EAAE,QAAQ,QAAQ,MAAM,OAAO,EACnE,QAAQ,CAAC,MAAM;AACd,UAAM,WACH,EAAE,QAAQ,OAAO,KACjB,EAAE,QAAQ,SAAS,KACpB;AACF,WAAO,KAAK;AAAA,MACV,MAAO,EAAE,QAAQ,MAAM,KAAgB;AAAA,MACvC,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AAGH,MAAI,SAAwD;AAC5D,MAAI,MAAM,WAAW;AACnB,QAAI,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,SAAS,GAAG;AAChD,eAAS;AAAA,IACX,WACE,SAAS,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,KAC1C,cAAc,WAAW,GACzB;AACA,eAAS;AAAA,IACX,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AAGA,QAAM,gBAAgB,OAAO;AAAA,IAC3B,CAAC,MAAM,EAAE,eAAe;AAAA,EAC1B,EAAE;AAEF,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,iBAAiB;AAAA,IACjB,eAAe,cAAc,OAAO,CAAC,MAAM,EAAE,cAAc,MAAM;AAAA,IACjE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import {
6
6
  FrameManager
7
- } from "./core/context/frame-manager.js";
7
+ } from "./core/context/index.js";
8
8
  import { logger, Logger, LogLevel } from "./core/monitoring/logger.js";
9
9
  import {
10
10
  StackMemoryError,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["/**\n * StackMemory - Lossless memory runtime for AI coding tools\n * Main entry point for the StackMemory package\n */\n\nexport {\n FrameManager,\n type FrameType,\n type FrameState,\n} from './core/context/frame-manager.js';\nexport { logger, Logger, LogLevel } from './core/monitoring/logger.js';\nexport {\n StackMemoryError,\n ErrorCode,\n ErrorHandler,\n} from './core/monitoring/error-handler.js';\nexport { default as LocalStackMemoryMCP } from './integrations/mcp/server.js';\n\n// Re-export key types\nexport interface StackMemoryConfig {\n projectRoot?: string;\n dbPath?: string;\n logLevel?: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';\n}\n\nexport interface ContextItem {\n id: string;\n type: string;\n content: string;\n importance: number;\n timestamp: number;\n}\n"],
4
+ "sourcesContent": ["/**\n * StackMemory - Lossless memory runtime for AI coding tools\n * Main entry point for the StackMemory package\n */\n\nexport {\n FrameManager,\n type FrameType,\n type FrameState,\n} from './core/context/index.js';\nexport { logger, Logger, LogLevel } from './core/monitoring/logger.js';\nexport {\n StackMemoryError,\n ErrorCode,\n ErrorHandler,\n} from './core/monitoring/error-handler.js';\nexport { default as LocalStackMemoryMCP } from './integrations/mcp/server.js';\n\n// Re-export key types\nexport interface StackMemoryConfig {\n projectRoot?: string;\n dbPath?: string;\n logLevel?: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';\n}\n\nexport interface ContextItem {\n id: string;\n type: string;\n content: string;\n importance: number;\n timestamp: number;\n}\n"],
5
5
  "mappings": ";;;;AAKA;AAAA,EACE;AAAA,OAGK;AACP,SAAS,QAAQ,QAAQ,gBAAgB;AACzC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAoB,WAAXA,gBAAsC;",
6
6
  "names": ["default"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/integrations/mcp/handlers/context-handlers.ts"],
4
- "sourcesContent": ["/**\n * Context-related MCP tool handlers\n * Handles frame management and context retrieval\n */\n\nimport { FrameManager, FrameType } from '../../../core/context/frame-manager.js';\nimport { LLMContextRetrieval } from '../../../core/retrieval/index.js';\nimport { logger } from '../../../core/monitoring/logger.js';\n\nexport interface ContextHandlerDependencies {\n frameManager: FrameManager;\n contextRetrieval: LLMContextRetrieval;\n}\n\nexport class ContextHandlers {\n constructor(private deps: ContextHandlerDependencies) {}\n\n /**\n * Get current project context\n */\n async handleGetContext(args: any): Promise<any> {\n try {\n const query = args.query || '';\n const limit = args.limit || 5;\n\n logger.info('Getting context', { query, limit });\n\n // Get hot stack context\n const hotStack = this.deps.frameManager.getHotStackContext(20);\n\n if (hotStack.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active context frames found. Use start_frame to begin working on a task.',\n },\n ],\n };\n }\n\n // Use LLM context retrieval if available\n if (this.deps.contextRetrieval && query) {\n try {\n // TODO: Implement getRelevantContext method\n const llmContext = { summary: 'Context retrieval not yet implemented', frameIds: [] };\n return {\n content: [\n {\n type: 'text',\n text: llmContext.summary || 'No specific context found.',\n },\n ],\n metadata: {\n relevantFrames: llmContext.frameIds,\n query,\n },\n };\n } catch (error: unknown) {\n logger.warn('LLM context retrieval failed, falling back to hot stack', error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n // Format hot stack context\n const contextText = hotStack\n .map((frame, i) => {\n const depth = ' '.repeat(i);\n const constraints = frame.header.constraints?.length\n ? `\\n${depth} Constraints: ${frame.header.constraints.join(', ')}`\n : '';\n const events = frame.recentEvents.length\n ? `\\n${depth} Recent: ${frame.recentEvents.length} events`\n : '';\n\n return `${depth}Frame ${i + 1}: ${frame.header.goal}${constraints}${events}`;\n })\n .join('\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Current Context Stack:\\n${contextText}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error getting context', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Record a decision or important information\n */\n async handleAddDecision(args: any): Promise<any> {\n try {\n const { content, type } = args;\n \n if (!content) {\n throw new Error('Content is required');\n }\n\n const currentFrame = this.deps.frameManager.getCurrentFrameId();\n if (!currentFrame) {\n throw new Error('No active frame. Use start_frame first.');\n }\n\n // Add as anchor\n this.deps.frameManager.addAnchor(\n type === 'constraint' ? 'CONSTRAINT' : 'DECISION',\n content,\n type === 'constraint' ? 9 : 7\n );\n\n // Also add as event\n this.deps.frameManager.addEvent('decision', {\n type,\n content,\n timestamp: Date.now(),\n });\n\n logger.info('Added decision/constraint', { type, content });\n\n return {\n content: [\n {\n type: 'text',\n text: `Recorded ${type}: ${content}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error adding decision', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Start a new frame (task/subtask) on the call stack\n */\n async handleStartFrame(args: any): Promise<any> {\n try {\n const { name, type = 'task', constraints = [], definitions = {} } = args;\n \n if (!name) {\n throw new Error('Frame name is required');\n }\n\n const frameId = this.deps.frameManager.createFrame({\n type: type as FrameType,\n name,\n inputs: { constraints, definitions }\n });\n\n logger.info('Started frame', { frameId, name, type });\n\n return {\n content: [\n {\n type: 'text',\n text: `Started frame: ${name} (${frameId})`,\n },\n ],\n metadata: {\n frameId,\n type,\n name,\n },\n };\n } catch (error: unknown) {\n logger.error('Error starting frame', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Close current frame with summary\n */\n async handleCloseFrame(args: any): Promise<any> {\n try {\n const { summary, frameId } = args;\n const targetFrameId = frameId || this.deps.frameManager.getCurrentFrameId();\n\n if (!targetFrameId) {\n throw new Error('No active frame to close');\n }\n\n const frame = this.deps.frameManager.getFrame(targetFrameId);\n if (!frame) {\n throw new Error(`Frame not found: ${targetFrameId}`);\n }\n\n // Add summary if provided\n if (summary) {\n this.deps.frameManager.addEvent('observation', {\n type: 'completion_summary',\n content: summary,\n timestamp: Date.now(),\n });\n }\n\n this.deps.frameManager.closeFrame(targetFrameId, summary ? { summary } : {});\n\n logger.info('Closed frame', { frameId: targetFrameId, frameName: frame.name });\n\n return {\n content: [\n {\n type: 'text',\n text: `Closed frame: ${frame.name}${summary ? ` with summary: ${summary}` : ''}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error closing frame', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Add an anchor (important fact) to current frame\n */\n async handleAddAnchor(args: any): Promise<any> {\n try {\n const { type, text, priority = 5 } = args;\n \n if (!text) {\n throw new Error('Anchor text is required');\n }\n\n const currentFrame = this.deps.frameManager.getCurrentFrameId();\n if (!currentFrame) {\n throw new Error('No active frame. Use start_frame first.');\n }\n\n const validTypes = ['FACT', 'DECISION', 'CONSTRAINT', 'INTERFACE_CONTRACT', 'TODO', 'RISK'];\n if (!validTypes.includes(type)) {\n throw new Error(`Invalid anchor type. Must be one of: ${validTypes.join(', ')}`);\n }\n\n this.deps.frameManager.addAnchor(type, text, priority);\n\n logger.info('Added anchor', { type, text, priority });\n\n return {\n content: [\n {\n type: 'text',\n text: `Added ${type.toLowerCase()}: ${text}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error adding anchor', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Get current hot stack context\n */\n async handleGetHotStack(args: any): Promise<any> {\n try {\n const maxEvents = args.max_events || 10;\n const hotStack = this.deps.frameManager.getHotStackContext(maxEvents);\n\n if (hotStack.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active frames on the stack.',\n },\n ],\n };\n }\n\n const stackSummary = hotStack.map((frame, index) => ({\n depth: index,\n frameId: frame.frameId,\n goal: frame.header.goal,\n constraints: frame.header.constraints || [],\n anchors: frame.anchors.length,\n recentEvents: frame.recentEvents.length,\n artifacts: frame.activeArtifacts.length,\n }));\n\n return {\n content: [\n {\n type: 'text',\n text: `Hot Stack (${hotStack.length} frames):\\n` +\n stackSummary.map((f: any) => \n ` ${f.depth}: ${f.goal} (${f.anchors} anchors, ${f.recentEvents} events)`\n ).join('\\n'),\n },\n ],\n metadata: {\n stack: stackSummary,\n },\n };\n } catch (error: unknown) {\n logger.error('Error getting hot stack', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n}"],
4
+ "sourcesContent": ["/**\n * Context-related MCP tool handlers\n * Handles frame management and context retrieval\n */\n\nimport { FrameManager, FrameType } from '../../../core/context/index.js';\nimport { LLMContextRetrieval } from '../../../core/retrieval/index.js';\nimport { logger } from '../../../core/monitoring/logger.js';\n\nexport interface ContextHandlerDependencies {\n frameManager: FrameManager;\n contextRetrieval: LLMContextRetrieval;\n}\n\nexport class ContextHandlers {\n constructor(private deps: ContextHandlerDependencies) {}\n\n /**\n * Get current project context\n */\n async handleGetContext(args: any): Promise<any> {\n try {\n const query = args.query || '';\n const limit = args.limit || 5;\n\n logger.info('Getting context', { query, limit });\n\n // Get hot stack context\n const hotStack = this.deps.frameManager.getHotStackContext(20);\n\n if (hotStack.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active context frames found. Use start_frame to begin working on a task.',\n },\n ],\n };\n }\n\n // Use LLM context retrieval if available\n if (this.deps.contextRetrieval && query) {\n try {\n // TODO: Implement getRelevantContext method\n const llmContext = { summary: 'Context retrieval not yet implemented', frameIds: [] };\n return {\n content: [\n {\n type: 'text',\n text: llmContext.summary || 'No specific context found.',\n },\n ],\n metadata: {\n relevantFrames: llmContext.frameIds,\n query,\n },\n };\n } catch (error: unknown) {\n logger.warn('LLM context retrieval failed, falling back to hot stack', error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n // Format hot stack context\n const contextText = hotStack\n .map((frame, i) => {\n const depth = ' '.repeat(i);\n const constraints = frame.header.constraints?.length\n ? `\\n${depth} Constraints: ${frame.header.constraints.join(', ')}`\n : '';\n const events = frame.recentEvents.length\n ? `\\n${depth} Recent: ${frame.recentEvents.length} events`\n : '';\n\n return `${depth}Frame ${i + 1}: ${frame.header.goal}${constraints}${events}`;\n })\n .join('\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `Current Context Stack:\\n${contextText}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error getting context', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Record a decision or important information\n */\n async handleAddDecision(args: any): Promise<any> {\n try {\n const { content, type } = args;\n \n if (!content) {\n throw new Error('Content is required');\n }\n\n const currentFrame = this.deps.frameManager.getCurrentFrameId();\n if (!currentFrame) {\n throw new Error('No active frame. Use start_frame first.');\n }\n\n // Add as anchor\n this.deps.frameManager.addAnchor(\n type === 'constraint' ? 'CONSTRAINT' : 'DECISION',\n content,\n type === 'constraint' ? 9 : 7\n );\n\n // Also add as event\n this.deps.frameManager.addEvent('decision', {\n type,\n content,\n timestamp: Date.now(),\n });\n\n logger.info('Added decision/constraint', { type, content });\n\n return {\n content: [\n {\n type: 'text',\n text: `Recorded ${type}: ${content}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error adding decision', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Start a new frame (task/subtask) on the call stack\n */\n async handleStartFrame(args: any): Promise<any> {\n try {\n const { name, type = 'task', constraints = [], definitions = {} } = args;\n \n if (!name) {\n throw new Error('Frame name is required');\n }\n\n const frameId = this.deps.frameManager.createFrame({\n type: type as FrameType,\n name,\n inputs: { constraints, definitions }\n });\n\n logger.info('Started frame', { frameId, name, type });\n\n return {\n content: [\n {\n type: 'text',\n text: `Started frame: ${name} (${frameId})`,\n },\n ],\n metadata: {\n frameId,\n type,\n name,\n },\n };\n } catch (error: unknown) {\n logger.error('Error starting frame', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Close current frame with summary\n */\n async handleCloseFrame(args: any): Promise<any> {\n try {\n const { summary, frameId } = args;\n const targetFrameId = frameId || this.deps.frameManager.getCurrentFrameId();\n\n if (!targetFrameId) {\n throw new Error('No active frame to close');\n }\n\n const frame = this.deps.frameManager.getFrame(targetFrameId);\n if (!frame) {\n throw new Error(`Frame not found: ${targetFrameId}`);\n }\n\n // Add summary if provided\n if (summary) {\n this.deps.frameManager.addEvent('observation', {\n type: 'completion_summary',\n content: summary,\n timestamp: Date.now(),\n });\n }\n\n this.deps.frameManager.closeFrame(targetFrameId, summary ? { summary } : {});\n\n logger.info('Closed frame', { frameId: targetFrameId, frameName: frame.name });\n\n return {\n content: [\n {\n type: 'text',\n text: `Closed frame: ${frame.name}${summary ? ` with summary: ${summary}` : ''}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error closing frame', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Add an anchor (important fact) to current frame\n */\n async handleAddAnchor(args: any): Promise<any> {\n try {\n const { type, text, priority = 5 } = args;\n \n if (!text) {\n throw new Error('Anchor text is required');\n }\n\n const currentFrame = this.deps.frameManager.getCurrentFrameId();\n if (!currentFrame) {\n throw new Error('No active frame. Use start_frame first.');\n }\n\n const validTypes = ['FACT', 'DECISION', 'CONSTRAINT', 'INTERFACE_CONTRACT', 'TODO', 'RISK'];\n if (!validTypes.includes(type)) {\n throw new Error(`Invalid anchor type. Must be one of: ${validTypes.join(', ')}`);\n }\n\n this.deps.frameManager.addAnchor(type, text, priority);\n\n logger.info('Added anchor', { type, text, priority });\n\n return {\n content: [\n {\n type: 'text',\n text: `Added ${type.toLowerCase()}: ${text}`,\n },\n ],\n };\n } catch (error: unknown) {\n logger.error('Error adding anchor', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n\n /**\n * Get current hot stack context\n */\n async handleGetHotStack(args: any): Promise<any> {\n try {\n const maxEvents = args.max_events || 10;\n const hotStack = this.deps.frameManager.getHotStackContext(maxEvents);\n\n if (hotStack.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active frames on the stack.',\n },\n ],\n };\n }\n\n const stackSummary = hotStack.map((frame, index) => ({\n depth: index,\n frameId: frame.frameId,\n goal: frame.header.goal,\n constraints: frame.header.constraints || [],\n anchors: frame.anchors.length,\n recentEvents: frame.recentEvents.length,\n artifacts: frame.activeArtifacts.length,\n }));\n\n return {\n content: [\n {\n type: 'text',\n text: `Hot Stack (${hotStack.length} frames):\\n` +\n stackSummary.map((f: any) => \n ` ${f.depth}: ${f.goal} (${f.anchors} anchors, ${f.recentEvents} events)`\n ).join('\\n'),\n },\n ],\n metadata: {\n stack: stackSummary,\n },\n };\n } catch (error: unknown) {\n logger.error('Error getting hot stack', error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n }\n}"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/integrations/mcp/handlers/discovery-handlers.ts"],
4
- "sourcesContent": ["/**\n * Discovery MCP Tool Handlers\n * Intelligently discovers relevant files based on current context\n */\n\nimport { FrameManager } from '../../../core/context/frame-manager.js';\nimport { LLMContextRetrieval } from '../../../core/retrieval/index.js';\nimport { logger } from '../../../core/monitoring/logger.js';\nimport { execSync } from 'child_process';\nimport { existsSync, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, relative, extname } from 'path';\nimport Database from 'better-sqlite3';\n\nexport interface DiscoveryDependencies {\n frameManager: FrameManager;\n contextRetrieval: LLMContextRetrieval;\n db: Database.Database;\n projectRoot: string;\n}\n\ninterface DiscoveredFile {\n path: string;\n relevance: 'high' | 'medium' | 'low';\n reason: string;\n matchedKeywords?: string[];\n excerpt?: string;\n}\n\ninterface DiscoveryResult {\n files: DiscoveredFile[];\n keywords: string[];\n contextSummary: string;\n mdContext: Record<string, string>;\n}\n\nexport class DiscoveryHandlers {\n constructor(private deps: DiscoveryDependencies) {}\n\n /**\n * Discover relevant files based on current context\n */\n async handleDiscover(args: {\n query?: string;\n depth?: 'shallow' | 'medium' | 'deep';\n includePatterns?: string[];\n excludePatterns?: string[];\n maxFiles?: number;\n }): Promise<any> {\n try {\n const {\n query,\n depth = 'medium',\n includePatterns = ['*.ts', '*.tsx', '*.js', '*.md', '*.json'],\n excludePatterns = ['node_modules', 'dist', '.git', '*.min.js'],\n maxFiles = 20,\n } = args;\n\n logger.info('Starting discovery', { query, depth });\n\n // Step 1: Extract keywords from current context\n const keywords = this.extractContextKeywords(query);\n\n // Step 2: Parse .md files for additional context\n const mdContext = this.parseMdFiles();\n\n // Step 3: Get recently touched files from frames\n const recentFiles = this.getRecentFilesFromContext();\n\n // Step 4: Search codebase for relevant files\n const discoveredFiles = await this.searchCodebase(\n keywords,\n includePatterns,\n excludePatterns,\n depth,\n maxFiles\n );\n\n // Step 5: Merge and rank results\n const rankedFiles = this.rankFiles(\n discoveredFiles,\n recentFiles,\n keywords\n );\n\n // Step 6: Generate context summary\n const contextSummary = this.generateContextSummary(keywords, rankedFiles);\n\n const result: DiscoveryResult = {\n files: rankedFiles.slice(0, maxFiles),\n keywords,\n contextSummary,\n mdContext,\n };\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatDiscoveryResult(result),\n },\n ],\n metadata: result,\n };\n } catch (error) {\n logger.error('Discovery failed', error);\n throw error;\n }\n }\n\n /**\n * Get related files to a specific file or concept\n */\n async handleRelatedFiles(args: {\n file?: string;\n concept?: string;\n maxFiles?: number;\n }): Promise<any> {\n try {\n const { file, concept, maxFiles = 10 } = args;\n\n if (!file && !concept) {\n throw new Error('Either file or concept is required');\n }\n\n let relatedFiles: DiscoveredFile[] = [];\n\n if (file) {\n // Find files that import/reference this file\n relatedFiles = this.findFileReferences(file, maxFiles);\n }\n\n if (concept) {\n // Search for files mentioning this concept\n const conceptFiles = this.searchForConcept(concept, maxFiles);\n relatedFiles = this.mergeAndDedupe(relatedFiles, conceptFiles);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatRelatedFiles(relatedFiles, file, concept),\n },\n ],\n metadata: { relatedFiles },\n };\n } catch (error) {\n logger.error('Related files search failed', error);\n throw error;\n }\n }\n\n /**\n * Get session summary with actionable context\n */\n async handleSessionSummary(args: {\n includeFiles?: boolean;\n includeDecisions?: boolean;\n }): Promise<any> {\n try {\n const { includeFiles = true, includeDecisions = true } = args;\n\n const hotStack = this.deps.frameManager.getHotStackContext(50);\n const recentFiles = includeFiles ? this.getRecentFilesFromContext() : [];\n const decisions = includeDecisions ? this.getRecentDecisions() : [];\n\n const summary = {\n activeFrames: hotStack.length,\n currentGoal:\n hotStack[hotStack.length - 1]?.header?.goal || 'No active task',\n recentFiles: recentFiles.slice(0, 10),\n decisions: decisions.slice(0, 5),\n stackDepth: this.deps.frameManager.getStackDepth(),\n };\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatSessionSummary(summary),\n },\n ],\n metadata: summary,\n };\n } catch (error) {\n logger.error('Session summary failed', error);\n throw error;\n }\n }\n\n // ===============================\n // Private helper methods\n // ===============================\n\n private extractContextKeywords(query?: string): string[] {\n const keywords: Set<string> = new Set();\n\n // Add query terms\n if (query) {\n const queryWords = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n queryWords.forEach((w) => keywords.add(w));\n }\n\n // Extract from current frames\n const hotStack = this.deps.frameManager.getHotStackContext(20);\n for (const frame of hotStack) {\n // Frame name/goal\n if (frame.header?.goal) {\n const goalWords = frame.header.goal\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 2);\n goalWords.forEach((w) => keywords.add(w));\n }\n\n // Constraints\n frame.header?.constraints?.forEach((c: string) => {\n const words = c\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 2);\n words.forEach((w) => keywords.add(w));\n });\n\n // Recent events\n frame.recentEvents?.forEach((evt: any) => {\n if (evt.data?.content) {\n const words = String(evt.data.content)\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 3)\n .slice(0, 5);\n words.forEach((w) => keywords.add(w));\n }\n });\n }\n\n // Extract from recent files in events\n try {\n const fileEvents = this.deps.db\n .prepare(\n `\n SELECT DISTINCT data FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE e.type IN ('file_read', 'file_write', 'file_edit')\n ORDER BY e.timestamp DESC\n LIMIT 20\n `\n )\n .all() as any[];\n\n for (const evt of fileEvents) {\n try {\n const data = JSON.parse(evt.data || '{}');\n if (data.path) {\n // Extract meaningful parts from file path\n const pathParts = data.path.split('/').slice(-2);\n pathParts.forEach((part: string) => {\n const words = part\n .replace(/\\.[^.]+$/, '')\n .split(/[\\-_]+/)\n .filter((w) => w.length > 2);\n words.forEach((w) => keywords.add(w.toLowerCase()));\n });\n }\n } catch {}\n }\n } catch {}\n\n // Remove common stopwords\n const stopwords = new Set([\n 'the',\n 'and',\n 'for',\n 'with',\n 'this',\n 'that',\n 'from',\n 'have',\n 'has',\n 'been',\n 'will',\n 'can',\n 'should',\n 'would',\n 'could',\n 'function',\n 'const',\n 'let',\n 'var',\n 'import',\n 'export',\n 'return',\n 'async',\n 'await',\n ]);\n\n return Array.from(keywords).filter((k) => !stopwords.has(k));\n }\n\n private parseMdFiles(): Record<string, string> {\n const mdContext: Record<string, string> = {};\n const mdFiles = ['CLAUDE.md', 'README.md', '.stackmemory/context.md'];\n\n for (const mdFile of mdFiles) {\n const fullPath = join(this.deps.projectRoot, mdFile);\n if (existsSync(fullPath)) {\n try {\n const content = readFileSync(fullPath, 'utf8');\n // Extract key sections\n const sections = this.extractMdSections(content);\n mdContext[mdFile] = sections;\n } catch {}\n }\n }\n\n // Also check ~/.claude/CLAUDE.md\n const homeClaude = join(process.env['HOME'] || '', '.claude', 'CLAUDE.md');\n if (existsSync(homeClaude)) {\n try {\n const content = readFileSync(homeClaude, 'utf8');\n mdContext['~/.claude/CLAUDE.md'] = this.extractMdSections(content);\n } catch {}\n }\n\n return mdContext;\n }\n\n private extractMdSections(content: string): string {\n // Extract meaningful sections (headers and their content)\n const lines = content.split('\\n');\n const sections: string[] = [];\n let currentSection = '';\n let inCodeBlock = false;\n\n for (const line of lines) {\n if (line.startsWith('```')) {\n inCodeBlock = !inCodeBlock;\n continue;\n }\n if (inCodeBlock) continue;\n\n if (line.startsWith('#')) {\n if (currentSection) sections.push(currentSection.trim());\n currentSection = line + '\\n';\n } else if (currentSection && line.trim()) {\n currentSection += line + '\\n';\n }\n }\n if (currentSection) sections.push(currentSection.trim());\n\n // Return condensed version (first 500 chars of each section)\n return sections\n .map((s) => (s.length > 500 ? s.slice(0, 500) + '...' : s))\n .join('\\n\\n');\n }\n\n private getRecentFilesFromContext(): string[] {\n const files: Set<string> = new Set();\n\n try {\n const fileEvents = this.deps.db\n .prepare(\n `\n SELECT DISTINCT data FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE e.type IN ('file_read', 'file_write', 'file_edit', 'tool_call')\n AND e.timestamp > ?\n ORDER BY e.timestamp DESC\n LIMIT 50\n `\n )\n .all(Math.floor(Date.now() / 1000) - 3600) as any[]; // Last hour\n\n for (const evt of fileEvents) {\n try {\n const data = JSON.parse(evt.data || '{}');\n if (data.path) files.add(data.path);\n if (data.file) files.add(data.file);\n if (data.file_path) files.add(data.file_path);\n } catch {}\n }\n } catch {}\n\n // Also get from git status\n try {\n const gitStatus = execSync('git status --porcelain', {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n });\n const modifiedFiles = gitStatus\n .split('\\n')\n .filter((l) => l.trim())\n .map((l) => l.slice(3).trim())\n .filter((f) => f);\n modifiedFiles.forEach((f) => files.add(f));\n } catch {}\n\n return Array.from(files);\n }\n\n private async searchCodebase(\n keywords: string[],\n includePatterns: string[],\n excludePatterns: string[],\n depth: 'shallow' | 'medium' | 'deep',\n maxFiles: number\n ): Promise<DiscoveredFile[]> {\n const files: DiscoveredFile[] = [];\n const maxResults = depth === 'shallow' ? 10 : depth === 'medium' ? 25 : 50;\n\n // Build grep command for each keyword\n for (const keyword of keywords.slice(0, 10)) {\n try {\n const excludeArgs = excludePatterns\n .map((p) => `--exclude-dir=${p}`)\n .join(' ');\n const includeArgs = includePatterns\n .map((p) => `--include=${p}`)\n .join(' ');\n\n const cmd = `grep -ril ${excludeArgs} ${includeArgs} \"${keyword}\" . 2>/dev/null | head -${maxResults}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const matchedFiles = result.split('\\n').filter((f) => f.trim());\n for (const file of matchedFiles) {\n const cleanPath = file.replace(/^\\.\\//, '');\n const existing = files.find((f) => f.path === cleanPath);\n if (existing) {\n existing.matchedKeywords = existing.matchedKeywords || [];\n if (!existing.matchedKeywords.includes(keyword)) {\n existing.matchedKeywords.push(keyword);\n }\n } else {\n files.push({\n path: cleanPath,\n relevance: 'medium',\n reason: `Contains keyword: ${keyword}`,\n matchedKeywords: [keyword],\n });\n }\n }\n } catch {\n // Grep failed for this keyword, continue\n }\n }\n\n // Boost relevance for files with multiple keyword matches\n for (const file of files) {\n const matchCount = file.matchedKeywords?.length || 0;\n if (matchCount >= 3) {\n file.relevance = 'high';\n file.reason = `Matches ${matchCount} keywords: ${file.matchedKeywords?.slice(0, 3).join(', ')}`;\n }\n }\n\n return files;\n }\n\n private rankFiles(\n discovered: DiscoveredFile[],\n recent: string[],\n keywords: string[]\n ): DiscoveredFile[] {\n const recentSet = new Set(recent);\n\n // Add recent files with high relevance\n for (const recentFile of recent) {\n const existing = discovered.find((f) => f.path === recentFile);\n if (existing) {\n existing.relevance = 'high';\n existing.reason = 'Recently accessed + ' + existing.reason;\n } else {\n discovered.push({\n path: recentFile,\n relevance: 'high',\n reason: 'Recently accessed in context',\n });\n }\n }\n\n // Sort by relevance and match count\n return discovered.sort((a, b) => {\n const relevanceOrder = { high: 3, medium: 2, low: 1 };\n const relDiff = relevanceOrder[b.relevance] - relevanceOrder[a.relevance];\n if (relDiff !== 0) return relDiff;\n\n const aMatches = a.matchedKeywords?.length || 0;\n const bMatches = b.matchedKeywords?.length || 0;\n return bMatches - aMatches;\n });\n }\n\n private findFileReferences(file: string, maxFiles: number): DiscoveredFile[] {\n const results: DiscoveredFile[] = [];\n\n try {\n // Search for imports of this file\n const basename = file.replace(/\\.[^.]+$/, '');\n const cmd = `grep -ril \"from.*${basename}\" . --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.js\" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const files = result.split('\\n').filter((f) => f.trim());\n for (const f of files) {\n results.push({\n path: f.replace(/^\\.\\//, ''),\n relevance: 'high',\n reason: `Imports ${file}`,\n });\n }\n } catch {}\n\n return results;\n }\n\n private searchForConcept(\n concept: string,\n maxFiles: number\n ): DiscoveredFile[] {\n const results: DiscoveredFile[] = [];\n\n try {\n const cmd = `grep -ril \"${concept}\" . --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.md\" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const files = result.split('\\n').filter((f) => f.trim());\n for (const f of files) {\n results.push({\n path: f.replace(/^\\.\\//, ''),\n relevance: 'medium',\n reason: `Contains \"${concept}\"`,\n });\n }\n } catch {}\n\n return results;\n }\n\n private mergeAndDedupe(\n a: DiscoveredFile[],\n b: DiscoveredFile[]\n ): DiscoveredFile[] {\n const pathSet = new Set(a.map((f) => f.path));\n const merged = [...a];\n\n for (const file of b) {\n if (!pathSet.has(file.path)) {\n merged.push(file);\n pathSet.add(file.path);\n }\n }\n\n return merged;\n }\n\n private getRecentDecisions(): any[] {\n try {\n const decisions = this.deps.db\n .prepare(\n `\n SELECT a.text, a.type, a.priority, f.name as frame_name, a.created_at\n FROM anchors a\n JOIN frames f ON a.frame_id = f.frame_id\n WHERE a.type IN ('DECISION', 'CONSTRAINT', 'FACT')\n ORDER BY a.created_at DESC\n LIMIT 10\n `\n )\n .all();\n return decisions;\n } catch {\n return [];\n }\n }\n\n private generateContextSummary(\n keywords: string[],\n files: DiscoveredFile[]\n ): string {\n const hotStack = this.deps.frameManager.getHotStackContext(5);\n const currentGoal = hotStack[hotStack.length - 1]?.header?.goal;\n\n let summary = '';\n if (currentGoal) {\n summary += `Current task: ${currentGoal}\\n`;\n }\n summary += `Context keywords: ${keywords.slice(0, 10).join(', ')}\\n`;\n summary += `Relevant files found: ${files.length}\\n`;\n summary += `High relevance: ${files.filter((f) => f.relevance === 'high').length}`;\n\n return summary;\n }\n\n private formatDiscoveryResult(result: DiscoveryResult): string {\n let output = '# Discovery Results\\n\\n';\n\n output += '## Context Summary\\n';\n output += result.contextSummary + '\\n\\n';\n\n output += '## Relevant Files\\n\\n';\n for (const file of result.files.slice(0, 15)) {\n const icon =\n file.relevance === 'high'\n ? '[HIGH]'\n : file.relevance === 'medium'\n ? '[MED]'\n : '[LOW]';\n output += `${icon} ${file.path}\\n`;\n output += ` ${file.reason}\\n`;\n }\n\n if (result.keywords.length > 0) {\n output += '\\n## Keywords Used\\n';\n output += result.keywords.slice(0, 15).join(', ') + '\\n';\n }\n\n return output;\n }\n\n private formatRelatedFiles(\n files: DiscoveredFile[],\n file?: string,\n concept?: string\n ): string {\n let output = '# Related Files\\n\\n';\n\n if (file) output += `Related to file: ${file}\\n`;\n if (concept) output += `Related to concept: ${concept}\\n`;\n output += '\\n';\n\n for (const f of files) {\n output += `- ${f.path}\\n ${f.reason}\\n`;\n }\n\n return output;\n }\n\n private formatSessionSummary(summary: any): string {\n let output = '# Session Summary\\n\\n';\n\n output += `**Current Goal:** ${summary.currentGoal}\\n`;\n output += `**Active Frames:** ${summary.activeFrames}\\n`;\n output += `**Stack Depth:** ${summary.stackDepth}\\n\\n`;\n\n if (summary.recentFiles.length > 0) {\n output += '## Recent Files\\n';\n for (const f of summary.recentFiles) {\n output += `- ${f}\\n`;\n }\n output += '\\n';\n }\n\n if (summary.decisions.length > 0) {\n output += '## Recent Decisions\\n';\n for (const d of summary.decisions) {\n output += `- [${d.type}] ${d.text}\\n`;\n }\n }\n\n return output;\n }\n}\n"],
4
+ "sourcesContent": ["/**\n * Discovery MCP Tool Handlers\n * Intelligently discovers relevant files based on current context\n */\n\nimport { FrameManager } from '../../../core/context/index.js';\nimport { LLMContextRetrieval } from '../../../core/retrieval/index.js';\nimport { logger } from '../../../core/monitoring/logger.js';\nimport { execSync } from 'child_process';\nimport { existsSync, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, relative, extname } from 'path';\nimport Database from 'better-sqlite3';\n\nexport interface DiscoveryDependencies {\n frameManager: FrameManager;\n contextRetrieval: LLMContextRetrieval;\n db: Database.Database;\n projectRoot: string;\n}\n\ninterface DiscoveredFile {\n path: string;\n relevance: 'high' | 'medium' | 'low';\n reason: string;\n matchedKeywords?: string[];\n excerpt?: string;\n}\n\ninterface DiscoveryResult {\n files: DiscoveredFile[];\n keywords: string[];\n contextSummary: string;\n mdContext: Record<string, string>;\n}\n\nexport class DiscoveryHandlers {\n constructor(private deps: DiscoveryDependencies) {}\n\n /**\n * Discover relevant files based on current context\n */\n async handleDiscover(args: {\n query?: string;\n depth?: 'shallow' | 'medium' | 'deep';\n includePatterns?: string[];\n excludePatterns?: string[];\n maxFiles?: number;\n }): Promise<any> {\n try {\n const {\n query,\n depth = 'medium',\n includePatterns = ['*.ts', '*.tsx', '*.js', '*.md', '*.json'],\n excludePatterns = ['node_modules', 'dist', '.git', '*.min.js'],\n maxFiles = 20,\n } = args;\n\n logger.info('Starting discovery', { query, depth });\n\n // Step 1: Extract keywords from current context\n const keywords = this.extractContextKeywords(query);\n\n // Step 2: Parse .md files for additional context\n const mdContext = this.parseMdFiles();\n\n // Step 3: Get recently touched files from frames\n const recentFiles = this.getRecentFilesFromContext();\n\n // Step 4: Search codebase for relevant files\n const discoveredFiles = await this.searchCodebase(\n keywords,\n includePatterns,\n excludePatterns,\n depth,\n maxFiles\n );\n\n // Step 5: Merge and rank results\n const rankedFiles = this.rankFiles(\n discoveredFiles,\n recentFiles,\n keywords\n );\n\n // Step 6: Generate context summary\n const contextSummary = this.generateContextSummary(keywords, rankedFiles);\n\n const result: DiscoveryResult = {\n files: rankedFiles.slice(0, maxFiles),\n keywords,\n contextSummary,\n mdContext,\n };\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatDiscoveryResult(result),\n },\n ],\n metadata: result,\n };\n } catch (error) {\n logger.error('Discovery failed', error);\n throw error;\n }\n }\n\n /**\n * Get related files to a specific file or concept\n */\n async handleRelatedFiles(args: {\n file?: string;\n concept?: string;\n maxFiles?: number;\n }): Promise<any> {\n try {\n const { file, concept, maxFiles = 10 } = args;\n\n if (!file && !concept) {\n throw new Error('Either file or concept is required');\n }\n\n let relatedFiles: DiscoveredFile[] = [];\n\n if (file) {\n // Find files that import/reference this file\n relatedFiles = this.findFileReferences(file, maxFiles);\n }\n\n if (concept) {\n // Search for files mentioning this concept\n const conceptFiles = this.searchForConcept(concept, maxFiles);\n relatedFiles = this.mergeAndDedupe(relatedFiles, conceptFiles);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatRelatedFiles(relatedFiles, file, concept),\n },\n ],\n metadata: { relatedFiles },\n };\n } catch (error) {\n logger.error('Related files search failed', error);\n throw error;\n }\n }\n\n /**\n * Get session summary with actionable context\n */\n async handleSessionSummary(args: {\n includeFiles?: boolean;\n includeDecisions?: boolean;\n }): Promise<any> {\n try {\n const { includeFiles = true, includeDecisions = true } = args;\n\n const hotStack = this.deps.frameManager.getHotStackContext(50);\n const recentFiles = includeFiles ? this.getRecentFilesFromContext() : [];\n const decisions = includeDecisions ? this.getRecentDecisions() : [];\n\n const summary = {\n activeFrames: hotStack.length,\n currentGoal:\n hotStack[hotStack.length - 1]?.header?.goal || 'No active task',\n recentFiles: recentFiles.slice(0, 10),\n decisions: decisions.slice(0, 5),\n stackDepth: this.deps.frameManager.getStackDepth(),\n };\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatSessionSummary(summary),\n },\n ],\n metadata: summary,\n };\n } catch (error) {\n logger.error('Session summary failed', error);\n throw error;\n }\n }\n\n // ===============================\n // Private helper methods\n // ===============================\n\n private extractContextKeywords(query?: string): string[] {\n const keywords: Set<string> = new Set();\n\n // Add query terms\n if (query) {\n const queryWords = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n queryWords.forEach((w) => keywords.add(w));\n }\n\n // Extract from current frames\n const hotStack = this.deps.frameManager.getHotStackContext(20);\n for (const frame of hotStack) {\n // Frame name/goal\n if (frame.header?.goal) {\n const goalWords = frame.header.goal\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 2);\n goalWords.forEach((w) => keywords.add(w));\n }\n\n // Constraints\n frame.header?.constraints?.forEach((c: string) => {\n const words = c\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 2);\n words.forEach((w) => keywords.add(w));\n });\n\n // Recent events\n frame.recentEvents?.forEach((evt: any) => {\n if (evt.data?.content) {\n const words = String(evt.data.content)\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 3)\n .slice(0, 5);\n words.forEach((w) => keywords.add(w));\n }\n });\n }\n\n // Extract from recent files in events\n try {\n const fileEvents = this.deps.db\n .prepare(\n `\n SELECT DISTINCT data FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE e.type IN ('file_read', 'file_write', 'file_edit')\n ORDER BY e.timestamp DESC\n LIMIT 20\n `\n )\n .all() as any[];\n\n for (const evt of fileEvents) {\n try {\n const data = JSON.parse(evt.data || '{}');\n if (data.path) {\n // Extract meaningful parts from file path\n const pathParts = data.path.split('/').slice(-2);\n pathParts.forEach((part: string) => {\n const words = part\n .replace(/\\.[^.]+$/, '')\n .split(/[\\-_]+/)\n .filter((w) => w.length > 2);\n words.forEach((w) => keywords.add(w.toLowerCase()));\n });\n }\n } catch {}\n }\n } catch {}\n\n // Remove common stopwords\n const stopwords = new Set([\n 'the',\n 'and',\n 'for',\n 'with',\n 'this',\n 'that',\n 'from',\n 'have',\n 'has',\n 'been',\n 'will',\n 'can',\n 'should',\n 'would',\n 'could',\n 'function',\n 'const',\n 'let',\n 'var',\n 'import',\n 'export',\n 'return',\n 'async',\n 'await',\n ]);\n\n return Array.from(keywords).filter((k) => !stopwords.has(k));\n }\n\n private parseMdFiles(): Record<string, string> {\n const mdContext: Record<string, string> = {};\n const mdFiles = ['CLAUDE.md', 'README.md', '.stackmemory/context.md'];\n\n for (const mdFile of mdFiles) {\n const fullPath = join(this.deps.projectRoot, mdFile);\n if (existsSync(fullPath)) {\n try {\n const content = readFileSync(fullPath, 'utf8');\n // Extract key sections\n const sections = this.extractMdSections(content);\n mdContext[mdFile] = sections;\n } catch {}\n }\n }\n\n // Also check ~/.claude/CLAUDE.md\n const homeClaude = join(process.env['HOME'] || '', '.claude', 'CLAUDE.md');\n if (existsSync(homeClaude)) {\n try {\n const content = readFileSync(homeClaude, 'utf8');\n mdContext['~/.claude/CLAUDE.md'] = this.extractMdSections(content);\n } catch {}\n }\n\n return mdContext;\n }\n\n private extractMdSections(content: string): string {\n // Extract meaningful sections (headers and their content)\n const lines = content.split('\\n');\n const sections: string[] = [];\n let currentSection = '';\n let inCodeBlock = false;\n\n for (const line of lines) {\n if (line.startsWith('```')) {\n inCodeBlock = !inCodeBlock;\n continue;\n }\n if (inCodeBlock) continue;\n\n if (line.startsWith('#')) {\n if (currentSection) sections.push(currentSection.trim());\n currentSection = line + '\\n';\n } else if (currentSection && line.trim()) {\n currentSection += line + '\\n';\n }\n }\n if (currentSection) sections.push(currentSection.trim());\n\n // Return condensed version (first 500 chars of each section)\n return sections\n .map((s) => (s.length > 500 ? s.slice(0, 500) + '...' : s))\n .join('\\n\\n');\n }\n\n private getRecentFilesFromContext(): string[] {\n const files: Set<string> = new Set();\n\n try {\n const fileEvents = this.deps.db\n .prepare(\n `\n SELECT DISTINCT data FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE e.type IN ('file_read', 'file_write', 'file_edit', 'tool_call')\n AND e.timestamp > ?\n ORDER BY e.timestamp DESC\n LIMIT 50\n `\n )\n .all(Math.floor(Date.now() / 1000) - 3600) as any[]; // Last hour\n\n for (const evt of fileEvents) {\n try {\n const data = JSON.parse(evt.data || '{}');\n if (data.path) files.add(data.path);\n if (data.file) files.add(data.file);\n if (data.file_path) files.add(data.file_path);\n } catch {}\n }\n } catch {}\n\n // Also get from git status\n try {\n const gitStatus = execSync('git status --porcelain', {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n });\n const modifiedFiles = gitStatus\n .split('\\n')\n .filter((l) => l.trim())\n .map((l) => l.slice(3).trim())\n .filter((f) => f);\n modifiedFiles.forEach((f) => files.add(f));\n } catch {}\n\n return Array.from(files);\n }\n\n private async searchCodebase(\n keywords: string[],\n includePatterns: string[],\n excludePatterns: string[],\n depth: 'shallow' | 'medium' | 'deep',\n maxFiles: number\n ): Promise<DiscoveredFile[]> {\n const files: DiscoveredFile[] = [];\n const maxResults = depth === 'shallow' ? 10 : depth === 'medium' ? 25 : 50;\n\n // Build grep command for each keyword\n for (const keyword of keywords.slice(0, 10)) {\n try {\n const excludeArgs = excludePatterns\n .map((p) => `--exclude-dir=${p}`)\n .join(' ');\n const includeArgs = includePatterns\n .map((p) => `--include=${p}`)\n .join(' ');\n\n const cmd = `grep -ril ${excludeArgs} ${includeArgs} \"${keyword}\" . 2>/dev/null | head -${maxResults}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const matchedFiles = result.split('\\n').filter((f) => f.trim());\n for (const file of matchedFiles) {\n const cleanPath = file.replace(/^\\.\\//, '');\n const existing = files.find((f) => f.path === cleanPath);\n if (existing) {\n existing.matchedKeywords = existing.matchedKeywords || [];\n if (!existing.matchedKeywords.includes(keyword)) {\n existing.matchedKeywords.push(keyword);\n }\n } else {\n files.push({\n path: cleanPath,\n relevance: 'medium',\n reason: `Contains keyword: ${keyword}`,\n matchedKeywords: [keyword],\n });\n }\n }\n } catch {\n // Grep failed for this keyword, continue\n }\n }\n\n // Boost relevance for files with multiple keyword matches\n for (const file of files) {\n const matchCount = file.matchedKeywords?.length || 0;\n if (matchCount >= 3) {\n file.relevance = 'high';\n file.reason = `Matches ${matchCount} keywords: ${file.matchedKeywords?.slice(0, 3).join(', ')}`;\n }\n }\n\n return files;\n }\n\n private rankFiles(\n discovered: DiscoveredFile[],\n recent: string[],\n keywords: string[]\n ): DiscoveredFile[] {\n const recentSet = new Set(recent);\n\n // Add recent files with high relevance\n for (const recentFile of recent) {\n const existing = discovered.find((f) => f.path === recentFile);\n if (existing) {\n existing.relevance = 'high';\n existing.reason = 'Recently accessed + ' + existing.reason;\n } else {\n discovered.push({\n path: recentFile,\n relevance: 'high',\n reason: 'Recently accessed in context',\n });\n }\n }\n\n // Sort by relevance and match count\n return discovered.sort((a, b) => {\n const relevanceOrder = { high: 3, medium: 2, low: 1 };\n const relDiff = relevanceOrder[b.relevance] - relevanceOrder[a.relevance];\n if (relDiff !== 0) return relDiff;\n\n const aMatches = a.matchedKeywords?.length || 0;\n const bMatches = b.matchedKeywords?.length || 0;\n return bMatches - aMatches;\n });\n }\n\n private findFileReferences(file: string, maxFiles: number): DiscoveredFile[] {\n const results: DiscoveredFile[] = [];\n\n try {\n // Search for imports of this file\n const basename = file.replace(/\\.[^.]+$/, '');\n const cmd = `grep -ril \"from.*${basename}\" . --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.js\" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const files = result.split('\\n').filter((f) => f.trim());\n for (const f of files) {\n results.push({\n path: f.replace(/^\\.\\//, ''),\n relevance: 'high',\n reason: `Imports ${file}`,\n });\n }\n } catch {}\n\n return results;\n }\n\n private searchForConcept(\n concept: string,\n maxFiles: number\n ): DiscoveredFile[] {\n const results: DiscoveredFile[] = [];\n\n try {\n const cmd = `grep -ril \"${concept}\" . --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.md\" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const files = result.split('\\n').filter((f) => f.trim());\n for (const f of files) {\n results.push({\n path: f.replace(/^\\.\\//, ''),\n relevance: 'medium',\n reason: `Contains \"${concept}\"`,\n });\n }\n } catch {}\n\n return results;\n }\n\n private mergeAndDedupe(\n a: DiscoveredFile[],\n b: DiscoveredFile[]\n ): DiscoveredFile[] {\n const pathSet = new Set(a.map((f) => f.path));\n const merged = [...a];\n\n for (const file of b) {\n if (!pathSet.has(file.path)) {\n merged.push(file);\n pathSet.add(file.path);\n }\n }\n\n return merged;\n }\n\n private getRecentDecisions(): any[] {\n try {\n const decisions = this.deps.db\n .prepare(\n `\n SELECT a.text, a.type, a.priority, f.name as frame_name, a.created_at\n FROM anchors a\n JOIN frames f ON a.frame_id = f.frame_id\n WHERE a.type IN ('DECISION', 'CONSTRAINT', 'FACT')\n ORDER BY a.created_at DESC\n LIMIT 10\n `\n )\n .all();\n return decisions;\n } catch {\n return [];\n }\n }\n\n private generateContextSummary(\n keywords: string[],\n files: DiscoveredFile[]\n ): string {\n const hotStack = this.deps.frameManager.getHotStackContext(5);\n const currentGoal = hotStack[hotStack.length - 1]?.header?.goal;\n\n let summary = '';\n if (currentGoal) {\n summary += `Current task: ${currentGoal}\\n`;\n }\n summary += `Context keywords: ${keywords.slice(0, 10).join(', ')}\\n`;\n summary += `Relevant files found: ${files.length}\\n`;\n summary += `High relevance: ${files.filter((f) => f.relevance === 'high').length}`;\n\n return summary;\n }\n\n private formatDiscoveryResult(result: DiscoveryResult): string {\n let output = '# Discovery Results\\n\\n';\n\n output += '## Context Summary\\n';\n output += result.contextSummary + '\\n\\n';\n\n output += '## Relevant Files\\n\\n';\n for (const file of result.files.slice(0, 15)) {\n const icon =\n file.relevance === 'high'\n ? '[HIGH]'\n : file.relevance === 'medium'\n ? '[MED]'\n : '[LOW]';\n output += `${icon} ${file.path}\\n`;\n output += ` ${file.reason}\\n`;\n }\n\n if (result.keywords.length > 0) {\n output += '\\n## Keywords Used\\n';\n output += result.keywords.slice(0, 15).join(', ') + '\\n';\n }\n\n return output;\n }\n\n private formatRelatedFiles(\n files: DiscoveredFile[],\n file?: string,\n concept?: string\n ): string {\n let output = '# Related Files\\n\\n';\n\n if (file) output += `Related to file: ${file}\\n`;\n if (concept) output += `Related to concept: ${concept}\\n`;\n output += '\\n';\n\n for (const f of files) {\n output += `- ${f.path}\\n ${f.reason}\\n`;\n }\n\n return output;\n }\n\n private formatSessionSummary(summary: any): string {\n let output = '# Session Summary\\n\\n';\n\n output += `**Current Goal:** ${summary.currentGoal}\\n`;\n output += `**Active Frames:** ${summary.activeFrames}\\n`;\n output += `**Stack Depth:** ${summary.stackDepth}\\n\\n`;\n\n if (summary.recentFiles.length > 0) {\n output += '## Recent Files\\n';\n for (const f of summary.recentFiles) {\n output += `- ${f}\\n`;\n }\n output += '\\n';\n }\n\n if (summary.decisions.length > 0) {\n output += '## Recent Decisions\\n';\n for (const d of summary.decisions) {\n output += `- [${d.type}] ${d.text}\\n`;\n }\n }\n\n return output;\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,7 +16,7 @@ import {
16
16
  import { readFileSync, existsSync, mkdirSync } from "fs";
17
17
  import { join, dirname } from "path";
18
18
  import { execSync } from "child_process";
19
- import { FrameManager } from "../../core/context/frame-manager.js";
19
+ import { FrameManager } from "../../core/context/index.js";
20
20
  import {
21
21
  LinearTaskManager
22
22
  } from "../../features/tasks/linear-task-manager.js";