@goondocks/myco 0.6.3 → 0.6.5

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 (105) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/{chunk-CPVXNRGW.js → chunk-4B5RO2YV.js} +4 -4
  4. package/dist/{chunk-25FY74AP.js → chunk-4DYD4HHG.js} +25 -7
  5. package/dist/chunk-4DYD4HHG.js.map +1 -0
  6. package/dist/{chunk-DBMHUMG3.js → chunk-54WVLTKD.js} +3 -3
  7. package/dist/{chunk-WU4PCNIK.js → chunk-5LMRZDH3.js} +2 -2
  8. package/dist/{chunk-CQ4RKK67.js → chunk-AHZN4Z34.js} +2 -2
  9. package/dist/{chunk-WBLTISAK.js → chunk-DYDBF5W6.js} +36 -6
  10. package/dist/chunk-DYDBF5W6.js.map +1 -0
  11. package/dist/{chunk-JSK7L46L.js → chunk-ERG2IEWX.js} +22 -4
  12. package/dist/{chunk-JSK7L46L.js.map → chunk-ERG2IEWX.js.map} +1 -1
  13. package/dist/{chunk-RNWALAFP.js → chunk-F7GAYVWF.js} +2 -2
  14. package/dist/chunk-F7GAYVWF.js.map +1 -0
  15. package/dist/{chunk-H7PRCVGQ.js → chunk-F7PGDD2X.js} +2 -2
  16. package/dist/{chunk-RY76WEN3.js → chunk-GENQ5QGP.js} +2 -2
  17. package/dist/{chunk-YG6MLLGL.js → chunk-HYVT345Y.js} +2 -2
  18. package/dist/{chunk-LDKXXKF6.js → chunk-LEK6DEAE.js} +4 -4
  19. package/dist/{chunk-IWBWZQK6.js → chunk-MDLSAFPP.js} +2 -2
  20. package/dist/{chunk-QLUE3BUL.js → chunk-O6TBHGVO.js} +9 -2
  21. package/dist/chunk-O6TBHGVO.js.map +1 -0
  22. package/dist/{chunk-RXJHB7W4.js → chunk-OEGZ5YTJ.js} +2 -2
  23. package/dist/{chunk-XNAM6Z4O.js → chunk-P723N2LP.js} +2 -2
  24. package/dist/{chunk-PQWQC3RF.js → chunk-TK7A4RX7.js} +144 -146
  25. package/dist/chunk-TK7A4RX7.js.map +1 -0
  26. package/dist/{chunk-ALBVNGCF.js → chunk-V6BJVYNH.js} +55 -44
  27. package/dist/{chunk-ALBVNGCF.js.map → chunk-V6BJVYNH.js.map} +1 -1
  28. package/dist/{chunk-CK24O5YQ.js → chunk-XH34FX4C.js} +2 -2
  29. package/dist/{chunk-4WL5X7VS.js → chunk-YRIIBPJD.js} +3 -3
  30. package/dist/{cli-EGWAINIE.js → cli-OJYHLO4Y.js} +21 -21
  31. package/dist/{client-FDKJ4BY7.js → client-SS3C5MF6.js} +5 -5
  32. package/dist/{config-HDUFDOQN.js → config-IBS6KOLQ.js} +3 -3
  33. package/dist/{curate-OHIJFBYF.js → curate-4CKEMOPV.js} +9 -10
  34. package/dist/{curate-OHIJFBYF.js.map → curate-4CKEMOPV.js.map} +1 -1
  35. package/dist/{detect-providers-4U3ZPW5G.js → detect-providers-LFIVJYQO.js} +3 -3
  36. package/dist/{digest-I2XYCK2M.js → digest-ZLARHLLY.js} +11 -11
  37. package/dist/{init-ZO2XQT6U.js → init-3LVKVQ4L.js} +8 -8
  38. package/dist/{logs-IENORIYR.js → logs-6CWVP574.js} +3 -3
  39. package/dist/{main-XZ6X4BUX.js → main-RB727YRP.js} +2109 -390
  40. package/dist/main-RB727YRP.js.map +1 -0
  41. package/dist/{rebuild-NAH4EW5B.js → rebuild-QWVVCBCZ.js} +9 -10
  42. package/dist/{rebuild-NAH4EW5B.js.map → rebuild-QWVVCBCZ.js.map} +1 -1
  43. package/dist/{reprocess-6FOP37XS.js → reprocess-YG3WLUI2.js} +11 -11
  44. package/dist/{restart-WSA4JSE3.js → restart-UIP7US4U.js} +6 -6
  45. package/dist/{search-QXJQUB35.js → search-BQLBW5CS.js} +6 -6
  46. package/dist/{server-VXN3CJ4Y.js → server-43KSJ65Q.js} +80 -32
  47. package/dist/{server-VXN3CJ4Y.js.map → server-43KSJ65Q.js.map} +1 -1
  48. package/dist/{session-start-KQ4KCQMZ.js → session-start-6SHGT2AW.js} +9 -9
  49. package/dist/setup-digest-X735EZSD.js +15 -0
  50. package/dist/setup-llm-QBSTQO7N.js +15 -0
  51. package/dist/src/cli.js +4 -4
  52. package/dist/src/daemon/main.js +4 -4
  53. package/dist/src/hooks/post-tool-use.js +5 -5
  54. package/dist/src/hooks/session-end.js +5 -5
  55. package/dist/src/hooks/session-start.js +4 -4
  56. package/dist/src/hooks/stop.js +7 -7
  57. package/dist/src/hooks/user-prompt-submit.js +5 -5
  58. package/dist/src/mcp/server.js +4 -4
  59. package/dist/src/prompts/extraction.md +4 -4
  60. package/dist/{stats-43OESUEB.js → stats-QBLIEFWL.js} +6 -6
  61. package/dist/ui/assets/index-CjWGVHhF.css +1 -0
  62. package/dist/ui/assets/index-Cq-H7wgE.js +369 -0
  63. package/dist/ui/index.html +2 -2
  64. package/dist/{verify-IIAHBAAU.js → verify-X272WGBD.js} +6 -6
  65. package/dist/{version-NKOECSVH.js → version-XE4GYTBV.js} +4 -4
  66. package/package.json +1 -1
  67. package/dist/chunk-25FY74AP.js.map +0 -1
  68. package/dist/chunk-PQWQC3RF.js.map +0 -1
  69. package/dist/chunk-QLUE3BUL.js.map +0 -1
  70. package/dist/chunk-RNWALAFP.js.map +0 -1
  71. package/dist/chunk-WBLTISAK.js.map +0 -1
  72. package/dist/main-XZ6X4BUX.js.map +0 -1
  73. package/dist/setup-digest-QNCM3PNQ.js +0 -15
  74. package/dist/setup-llm-EAOIUSPJ.js +0 -15
  75. package/dist/ui/assets/index-Bk4X_8-Z.css +0 -1
  76. package/dist/ui/assets/index-D3SY7ZHY.js +0 -299
  77. /package/dist/{chunk-CPVXNRGW.js.map → chunk-4B5RO2YV.js.map} +0 -0
  78. /package/dist/{chunk-DBMHUMG3.js.map → chunk-54WVLTKD.js.map} +0 -0
  79. /package/dist/{chunk-WU4PCNIK.js.map → chunk-5LMRZDH3.js.map} +0 -0
  80. /package/dist/{chunk-CQ4RKK67.js.map → chunk-AHZN4Z34.js.map} +0 -0
  81. /package/dist/{chunk-H7PRCVGQ.js.map → chunk-F7PGDD2X.js.map} +0 -0
  82. /package/dist/{chunk-RY76WEN3.js.map → chunk-GENQ5QGP.js.map} +0 -0
  83. /package/dist/{chunk-YG6MLLGL.js.map → chunk-HYVT345Y.js.map} +0 -0
  84. /package/dist/{chunk-LDKXXKF6.js.map → chunk-LEK6DEAE.js.map} +0 -0
  85. /package/dist/{chunk-IWBWZQK6.js.map → chunk-MDLSAFPP.js.map} +0 -0
  86. /package/dist/{chunk-RXJHB7W4.js.map → chunk-OEGZ5YTJ.js.map} +0 -0
  87. /package/dist/{chunk-XNAM6Z4O.js.map → chunk-P723N2LP.js.map} +0 -0
  88. /package/dist/{chunk-CK24O5YQ.js.map → chunk-XH34FX4C.js.map} +0 -0
  89. /package/dist/{chunk-4WL5X7VS.js.map → chunk-YRIIBPJD.js.map} +0 -0
  90. /package/dist/{cli-EGWAINIE.js.map → cli-OJYHLO4Y.js.map} +0 -0
  91. /package/dist/{client-FDKJ4BY7.js.map → client-SS3C5MF6.js.map} +0 -0
  92. /package/dist/{config-HDUFDOQN.js.map → config-IBS6KOLQ.js.map} +0 -0
  93. /package/dist/{detect-providers-4U3ZPW5G.js.map → detect-providers-LFIVJYQO.js.map} +0 -0
  94. /package/dist/{digest-I2XYCK2M.js.map → digest-ZLARHLLY.js.map} +0 -0
  95. /package/dist/{init-ZO2XQT6U.js.map → init-3LVKVQ4L.js.map} +0 -0
  96. /package/dist/{logs-IENORIYR.js.map → logs-6CWVP574.js.map} +0 -0
  97. /package/dist/{reprocess-6FOP37XS.js.map → reprocess-YG3WLUI2.js.map} +0 -0
  98. /package/dist/{restart-WSA4JSE3.js.map → restart-UIP7US4U.js.map} +0 -0
  99. /package/dist/{search-QXJQUB35.js.map → search-BQLBW5CS.js.map} +0 -0
  100. /package/dist/{session-start-KQ4KCQMZ.js.map → session-start-6SHGT2AW.js.map} +0 -0
  101. /package/dist/{setup-digest-QNCM3PNQ.js.map → setup-digest-X735EZSD.js.map} +0 -0
  102. /package/dist/{setup-llm-EAOIUSPJ.js.map → setup-llm-QBSTQO7N.js.map} +0 -0
  103. /package/dist/{stats-43OESUEB.js.map → stats-QBLIEFWL.js.map} +0 -0
  104. /package/dist/{verify-IIAHBAAU.js.map → verify-X272WGBD.js.map} +0 -0
  105. /package/dist/{version-NKOECSVH.js.map → version-XE4GYTBV.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  PROMPT_PREVIEW_CHARS
4
- } from "./chunk-WBLTISAK.js";
4
+ } from "./chunk-DYDBF5W6.js";
5
5
 
6
6
  // src/agents/adapter.ts
7
7
  import fs from "fs";
@@ -351,4 +351,4 @@ export {
351
351
  claudeCodeAdapter,
352
352
  AgentRegistry
353
353
  };
354
- //# sourceMappingURL=chunk-RNWALAFP.js.map
354
+ //# sourceMappingURL=chunk-F7GAYVWF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/adapter.ts","../src/agents/claude-code.ts","../src/agents/cursor.ts","../src/agents/registry.ts"],"sourcesContent":["/**\n * Agent adapter interface — declares what each coding agent provides to Myco.\n *\n * Each supported agent (Claude Code, Cursor, Cline, etc.) has an adapter that\n * tells Myco where to find transcripts, how to parse them, and what capabilities\n * the agent supports. The daemon uses these adapters at runtime to read the\n * authoritative conversation record.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** An image attached to a conversation turn */\nexport interface TranscriptImage {\n /** Base64-encoded image data */\n data: string;\n /** MIME type (e.g., image/png) */\n mediaType: string;\n}\n\n/** A single conversation turn extracted from an agent's transcript */\nexport interface TranscriptTurn {\n prompt: string;\n toolCount: number;\n /** Per-tool call counts (e.g., { Read: 5, Edit: 3 }). Populated from buffer events. */\n toolBreakdown?: Record<string, number>;\n /** Deduplicated file paths touched in this turn. Populated from buffer events. */\n files?: string[];\n aiResponse?: string;\n timestamp: string;\n /** Images attached to this turn's user prompt */\n images?: TranscriptImage[];\n}\n\n/**\n * Maps agent-specific hook field names to normalized names.\n * Each agent's hook system uses different field names for the same data.\n */\nexport interface HookFieldNames {\n /** Field name for the transcript file path (e.g., 'transcript_path') */\n transcriptPath: string;\n /** Field name for the last AI response text (e.g., 'last_assistant_message') */\n lastResponse: string;\n /** Field name for the session ID (e.g., 'session_id') */\n sessionId: string;\n}\n\nexport interface AgentAdapter {\n /** Agent identifier (matches plugin directory names) */\n readonly name: string;\n /** Human-readable display name */\n readonly displayName: string;\n /** Environment variable for the plugin root directory */\n readonly pluginRootEnvVar: string;\n /** Maps agent-specific hook body field names to normalized names */\n readonly hookFields: HookFieldNames;\n\n /**\n * Find the transcript file for a given session ID.\n * Returns the absolute path if found, null otherwise.\n */\n findTranscript(sessionId: string): string | null;\n\n /**\n * Parse a transcript file's content into normalized turns.\n * Each adapter handles its agent's specific format.\n */\n parseTurns(content: string): TranscriptTurn[];\n\n /**\n * Write MYCO_VAULT_DIR into this agent's project-level config file.\n * Called during init when the vault is outside the project root.\n * Returns true if the config was written, false if not applicable.\n */\n configureVaultEnv(projectRoot: string, vaultDir: string): boolean;\n}\n\n/**\n * Scan subdirectories of baseDir for a JSONL transcript file matching sessionId.\n * Shared by claude-code, cursor, custom adapters, and tests.\n */\nexport function findJsonlInSubdirs(baseDir: string, sessionId: string): string | null {\n try {\n for (const entry of fs.readdirSync(baseDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const candidate = path.join(baseDir, entry.name, `${sessionId}.jsonl`);\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { /* not here */ }\n }\n } catch { /* baseDir doesn't exist or unreadable */ }\n return null;\n}\n\n/**\n * Factory for creating simple per-project adapters from a base directory.\n * Used for user-configured transcript_paths and testing.\n */\nexport function createPerProjectAdapter(\n baseDir: string,\n parseTurns: AgentAdapter['parseTurns'],\n name?: string,\n): AgentAdapter {\n return {\n name: name ?? `custom:${path.basename(baseDir)}`,\n displayName: `Custom (${baseDir})`,\n pluginRootEnvVar: '',\n hookFields: { transcriptPath: 'transcript_path', lastResponse: 'last_assistant_message', sessionId: 'session_id' },\n findTranscript: (sessionId) => findJsonlInSubdirs(baseDir, sessionId),\n parseTurns,\n configureVaultEnv: () => false,\n };\n}\n\n/** Map MIME type to file extension */\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/png': 'png',\n};\n\nexport function extensionForMimeType(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? 'png';\n}\n\n/** Map file extension to MIME type */\nconst EXT_TO_MIME: Record<string, string> = {\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.png': 'image/png',\n};\n\nexport function mimeTypeForExtension(ext: string): string {\n return EXT_TO_MIME[ext.toLowerCase()] ?? 'image/png';\n}\n\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\n\n/** Claude Code injects [Image: source: /path] text alongside base64 image blocks. Strip these since the actual images are captured as Obsidian embeds. */\nconst IMAGE_TEXT_REF_PATTERN = /\\[Image: source: [^\\]]+\\]\\n*/g;\n\nexport interface ParseJsonlOptions {\n /** Field name containing the message role ('type' for Claude Code, 'role' for Cursor) */\n roleField: 'type' | 'role';\n /** Whether entries have a timestamp field to extract */\n extractTimestamp: boolean;\n /** Whether to check for text-only user messages (Claude Code has tool_result user messages to skip) */\n skipToolResultUsers: boolean;\n /** Whether to strip [Image: source: ...] text references from prompts (Claude Code-specific) */\n stripImageTextRefs: boolean;\n}\n\n/**\n * Shared JSONL transcript parser — used by both Claude Code and Cursor adapters.\n * Handles user/assistant role detection, text/image extraction, and tool counting.\n */\nexport function parseJsonlTurns(content: string, opts: ParseJsonlOptions): TranscriptTurn[] {\n const lines = content.split('\\n').filter(Boolean);\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const line of lines) {\n let entry: Record<string, unknown>;\n try { entry = JSON.parse(line); } catch { continue; }\n\n const role = entry[opts.roleField] as string;\n const timestamp = opts.extractTimestamp ? (entry.timestamp as string ?? '') : '';\n\n if (role === 'user') {\n const msg = entry.message as { content?: Array<{ type: string; text?: string; source?: { type?: string; data?: string; media_type?: string } }> } | undefined;\n const blocks = Array.isArray(msg?.content) ? msg!.content : [];\n const hasText = blocks.some((b) => b.type === 'text' && b.text?.trim());\n\n if (!hasText && opts.skipToolResultUsers) continue;\n if (!hasText) continue;\n\n if (current) turns.push(current);\n\n const rawPrompt = blocks\n .filter((b) => b.type === 'text' && b.text)\n .map((b) => b.text!)\n .join('\\n');\n\n const promptText = (opts.stripImageTextRefs ? rawPrompt.replace(IMAGE_TEXT_REF_PATTERN, '') : rawPrompt)\n .trim()\n .slice(0, PROMPT_PREVIEW_CHARS);\n\n const images: TranscriptImage[] = blocks\n .filter((b) => b.type === 'image' && b.source?.type === 'base64' && b.source.data)\n .map((b) => ({ data: b.source!.data!, mediaType: b.source!.media_type ?? 'image/png' }));\n\n current = { prompt: promptText, toolCount: 0, timestamp, ...(images.length > 0 ? { images } : {}) };\n } else if (role === 'assistant' && current) {\n const msg = entry.message as { content?: Array<{ type: string; text?: string }> } | undefined;\n if (Array.isArray(msg?.content)) {\n const textParts = msg!.content.filter((b) => b.type === 'text' && b.text).map((b) => b.text!);\n const text = textParts.join('\\n').trim();\n if (text) current.aiResponse = text;\n current.toolCount += msg!.content.filter((b) => b.type === 'tool_use').length;\n }\n }\n }\n\n if (current) turns.push(current);\n return turns;\n}\n","import type { AgentAdapter } from './adapter.js';\nimport { findJsonlInSubdirs, parseJsonlTurns } from './adapter.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\nconst TRANSCRIPT_BASE = path.join(os.homedir(), '.claude', 'projects');\n\nexport const claudeCodeAdapter: AgentAdapter = {\n name: 'claude-code',\n displayName: 'Claude Code',\n pluginRootEnvVar: 'CLAUDE_PLUGIN_ROOT',\n hookFields: {\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n sessionId: 'session_id',\n },\n\n findTranscript: (sessionId) => findJsonlInSubdirs(TRANSCRIPT_BASE, sessionId),\n\n parseTurns: (content) => parseJsonlTurns(content, {\n roleField: 'type',\n extractTimestamp: true,\n skipToolResultUsers: true,\n stripImageTextRefs: true,\n }),\n\n configureVaultEnv: (projectRoot, vaultDir) => {\n const settingsDir = path.join(projectRoot, '.claude');\n if (!fs.existsSync(settingsDir)) return false;\n\n // Write to settings.json — Claude Code only injects env vars from this\n // file into hook processes (settings.user.json env is not propagated).\n // The caller passes the collapsed ~/... form so the committed path\n // doesn't leak the user's home directory.\n const settingsPath = path.join(settingsDir, 'settings.json');\n let settings: Record<string, unknown> = {};\n if (fs.existsSync(settingsPath)) {\n try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')); } catch { /* fresh */ }\n }\n const env = (settings.env ?? {}) as Record<string, string>;\n env.MYCO_VAULT_DIR = vaultDir;\n settings.env = env;\n fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n return true;\n },\n};\n","import type { AgentAdapter } from './adapter.js';\nimport type { TranscriptTurn, TranscriptImage } from './adapter.js';\nimport { mimeTypeForExtension, parseJsonlTurns } from './adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n/**\n * Cursor stores conversation transcripts in:\n * ~/.cursor/projects/<project-path>/agent-transcripts/<session-id>.txt\n *\n * Images are saved as files in:\n * ~/.cursor/projects/<project-path>/assets/<filename>.png\n *\n * Transcript format is plain text with role markers on their own line:\n * user: — human prompt (may contain <image_files> and <user_query> tags)\n * assistant: — assistant response (may contain [Tool call] and [Thinking] blocks)\n */\n\nconst USER_MARKER = '\\nuser:\\n';\nconst ASSISTANT_MARKER = '\\nassistant:\\n';\nconst TOOL_CALL_MARKER = '[Tool call]';\nconst TOOL_RESULT_MARKER = '[Tool result]';\nconst THINKING_MARKER = '[Thinking]';\n\nfunction getCursorProjectsBase(): string {\n return path.join(os.homedir(), '.cursor', 'projects');\n}\n\nconst CURSOR_PROJECTS = getCursorProjectsBase();\n\nexport const cursorAdapter: AgentAdapter = {\n name: 'cursor',\n displayName: 'Cursor',\n pluginRootEnvVar: 'CURSOR_PLUGIN_ROOT',\n hookFields: {\n transcriptPath: 'transcript_path',\n lastResponse: 'last_assistant_message',\n sessionId: 'conversation_id',\n },\n\n findTranscript(sessionId: string): string | null {\n try {\n for (const project of fs.readdirSync(CURSOR_PROJECTS, { withFileTypes: true })) {\n if (!project.isDirectory()) continue;\n const transcriptsDir = path.join(CURSOR_PROJECTS, project.name, 'agent-transcripts');\n // Try .txt (older Cursor) then .jsonl inside session directory (newer Cursor)\n for (const candidate of [\n path.join(transcriptsDir, `${sessionId}.txt`),\n path.join(transcriptsDir, sessionId, `${sessionId}.jsonl`),\n ]) {\n try {\n fs.accessSync(candidate);\n return candidate;\n } catch { /* not here */ }\n }\n }\n } catch { /* projects dir doesn't exist */ }\n return null;\n },\n\n parseTurns(content: string): TranscriptTurn[] {\n // Detect format: JSONL (starts with '{') or plain text (starts with 'user:')\n const trimmed = content.trimStart();\n if (trimmed.startsWith('{')) {\n return parseCursorJsonl(content);\n }\n return parseCursorText(content);\n },\n\n configureVaultEnv(projectRoot: string, vaultDir: string): boolean {\n const mcpPath = path.join(projectRoot, '.cursor', 'mcp.json');\n if (!fs.existsSync(mcpPath)) return false;\n\n try {\n const config = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\n if (config.mcpServers?.myco) {\n config.mcpServers.myco.env = { ...config.mcpServers.myco.env, MYCO_VAULT_DIR: vaultDir };\n fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n return true;\n }\n } catch { /* malformed config */ }\n return false;\n },\n};\n\n/** Parse Cursor's newer JSONL format — same structure as Claude's but uses 'role' field */\nfunction parseCursorJsonl(content: string): TranscriptTurn[] {\n return parseJsonlTurns(content, {\n roleField: 'role',\n extractTimestamp: false,\n skipToolResultUsers: false,\n stripImageTextRefs: false,\n });\n}\n\n/** Parse Cursor's older plain-text transcript format. */\nfunction parseCursorText(content: string): TranscriptTurn[] {\n const turns: TranscriptTurn[] = [];\n // Split on user marker — each block is a new human turn.\n const sections = ('\\n' + content).split(USER_MARKER).slice(1);\n\n for (const section of sections) {\n // Extract user query from <user_query> tags or raw text before first assistant response\n let promptText = '';\n const queryMatch = section.match(/<user_query>\\s*([\\s\\S]*?)\\s*<\\/user_query>/);\n if (queryMatch) {\n promptText = queryMatch[1].trim().slice(0, PROMPT_PREVIEW_CHARS);\n } else {\n // No tags — take text before the first assistant response.\n const beforeAssistant = section.split(ASSISTANT_MARKER)[0];\n promptText = beforeAssistant.replace(/<[^>]+>[\\s\\S]*?<\\/[^>]+>/g, '').trim().slice(0, PROMPT_PREVIEW_CHARS);\n }\n\n // Extract image references from <image_files> tags\n const images: TranscriptImage[] = [];\n const imageFilesMatch = section.match(/<image_files>([\\s\\S]*?)<\\/image_files>/);\n if (imageFilesMatch) {\n const imageBlock = imageFilesMatch[1];\n const pathMatches = imageBlock.matchAll(/^\\d+\\.\\s+(.+\\.(?:png|jpg|jpeg|gif|webp))\\s*$/gmi);\n for (const match of pathMatches) {\n const imagePath = match[1].trim();\n try {\n const data = fs.readFileSync(imagePath).toString('base64');\n const mediaType = mimeTypeForExtension(path.extname(imagePath));\n images.push({ data, mediaType });\n } catch {\n // Image file not accessible — skip\n }\n }\n }\n\n // Count tool calls in assistant sections\n const toolCallCount = section.split(TOOL_CALL_MARKER).length - 1;\n\n // Extract the last meaningful assistant text response.\n // Scan assistant blocks (split on \\nA:\\n) from the end.\n // A block is \"meaningful\" if it contains lines that aren't tool calls/results/thinking.\n let aiResponse: string | undefined;\n const assistantBlocks = section.split(ASSISTANT_MARKER).slice(1);\n for (let j = assistantBlocks.length - 1; j >= 0; j--) {\n const lines = assistantBlocks[j].split('\\n');\n const textLines: string[] = [];\n let skip = false;\n for (const line of lines) {\n // Skip tool calls, tool results, and thinking blocks\n if (line.startsWith(TOOL_CALL_MARKER) || line.startsWith(TOOL_RESULT_MARKER) || line.startsWith(THINKING_MARKER)) {\n skip = true;\n continue;\n }\n // Resume after a blank line following a skipped block\n if (skip && line.trim() === '') continue;\n if (skip && !line.startsWith(' ')) skip = false; // End of indented tool args\n if (skip) continue;\n textLines.push(line);\n }\n const text = textLines.join('\\n').trim();\n if (text) {\n aiResponse = text;\n break;\n }\n }\n\n if (promptText || images.length > 0) {\n turns.push({\n prompt: promptText,\n toolCount: toolCallCount,\n timestamp: '',\n ...(aiResponse ? { aiResponse } : {}),\n ...(images.length > 0 ? { images } : {}),\n });\n }\n }\n\n return turns;\n}\n","import type { AgentAdapter, TranscriptTurn } from './adapter.js';\nimport { claudeCodeAdapter } from './claude-code.js';\nimport { cursorAdapter } from './cursor.js';\nimport fs from 'node:fs';\n\n/**\n * All known agent adapters, ordered by priority.\n * When searching for a transcript, adapters are tried in order.\n * Add new adapters here as agent support grows.\n */\nconst ALL_ADAPTERS: AgentAdapter[] = [\n claudeCodeAdapter,\n cursorAdapter,\n];\n\nexport class AgentRegistry {\n private adapters: AgentAdapter[];\n\n constructor(additionalAdapters: AgentAdapter[] = []) {\n this.adapters = [...ALL_ADAPTERS, ...additionalAdapters];\n }\n\n /**\n * Find and parse transcript turns for a session.\n * Tries each adapter in priority order. Returns the first match.\n */\n getTranscriptTurns(sessionId: string): { turns: TranscriptTurn[]; source: string } | null {\n for (const adapter of this.adapters) {\n const filePath = adapter.findTranscript(sessionId);\n if (!filePath) continue;\n\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const turns = adapter.parseTurns(content);\n if (turns.length > 0) {\n return { turns, source: adapter.name };\n }\n } catch {\n // Adapter found a path but read/parse failed — try next\n }\n }\n return null;\n }\n\n /** List all registered adapter names */\n get adapterNames(): string[] {\n return this.adapters.map((a) => a.name);\n }\n\n /** Get a specific adapter by name */\n getAdapter(name: string): AgentAdapter | undefined {\n return this.adapters.find((a) => a.name === name);\n }\n\n /** Detect which agent is currently active based on environment variables */\n detectActiveAgent(): AgentAdapter | undefined {\n for (const adapter of this.adapters) {\n if (process.env[adapter.pluginRootEnvVar]) {\n return adapter;\n }\n }\n return undefined;\n }\n\n /**\n * Parse turns from a known transcript file path (provided by hook).\n * Tries each adapter's parseTurns until one produces results.\n * Skips directory scanning entirely — the path is already known.\n */\n parseTurnsFromPath(filePath: string): { turns: TranscriptTurn[]; source: string } | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n // Try the active agent's parser first, then fall back to others\n const active = this.detectActiveAgent();\n const orderedAdapters = active\n ? [active, ...this.adapters.filter((a) => a !== active)]\n : this.adapters;\n\n for (const adapter of orderedAdapters) {\n const turns = adapter.parseTurns(content);\n if (turns.length > 0) {\n return { turns, source: `${adapter.name}:direct` };\n }\n }\n } catch {\n // File unreadable — caller will fall back to directory scanning\n }\n return null;\n }\n\n /**\n * Resolve the plugin root directory from the active agent's environment variable.\n * Returns undefined if no agent env var is set (e.g., running from CLI directly).\n */\n resolvePluginRoot(): string | undefined {\n for (const adapter of this.adapters) {\n const value = process.env[adapter.pluginRootEnvVar];\n if (value) return value;\n }\n return undefined;\n }\n}\n"],"mappings":"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","names":["fs","path","fs","path","os","fs"]}
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  LEVEL_ORDER
4
- } from "./chunk-QLUE3BUL.js";
4
+ } from "./chunk-O6TBHGVO.js";
5
5
 
6
6
  // src/logs/reader.ts
7
7
  import fs from "fs";
@@ -88,4 +88,4 @@ export {
88
88
  queryLogs,
89
89
  matchesFilter
90
90
  };
91
- //# sourceMappingURL=chunk-H7PRCVGQ.js.map
91
+ //# sourceMappingURL=chunk-F7PGDD2X.js.map
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  require_dist
4
- } from "./chunk-JSK7L46L.js";
4
+ } from "./chunk-ERG2IEWX.js";
5
5
  import {
6
6
  __toESM
7
7
  } from "./chunk-PZUWP5VK.js";
@@ -34,4 +34,4 @@ export {
34
34
  sessionFm,
35
35
  sporeFm
36
36
  };
37
- //# sourceMappingURL=chunk-RY76WEN3.js.map
37
+ //# sourceMappingURL=chunk-GENQ5QGP.js.map
@@ -2,7 +2,7 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  MycoConfigSchema,
4
4
  require_dist
5
- } from "./chunk-JSK7L46L.js";
5
+ } from "./chunk-ERG2IEWX.js";
6
6
  import {
7
7
  __toESM
8
8
  } from "./chunk-PZUWP5VK.js";
@@ -156,4 +156,4 @@ export {
156
156
  loadConfig,
157
157
  saveConfig
158
158
  };
159
- //# sourceMappingURL=chunk-YG6MLLGL.js.map
159
+ //# sourceMappingURL=chunk-HYVT345Y.js.map
@@ -3,17 +3,17 @@ import {
3
3
  VaultWriter,
4
4
  indexNote,
5
5
  supersedeSpore
6
- } from "./chunk-ALBVNGCF.js";
6
+ } from "./chunk-V6BJVYNH.js";
7
7
  import {
8
8
  generateEmbedding
9
9
  } from "./chunk-RGVBGTD6.js";
10
10
  import {
11
11
  stripFrontmatter
12
- } from "./chunk-RY76WEN3.js";
12
+ } from "./chunk-GENQ5QGP.js";
13
13
  import {
14
14
  DIGEST_TIERS,
15
15
  EMBEDDING_INPUT_LIMIT
16
- } from "./chunk-WBLTISAK.js";
16
+ } from "./chunk-DYDBF5W6.js";
17
17
 
18
18
  // src/mcp/tools/context.ts
19
19
  import fs from "fs";
@@ -110,4 +110,4 @@ export {
110
110
  consolidateSpores,
111
111
  handleMycoContext
112
112
  };
113
- //# sourceMappingURL=chunk-LDKXXKF6.js.map
113
+ //# sourceMappingURL=chunk-LEK6DEAE.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  MycoConfigSchema,
7
7
  require_dist
8
- } from "./chunk-JSK7L46L.js";
8
+ } from "./chunk-ERG2IEWX.js";
9
9
  import {
10
10
  __toESM
11
11
  } from "./chunk-PZUWP5VK.js";
@@ -96,4 +96,4 @@ async function run(args, vaultDir) {
96
96
  export {
97
97
  run
98
98
  };
99
- //# sourceMappingURL=chunk-IWBWZQK6.js.map
99
+ //# sourceMappingURL=chunk-MDLSAFPP.js.map
@@ -5,7 +5,7 @@ import fs from "fs";
5
5
  import path from "path";
6
6
 
7
7
  // src/daemon/log-buffer.ts
8
- var LOG_RING_BUFFER_CAPACITY = 1e3;
8
+ var LOG_RING_BUFFER_CAPACITY = 5e3;
9
9
  var LOG_QUERY_DEFAULT_LIMIT = 100;
10
10
  var LogRingBuffer = class {
11
11
  buffer;
@@ -49,6 +49,7 @@ var LogRingBuffer = class {
49
49
  const bufIdx = (this.head - this.count + i + this.capacity) % this.capacity;
50
50
  const entry = this.buffer[bufIdx];
51
51
  if (entry && LEVEL_ORDER[entry.level] >= minLevel) {
52
+ if (options?.component && entry.component !== options.component) continue;
52
53
  entries.push(entry);
53
54
  }
54
55
  }
@@ -104,6 +105,12 @@ var DaemonLogger = class {
104
105
  error(component, message, data) {
105
106
  this.write("error", component, message, data);
106
107
  }
108
+ /** Dispatch a log entry by dynamic level string. */
109
+ log(level, component, message, data) {
110
+ if (level in LEVEL_ORDER) {
111
+ this.write(level, component, message, data);
112
+ }
113
+ }
107
114
  close() {
108
115
  if (this.fd !== null) {
109
116
  fs.closeSync(this.fd);
@@ -158,4 +165,4 @@ export {
158
165
  LEVEL_ORDER,
159
166
  DaemonLogger
160
167
  };
161
- //# sourceMappingURL=chunk-QLUE3BUL.js.map
168
+ //# sourceMappingURL=chunk-O6TBHGVO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/logger.ts","../src/daemon/log-buffer.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { LogRingBuffer } from './log-buffer.js';\n\nexport interface LogEntry {\n timestamp: string;\n level: string;\n component: string;\n message: string;\n [key: string]: unknown;\n}\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport const LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0, info: 1, warn: 2, error: 3,\n};\n\ninterface LoggerOptions {\n level?: LogLevel;\n maxSize?: number;\n maxFiles?: number;\n}\n\nexport class DaemonLogger {\n private logPath: string;\n private fd: number | null = null;\n private currentSize = 0;\n private level: LogLevel;\n private maxSize: number;\n private maxFiles: number;\n private logDir: string;\n private ringBuffer: LogRingBuffer;\n\n constructor(logDir: string, options: LoggerOptions = {}) {\n this.logDir = logDir;\n this.logPath = path.join(logDir, 'daemon.log');\n this.level = options.level ?? 'info';\n this.maxSize = options.maxSize ?? 5_242_880;\n this.maxFiles = options.maxFiles ?? 3;\n this.ringBuffer = new LogRingBuffer();\n\n fs.mkdirSync(logDir, { recursive: true });\n this.fd = fs.openSync(this.logPath, 'a');\n try {\n this.currentSize = fs.fstatSync(this.fd).size;\n } catch {\n this.currentSize = 0;\n }\n }\n\n debug(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('debug', component, message, data);\n }\n\n info(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('info', component, message, data);\n }\n\n warn(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('warn', component, message, data);\n }\n\n error(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('error', component, message, data);\n }\n\n /** Dispatch a log entry by dynamic level string. */\n log(level: string, component: string, message: string, data?: Record<string, unknown>): void {\n if (level in LEVEL_ORDER) {\n this.write(level as LogLevel, component, message, data);\n }\n }\n\n close(): void {\n if (this.fd !== null) {\n fs.closeSync(this.fd);\n this.fd = null;\n }\n }\n\n getRingBuffer(): LogRingBuffer {\n return this.ringBuffer;\n }\n\n private write(level: LogLevel, component: string, message: string, data?: Record<string, unknown>): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) return;\n\n const entry: LogEntry = {\n timestamp: new Date().toISOString(),\n level,\n component,\n message,\n ...data,\n };\n\n this.ringBuffer.push(entry);\n\n const line = JSON.stringify(entry) + '\\n';\n const bytes = Buffer.byteLength(line);\n\n if (this.currentSize + bytes > this.maxSize) {\n this.rotate();\n }\n\n if (this.fd !== null) {\n fs.writeSync(this.fd, line);\n this.currentSize += bytes;\n }\n }\n\n private rotate(): void {\n this.close();\n\n for (let i = this.maxFiles - 1; i >= 1; i--) {\n const from = path.join(this.logDir, `daemon.${i}.log`);\n const to = path.join(this.logDir, `daemon.${i + 1}.log`);\n if (fs.existsSync(from)) {\n if (i + 1 > this.maxFiles) {\n fs.unlinkSync(from);\n } else {\n fs.renameSync(from, to);\n }\n }\n }\n\n if (fs.existsSync(this.logPath)) {\n fs.renameSync(this.logPath, path.join(this.logDir, 'daemon.1.log'));\n }\n\n this.fd = fs.openSync(this.logPath, 'a');\n this.currentSize = 0;\n }\n}\n","import type { LogEntry, LogLevel } from './logger.js';\nimport { LEVEL_ORDER } from './logger.js';\n\nconst LOG_RING_BUFFER_CAPACITY = 5000;\nconst LOG_QUERY_DEFAULT_LIMIT = 100;\n\ninterface LogQueryResult {\n entries: LogEntry[];\n cursor: string;\n cursor_reset?: boolean;\n}\n\ninterface LogQueryOptions {\n level?: LogLevel;\n component?: string;\n limit?: number;\n}\n\nexport class LogRingBuffer {\n private buffer: LogEntry[];\n private head = 0;\n private count = 0;\n private sequence = 0;\n private startSequence = 0;\n private readonly capacity: number;\n\n constructor(capacity = LOG_RING_BUFFER_CAPACITY) {\n this.capacity = capacity;\n this.buffer = new Array(capacity);\n }\n\n push(entry: LogEntry): void {\n this.buffer[this.head] = entry;\n this.head = (this.head + 1) % this.capacity;\n if (this.count < this.capacity) {\n this.count++;\n } else {\n this.startSequence++;\n }\n this.sequence++;\n }\n\n since(cursor: string | null, options?: LogQueryOptions): LogQueryResult {\n const limit = options?.limit ?? LOG_QUERY_DEFAULT_LIMIT;\n const minLevel = options?.level ? LEVEL_ORDER[options.level] : 0;\n\n let startIdx = 0;\n let cursorReset = false;\n\n if (cursor !== null) {\n const seq = parseInt(cursor, 10);\n if (isNaN(seq) || seq < this.startSequence) {\n cursorReset = true;\n startIdx = 0;\n } else {\n startIdx = seq - this.startSequence;\n }\n } else {\n // No cursor: return last `limit` entries\n startIdx = Math.max(0, this.count - limit);\n }\n\n const entries: LogEntry[] = [];\n for (let i = startIdx; i < this.count && entries.length < limit; i++) {\n const bufIdx = (this.head - this.count + i + this.capacity) % this.capacity;\n const entry = this.buffer[bufIdx];\n if (entry && LEVEL_ORDER[entry.level as LogLevel] >= minLevel) {\n if (options?.component && entry.component !== options.component) continue;\n entries.push(entry);\n }\n }\n\n const result: LogQueryResult = {\n entries,\n cursor: String(this.sequence),\n };\n if (cursorReset) result.cursor_reset = true;\n return result;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  AgentRegistry
4
- } from "./chunk-RNWALAFP.js";
4
+ } from "./chunk-F7GAYVWF.js";
5
5
 
6
6
  // src/native-deps.ts
7
7
  import { execFileSync } from "child_process";
@@ -53,4 +53,4 @@ function ensureNativeDeps() {
53
53
  export {
54
54
  ensureNativeDeps
55
55
  };
56
- //# sourceMappingURL=chunk-RXJHB7W4.js.map
56
+ //# sourceMappingURL=chunk-OEGZ5YTJ.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  MycoConfigSchema,
7
7
  require_dist
8
- } from "./chunk-JSK7L46L.js";
8
+ } from "./chunk-ERG2IEWX.js";
9
9
  import {
10
10
  __toESM
11
11
  } from "./chunk-PZUWP5VK.js";
@@ -144,4 +144,4 @@ async function run(args, vaultDir) {
144
144
  export {
145
145
  run
146
146
  };
147
- //# sourceMappingURL=chunk-XNAM6Z4O.js.map
147
+ //# sourceMappingURL=chunk-P723N2LP.js.map