@goondocks/myco 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +9 -4
  4. package/commands/init.md +63 -39
  5. package/commands/setup-llm.md +69 -44
  6. package/commands/status.md +28 -10
  7. package/dist/{chunk-LAL7WK6I.js → chunk-2GJFTIWX.js} +2 -2
  8. package/dist/chunk-4FCFRJIQ.js +147 -0
  9. package/dist/chunk-4FCFRJIQ.js.map +1 -0
  10. package/dist/{chunk-PA3VMINE.js → chunk-AK6GNLPV.js} +6 -1
  11. package/dist/chunk-AK6GNLPV.js.map +1 -0
  12. package/dist/{chunk-ZCGGWNAA.js → chunk-BNIYWCST.js} +3 -3
  13. package/dist/chunk-BNIYWCST.js.map +1 -0
  14. package/dist/{chunk-ISCT2SI6.js → chunk-G6ZMTQMJ.js} +7357 -60
  15. package/dist/chunk-G6ZMTQMJ.js.map +1 -0
  16. package/dist/{chunk-7WNE22W7.js → chunk-IVS5MYBL.js} +3 -3
  17. package/dist/{chunk-7WNE22W7.js.map → chunk-IVS5MYBL.js.map} +1 -1
  18. package/dist/{chunk-7VPJK56U.js → chunk-JBD5KP5G.js} +31 -16
  19. package/dist/chunk-JBD5KP5G.js.map +1 -0
  20. package/dist/chunk-NUA7UTIY.js +37 -0
  21. package/dist/chunk-NUA7UTIY.js.map +1 -0
  22. package/dist/{chunk-5EGHGTN2.js → chunk-OUFSLZTX.js} +4 -4
  23. package/dist/chunk-P7RNAYU7.js +242 -0
  24. package/dist/chunk-P7RNAYU7.js.map +1 -0
  25. package/dist/chunk-QQ36XEJP.js +38 -0
  26. package/dist/chunk-QQ36XEJP.js.map +1 -0
  27. package/dist/chunk-RDXTQ436.js +49 -0
  28. package/dist/chunk-RDXTQ436.js.map +1 -0
  29. package/dist/{chunk-AWF3M57N.js → chunk-S7EIHYE7.js} +8 -8
  30. package/dist/{chunk-AWF3M57N.js.map → chunk-S7EIHYE7.js.map} +1 -1
  31. package/dist/{chunk-QWU7QLZI.js → chunk-TZDDXRHG.js} +10 -10
  32. package/dist/chunk-TZDDXRHG.js.map +1 -0
  33. package/dist/chunk-VYV5IFD6.js +99 -0
  34. package/dist/chunk-VYV5IFD6.js.map +1 -0
  35. package/dist/{chunk-F6AG6YXJ.js → chunk-XCPQHC4X.js} +2 -2
  36. package/dist/{chunk-5O52JLGT.js → chunk-XHWIIU5D.js} +8 -9
  37. package/dist/chunk-XHWIIU5D.js.map +1 -0
  38. package/dist/{chunk-2YQGS5CK.js → chunk-YZO22BBI.js} +45 -31
  39. package/dist/chunk-YZO22BBI.js.map +1 -0
  40. package/dist/{chunk-ZBNT6E22.js → chunk-ZCBL5HER.js} +2 -2
  41. package/dist/{cli-DNYLL2JD.js → cli-ZN6VBA7V.js} +23 -17
  42. package/dist/cli-ZN6VBA7V.js.map +1 -0
  43. package/dist/{client-XBD4NCDO.js → client-5SUO2UYH.js} +5 -5
  44. package/dist/{config-MD4XMLUS.js → config-4GGMWGAF.js} +4 -4
  45. package/dist/{detect-providers-LNOLBICR.js → detect-providers-5FU3BN5Q.js} +3 -3
  46. package/dist/{init-57K46O7U.js → init-7UXGDOFS.js} +51 -60
  47. package/dist/init-7UXGDOFS.js.map +1 -0
  48. package/dist/{main-FOZYS5B7.js → main-6UPAIDGS.js} +648 -228
  49. package/dist/main-6UPAIDGS.js.map +1 -0
  50. package/dist/{rebuild-JW6BCHHZ.js → rebuild-QDSYYCS7.js} +10 -10
  51. package/dist/rebuild-QDSYYCS7.js.map +1 -0
  52. package/dist/{reprocess-5YK7ZOFW.js → reprocess-ZNUQCIS3.js} +18 -18
  53. package/dist/reprocess-ZNUQCIS3.js.map +1 -0
  54. package/dist/{restart-GZE73CZ7.js → restart-5UY2KV54.js} +6 -6
  55. package/dist/{search-2HMG3ON7.js → search-2VEN3XIG.js} +9 -9
  56. package/dist/{server-KKVTFBSN.js → server-OR5B4B7K.js} +77 -54
  57. package/dist/{server-KKVTFBSN.js.map → server-OR5B4B7K.js.map} +1 -1
  58. package/dist/{session-5GI2YU6R.js → session-QF6MILAC.js} +2 -2
  59. package/dist/{session-start-5KXWKS3B.js → session-start-TUITIUMB.js} +29 -28
  60. package/dist/session-start-TUITIUMB.js.map +1 -0
  61. package/dist/setup-digest-ETCZAUIU.js +15 -0
  62. package/dist/setup-llm-DWEJE3JE.js +15 -0
  63. package/dist/setup-llm-DWEJE3JE.js.map +1 -0
  64. package/dist/src/cli.js +4 -4
  65. package/dist/src/daemon/main.js +4 -4
  66. package/dist/src/hooks/post-tool-use.js +5 -5
  67. package/dist/src/hooks/session-end.js +5 -5
  68. package/dist/src/hooks/session-start.js +4 -4
  69. package/dist/src/hooks/stop.js +7 -7
  70. package/dist/src/hooks/user-prompt-submit.js +5 -5
  71. package/dist/src/hooks/user-prompt-submit.js.map +1 -1
  72. package/dist/src/mcp/server.js +4 -4
  73. package/dist/src/prompts/classification.md +1 -0
  74. package/dist/src/prompts/digest-10000.md +74 -0
  75. package/dist/src/prompts/digest-1500.md +25 -0
  76. package/dist/src/prompts/digest-3000.md +32 -0
  77. package/dist/src/prompts/digest-5000.md +43 -0
  78. package/dist/src/prompts/digest-system.md +32 -0
  79. package/dist/src/prompts/extraction.md +11 -10
  80. package/dist/src/prompts/summary.md +11 -1
  81. package/dist/src/prompts/title.md +1 -1
  82. package/dist/{stats-R5KAGBQE.js → stats-IVIXIKTS.js} +12 -12
  83. package/dist/stats-IVIXIKTS.js.map +1 -0
  84. package/dist/{verify-7MWOV72E.js → verify-4H6CEE5T.js} +6 -6
  85. package/dist/{version-DLKARUP4.js → version-5B2TWXQJ.js} +4 -4
  86. package/dist/version-5B2TWXQJ.js.map +1 -0
  87. package/package.json +1 -1
  88. package/skills/myco/SKILL.md +20 -20
  89. package/skills/myco/references/wisdom.md +14 -14
  90. package/skills/rules/SKILL.md +4 -4
  91. package/dist/chunk-2YQGS5CK.js.map +0 -1
  92. package/dist/chunk-5O52JLGT.js.map +0 -1
  93. package/dist/chunk-7VPJK56U.js.map +0 -1
  94. package/dist/chunk-BA23DROX.js +0 -160
  95. package/dist/chunk-BA23DROX.js.map +0 -1
  96. package/dist/chunk-EF4JVH24.js +0 -7299
  97. package/dist/chunk-EF4JVH24.js.map +0 -1
  98. package/dist/chunk-ISCT2SI6.js.map +0 -1
  99. package/dist/chunk-PA3VMINE.js.map +0 -1
  100. package/dist/chunk-QWU7QLZI.js.map +0 -1
  101. package/dist/chunk-YMYJ7FNH.js +0 -19
  102. package/dist/chunk-YMYJ7FNH.js.map +0 -1
  103. package/dist/chunk-ZCGGWNAA.js.map +0 -1
  104. package/dist/cli-DNYLL2JD.js.map +0 -1
  105. package/dist/init-57K46O7U.js.map +0 -1
  106. package/dist/main-FOZYS5B7.js.map +0 -1
  107. package/dist/rebuild-JW6BCHHZ.js.map +0 -1
  108. package/dist/reprocess-5YK7ZOFW.js.map +0 -1
  109. package/dist/session-start-5KXWKS3B.js.map +0 -1
  110. package/dist/stats-R5KAGBQE.js.map +0 -1
  111. /package/dist/{chunk-LAL7WK6I.js.map → chunk-2GJFTIWX.js.map} +0 -0
  112. /package/dist/{chunk-5EGHGTN2.js.map → chunk-OUFSLZTX.js.map} +0 -0
  113. /package/dist/{chunk-F6AG6YXJ.js.map → chunk-XCPQHC4X.js.map} +0 -0
  114. /package/dist/{chunk-ZBNT6E22.js.map → chunk-ZCBL5HER.js.map} +0 -0
  115. /package/dist/{client-XBD4NCDO.js.map → client-5SUO2UYH.js.map} +0 -0
  116. /package/dist/{config-MD4XMLUS.js.map → config-4GGMWGAF.js.map} +0 -0
  117. /package/dist/{detect-providers-LNOLBICR.js.map → detect-providers-5FU3BN5Q.js.map} +0 -0
  118. /package/dist/{restart-GZE73CZ7.js.map → restart-5UY2KV54.js.map} +0 -0
  119. /package/dist/{search-2HMG3ON7.js.map → search-2VEN3XIG.js.map} +0 -0
  120. /package/dist/{session-5GI2YU6R.js.map → session-QF6MILAC.js.map} +0 -0
  121. /package/dist/{version-DLKARUP4.js.map → setup-digest-ETCZAUIU.js.map} +0 -0
  122. /package/dist/{verify-7MWOV72E.js.map → verify-4H6CEE5T.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index/sqlite.ts"],"sourcesContent":["import Database from 'better-sqlite3';\n\nexport interface IndexedNote {\n path: string;\n type: string;\n id: string;\n title: string;\n content: string;\n frontmatter: Record<string, unknown>;\n created: string;\n updated_at?: string;\n}\n\nexport interface QueryOptions {\n type?: string;\n id?: string;\n limit?: number;\n since?: string;\n /** Filter by frontmatter fields using json_extract. Applied before LIMIT. */\n frontmatter?: Record<string, string>;\n}\n\nexport class MycoIndex {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.init();\n }\n\n private init(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS notes (\n path TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n id TEXT NOT NULL,\n title TEXT NOT NULL DEFAULT '',\n content TEXT NOT NULL DEFAULT '',\n frontmatter TEXT NOT NULL DEFAULT '{}',\n created TEXT NOT NULL,\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_notes_type ON notes(type);\n CREATE INDEX IF NOT EXISTS idx_notes_id ON notes(id);\n CREATE INDEX IF NOT EXISTS idx_notes_created ON notes(created);\n `);\n }\n\n getPragma(name: string): unknown {\n return this.db.pragma(`${name}`, { simple: true });\n }\n\n getDb(): Database.Database {\n return this.db;\n }\n\n upsertNote(note: Omit<IndexedNote, 'updated_at'>): void {\n const stmt = this.db.prepare(`\n INSERT INTO notes (path, type, id, title, content, frontmatter, created, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))\n ON CONFLICT(path) DO UPDATE SET\n type = excluded.type,\n id = excluded.id,\n title = excluded.title,\n content = excluded.content,\n frontmatter = excluded.frontmatter,\n created = excluded.created,\n updated_at = datetime('now')\n `);\n stmt.run(\n note.path,\n note.type,\n note.id,\n note.title,\n note.content,\n JSON.stringify(note.frontmatter),\n note.created,\n );\n }\n\n getNoteByPath(notePath: string): IndexedNote | null {\n const row = this.db.prepare('SELECT * FROM notes WHERE path = ?').get(notePath) as any;\n if (!row) return null;\n return { ...row, frontmatter: JSON.parse(row.frontmatter) };\n }\n\n deleteNote(notePath: string): void {\n this.db.prepare('DELETE FROM notes WHERE path = ?').run(notePath);\n }\n\n query(options: QueryOptions): IndexedNote[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.type) {\n conditions.push('type = ?');\n params.push(options.type);\n }\n if (options.id) {\n conditions.push('id = ?');\n params.push(options.id);\n }\n if (options.since) {\n conditions.push('created >= ?');\n params.push(options.since);\n }\n if (options.frontmatter) {\n for (const [key, value] of Object.entries(options.frontmatter)) {\n conditions.push(`json_extract(frontmatter, '$.' || ?) = ?`);\n params.push(key, value);\n }\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limitClause = options.limit ? 'LIMIT ?' : '';\n if (options.limit) params.push(options.limit);\n const sql = `SELECT * FROM notes ${where} ORDER BY created DESC ${limitClause}`;\n\n const rows = this.db.prepare(sql).all(...params) as any[];\n return rows.map((row) => ({ ...row, frontmatter: JSON.parse(row.frontmatter) }));\n }\n\n queryByIds(ids: string[]): IndexedNote[] {\n if (ids.length === 0) return [];\n const placeholders = ids.map(() => '?').join(',');\n const sql = `SELECT * FROM notes WHERE id IN (${placeholders})`;\n const rows = this.db.prepare(sql).all(...ids) as any[];\n return rows.map((row) => ({ ...row, frontmatter: JSON.parse(row.frontmatter) }));\n }\n\n close(): void {\n this.db.close();\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/vault/session-id.ts","../src/obsidian/formatter.ts","../src/vault/writer.ts"],"sourcesContent":["/**\n * Session ID convention utilities.\n *\n * The runtime session ID is a bare UUID (e.g., \"abc123\").\n * The vault stores it with a \"session-\" prefix (e.g., \"session-abc123\").\n * These helpers convert between the two forms.\n */\nconst SESSION_PREFIX = 'session-';\n\n/** Convert a bare session ID to a vault note ID: \"abc123\" → \"session-abc123\" */\nexport function sessionNoteId(bareId: string): string {\n if (bareId.startsWith(SESSION_PREFIX)) return bareId;\n return `${SESSION_PREFIX}${bareId}`;\n}\n\n/** Convert a vault note ID to a bare session ID: \"session-abc123\" → \"abc123\" */\nexport function bareSessionId(noteId: string): string {\n if (noteId.startsWith(SESSION_PREFIX)) return noteId.slice(SESSION_PREFIX.length);\n return noteId;\n}\n\n/** Convert a bare session ID to an Obsidian wikilink: \"abc123\" → \"[[session-abc123]]\" */\nexport function sessionWikilink(bareId: string): string {\n return `[[${sessionNoteId(bareId)}]]`;\n}\n\n/** Build the relative vault path for a session note */\nexport function sessionRelativePath(bareId: string, date: string): string {\n return `sessions/${date}/${sessionNoteId(bareId)}.md`;\n}\n\n","/**\n * Pure formatting functions for Obsidian-native vault notes.\n * No I/O, no external dependencies — just string transforms.\n */\nimport type { ArtifactType } from '../vault/types.js';\nimport { sessionNoteId } from '../vault/session-id.js';\n\n// Callout type mapping for observation types\nconst CALLOUT_MAP: Record<string, string> = {\n gotcha: 'warning',\n bug_fix: 'bug',\n decision: 'info',\n discovery: 'tip',\n trade_off: 'question',\n};\n\nexport function observationCalloutType(observationType: string): string {\n return CALLOUT_MAP[observationType] ?? 'note';\n}\n\n/**\n * Escape angle brackets that Obsidian would interpret as HTML tags.\n * Matches `<` followed by a letter, `/`, or `!` (opening/closing tags, comments).\n */\nexport function escapeHtmlTags(text: string): string {\n return text.replace(/<(?=[a-zA-Z/!])/g, '\\\\<');\n}\n\nexport function callout(type: string, title: string, content: string): string {\n const safe = escapeHtmlTags(content);\n const indented = safe.split('\\n').map((line) => `> ${line}`).join('\\n');\n return `> [!${type}] ${title}\\n${indented}`;\n}\n\nexport function inlineField(key: string, value: string): string {\n return `${key}:: ${value}`;\n}\n\nexport function wikilink(target: string, display?: string): string {\n return display ? `[[${target}|${display}]]` : `[[${target}]]`;\n}\n\n/**\n * Normalize an observation_type to a tag-safe form.\n * Frontmatter keeps underscores; tags use hyphens per Obsidian convention.\n */\nfunction tagNormalize(s: string): string {\n return s.replace(/_/g, '-');\n}\n\n/**\n * Sanitize a user/LLM-provided tag for Obsidian compatibility.\n * Obsidian tags cannot contain spaces — replace with slash (nested tag).\n * Strips leading # if present.\n */\nfunction sanitizeTag(raw: string): string {\n const stripped = raw.startsWith('#') ? raw.slice(1) : raw;\n return stripped.replace(/\\s+/g, '/');\n}\n\nexport function buildTags(type: string, subtype: string, extraTags: string[] = []): string[] {\n const tags: string[] = [`type/${type}`];\n\n if (subtype) {\n tags.push(`${type}/${tagNormalize(subtype)}`);\n }\n\n for (const tag of extraTags) {\n const normalized = sanitizeTag(tag);\n if (normalized && !tags.includes(normalized)) {\n tags.push(normalized);\n }\n }\n\n return tags;\n}\n\nexport function footerTags(tags: string[]): string {\n return tags.map((t) => (t.startsWith('#') ? t : `#${t}`)).join(' ');\n}\n\n// --- Session formatting ---\n\nexport interface SessionBodyInput {\n title: string;\n narrative: string;\n sessionId: string;\n user?: string;\n started?: string;\n ended?: string;\n branch?: string;\n relatedMemories?: Array<{ id: string; title: string }>;\n turns: Array<{\n prompt: string;\n toolCount: number;\n aiResponse?: string;\n /** Filenames of images in the vault attachments folder */\n images?: string[];\n }>;\n tags?: string[];\n}\n\nexport function formatSessionBody(input: SessionBodyInput): string {\n const sections: string[] = [];\n\n sections.push(`# ${input.title}`);\n\n if (input.narrative) {\n sections.push(callout('abstract', 'Summary', input.narrative));\n }\n\n // Inline fields\n const fields: string[] = [];\n fields.push(inlineField('Session', wikilink(sessionNoteId(input.sessionId))));\n if (input.user) fields.push(inlineField('User', input.user));\n if (input.started && input.ended) {\n const duration = formatDuration(input.started, input.ended);\n if (duration) fields.push(inlineField('Duration', duration));\n }\n if (input.branch) fields.push(inlineField('Branch', `\\`${input.branch}\\``));\n sections.push(fields.join('\\n'));\n\n // Related memories\n if (input.relatedMemories?.length) {\n const links = input.relatedMemories.map((m) => `- ${wikilink(m.id, m.title)}`);\n sections.push(`## Related Memories\\n${links.join('\\n')}`);\n }\n\n // Conversation turns — always rebuilt from the full transcript.\n // The transcript is the source of truth for the complete conversation.\n if (input.turns.length > 0) {\n const turnLines: string[] = [];\n for (let i = 0; i < input.turns.length; i++) {\n const turn = input.turns[i];\n const turnNum = i + 1;\n turnLines.push(`### Turn ${turnNum}`);\n if (turn.prompt || turn.images?.length) {\n // Build prompt content: text + images + tool count\n const parts: string[] = [];\n if (turn.prompt) parts.push(turn.prompt);\n if (turn.images?.length) {\n parts.push(turn.images.map((f) => `![[${f}]]`).join('\\n'));\n }\n if (turn.toolCount > 0) parts.push(`*${turn.toolCount} tool calls*`);\n turnLines.push(callout('user', 'Prompt', parts.join('\\n\\n')));\n } else if (turn.toolCount > 0) {\n turnLines.push(callout('user', 'Prompt', `*${turn.toolCount} tool calls*`));\n }\n if (turn.aiResponse) {\n turnLines.push(callout('assistant', 'Response', turn.aiResponse));\n }\n }\n sections.push(`## Conversation\\n\\n${turnLines.join('\\n\\n')}`);\n }\n\n // Footer tags\n const allTags = buildTags('session', 'ended', [\n ...(input.user ? [`user/${input.user}`] : []),\n ...(input.tags ?? []),\n ]);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Memory formatting ---\n\nexport interface MemoryBodyInput {\n title: string;\n observationType: string;\n content: string;\n sessionId?: string;\n root_cause?: string;\n fix?: string;\n rationale?: string;\n alternatives_rejected?: string;\n gained?: string;\n sacrificed?: string;\n tags?: string[];\n}\n\nexport function formatMemoryBody(input: MemoryBodyInput): string {\n const sections: string[] = [];\n const calloutType = observationCalloutType(input.observationType);\n const calloutTitle = capitalize(tagNormalize(input.observationType));\n\n sections.push(`# ${input.title}`);\n sections.push(callout(calloutType, calloutTitle, input.content));\n\n // Inline fields\n const fields: string[] = [];\n if (input.sessionId) {\n fields.push(inlineField('Session', wikilink(sessionNoteId(input.sessionId))));\n }\n fields.push(inlineField('Observation', input.observationType));\n if (fields.length > 0) sections.push(fields.join('\\n'));\n\n // Type-specific sub-sections\n if (input.root_cause) sections.push(`## Root Cause\\n${input.root_cause}`);\n if (input.fix) sections.push(`## Fix\\n${input.fix}`);\n if (input.rationale) sections.push(`## Rationale\\n${input.rationale}`);\n if (input.alternatives_rejected) sections.push(`## Alternatives Rejected\\n${input.alternatives_rejected}`);\n if (input.gained) sections.push(`## Gained\\n${input.gained}`);\n if (input.sacrificed) sections.push(`## Sacrificed\\n${input.sacrificed}`);\n\n // Footer tags\n const allTags = buildTags('memory', input.observationType, input.tags ?? []);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Plan formatting ---\n\nexport interface PlanBodyInput {\n id: string;\n status: string;\n author?: string;\n created?: string;\n sessions?: Array<{ id: string; title: string }>;\n content: string;\n tags?: string[];\n}\n\nexport function formatPlanBody(input: PlanBodyInput): string {\n const sections: string[] = [];\n\n // Inline fields block\n const fields: string[] = [];\n fields.push(inlineField('Plan', wikilink(input.id)));\n fields.push(inlineField('Status', input.status));\n if (input.author) fields.push(inlineField('Author', input.author));\n if (input.created) fields.push(inlineField('Created', input.created));\n sections.push(fields.join('\\n'));\n\n // User-provided content body (don't restructure)\n sections.push(input.content);\n\n // Sessions section\n if (input.sessions?.length) {\n const links = input.sessions.map((s) => `- ${wikilink(sessionNoteId(s.id), s.title)}`);\n sections.push(`## Sessions\\n${links.join('\\n')}`);\n }\n\n // Footer tags\n const statusTag = tagNormalize(input.status);\n const allTags = buildTags('plan', statusTag, input.tags ?? []);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Artifact formatting ---\n\nexport interface ArtifactBodyInput {\n id: string;\n title: string;\n artifact_type: ArtifactType;\n source_path: string;\n sessionId: string;\n content: string;\n tags?: string[];\n}\n\nexport function formatArtifactBody(input: ArtifactBodyInput): string {\n const sections: string[] = [];\n\n // Inline fields\n const fields: string[] = [];\n fields.push(inlineField('Artifact', wikilink(input.id)));\n fields.push(inlineField('Source', `\\`${input.source_path}\\``));\n fields.push(inlineField('Type', input.artifact_type));\n fields.push(inlineField('Session', wikilink(sessionNoteId(input.sessionId))));\n sections.push(fields.join('\\n'));\n\n // Body: full content from disk\n sections.push(input.content);\n\n // Footer tags\n const allTags = buildTags('artifact', input.artifact_type, input.tags ?? []);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Team formatting ---\n\nexport interface TeamBodyInput {\n user: string;\n role?: string;\n recentSessions?: Array<{ id: string; title: string }>;\n}\n\nexport function formatTeamBody(input: TeamBodyInput): string {\n const sections: string[] = [];\n\n sections.push(`# ${input.user}`);\n sections.push(callout('info', 'Team Member', input.role ?? 'Contributor'));\n\n // Inline fields\n const fields: string[] = [];\n fields.push(inlineField('User', input.user));\n if (input.role) fields.push(inlineField('Role', input.role));\n sections.push(fields.join('\\n'));\n\n // Recent sessions\n if (input.recentSessions?.length) {\n const links = input.recentSessions.map((s) => `- ${wikilink(sessionNoteId(s.id), s.title)}`);\n sections.push(`## Recent Sessions\\n${links.join('\\n')}`);\n }\n\n // Footer tags\n const allTags = buildTags('team', '', [`user/${input.user}`]);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Helpers ---\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction formatDuration(started: string, ended: string): string {\n const ms = new Date(ended).getTime() - new Date(started).getTime();\n if (isNaN(ms) || ms < 0) return '';\n const totalMinutes = Math.floor(ms / 60000);\n if (totalMinutes < 1) return '<1m';\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (hours === 0) return `${minutes}m`;\n if (minutes === 0) return `${hours}h`;\n return `${hours}h ${minutes}m`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport { buildTags, formatTeamBody, formatPlanBody, formatArtifactBody } from '../obsidian/formatter.js';\nimport type { ArtifactType } from './types.js';\nimport { sessionNoteId, sessionRelativePath } from './session-id.js';\n\ninterface WriteSessionInput {\n id: string;\n agent?: string;\n user?: string;\n started: string;\n ended?: string;\n parent?: string;\n parent_reason?: string;\n plans?: string[];\n branch?: string;\n tags?: string[];\n tools_used?: number;\n files_changed?: number;\n summary: string;\n}\n\ninterface WritePlanInput {\n id: string;\n status?: string;\n author?: string;\n tags?: string[];\n content: string;\n}\n\ninterface WriteMemoryInput {\n id: string;\n observation_type: string;\n session?: string;\n plan?: string;\n tags?: string[];\n content: string;\n}\n\ninterface WriteArtifactInput {\n id: string;\n artifact_type: ArtifactType;\n source_path: string;\n title: string;\n session: string;\n tags?: string[];\n content: string;\n}\n\ninterface WriteTeamMemberInput {\n user: string;\n role?: string;\n}\n\nexport class VaultWriter {\n constructor(private vaultDir: string) {}\n\n writeSession(input: WriteSessionInput): string {\n const date = input.started.slice(0, 10);\n const relativePath = sessionRelativePath(input.id, date);\n\n const frontmatter: Record<string, unknown> = {\n type: 'session',\n id: input.id,\n agent: input.agent ?? 'claude-code',\n user: input.user ?? '',\n started: input.started,\n };\n if (input.ended) frontmatter.ended = input.ended;\n if (input.parent) frontmatter.parent = input.parent;\n if (input.parent_reason) frontmatter.parent_reason = input.parent_reason;\n if (input.plans?.length) frontmatter.plans = input.plans;\n if (input.branch) frontmatter.branch = input.branch;\n frontmatter.tags = buildTags('session', 'ended', [\n ...(input.user ? [`user/${input.user}`] : []),\n ...(input.tags ?? []),\n ]);\n if (input.tools_used != null) frontmatter.tools_used = input.tools_used;\n if (input.files_changed != null) frontmatter.files_changed = input.files_changed;\n\n this.writeMarkdown(relativePath, frontmatter, input.summary);\n return relativePath;\n }\n\n writePlan(input: WritePlanInput): string {\n const relativePath = `plans/${input.id}.md`;\n const fullPath = path.join(this.vaultDir, relativePath);\n\n const status = input.status ?? 'active';\n let created = new Date().toISOString();\n\n // Preserve created from existing file (idempotent)\n try {\n const existing = fs.readFileSync(fullPath, 'utf-8');\n const fmMatch = existing.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (fmMatch) {\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n if (typeof parsed.created === 'string') created = parsed.created;\n }\n } catch {\n // File doesn't exist yet\n }\n const frontmatter: Record<string, unknown> = {\n type: 'plan',\n id: input.id,\n status,\n created,\n };\n if (input.author) frontmatter.author = input.author;\n frontmatter.tags = buildTags('plan', status, input.tags ?? []);\n\n const body = formatPlanBody({\n id: input.id,\n status,\n author: input.author,\n created,\n content: input.content,\n tags: input.tags,\n });\n\n this.writeMarkdown(relativePath, frontmatter, body);\n return relativePath;\n }\n\n writeMemory(input: WriteMemoryInput): string {\n const normalizedType = input.observation_type.replace(/_/g, '-');\n const relativePath = `memories/${normalizedType}/${input.id}.md`;\n const fullPath = path.join(this.vaultDir, relativePath);\n const now = new Date().toISOString();\n\n // Preserve created from existing file (idempotent)\n let created = now;\n try {\n const existing = fs.readFileSync(fullPath, 'utf-8');\n const fmMatch = existing.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (fmMatch) {\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n if (typeof parsed.created === 'string') created = parsed.created;\n }\n } catch {\n // File doesn't exist yet — created = now\n }\n\n const frontmatter: Record<string, unknown> = {\n type: 'memory',\n id: input.id,\n observation_type: input.observation_type,\n created,\n };\n if (input.session) frontmatter.session = input.session;\n if (input.plan) frontmatter.plan = input.plan;\n frontmatter.tags = buildTags('memory', input.observation_type, input.tags ?? []);\n\n this.writeMarkdown(relativePath, frontmatter, input.content);\n return relativePath;\n }\n\n writeArtifact(input: WriteArtifactInput): string {\n const relativePath = `artifacts/${input.id}.md`;\n const fullPath = path.join(this.vaultDir, relativePath);\n const now = new Date().toISOString();\n\n let created = now;\n\n // Preserve created from existing file (latest-wins update)\n try {\n const existing = fs.readFileSync(fullPath, 'utf-8');\n const fmMatch = existing.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (fmMatch) {\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n if (typeof parsed.created === 'string') created = parsed.created;\n }\n } catch {\n // File doesn't exist yet — created = now\n }\n\n const frontmatter: Record<string, unknown> = {\n type: 'artifact',\n id: input.id,\n artifact_type: input.artifact_type,\n source_path: input.source_path,\n title: input.title,\n last_captured_by: sessionNoteId(input.session),\n created,\n updated: now,\n tags: buildTags('artifact', input.artifact_type, input.tags ?? []),\n };\n\n const body = formatArtifactBody({\n id: input.id,\n title: input.title,\n artifact_type: input.artifact_type,\n source_path: input.source_path,\n sessionId: input.session,\n content: input.content,\n tags: input.tags,\n });\n\n this.writeMarkdown(relativePath, frontmatter, body);\n return relativePath;\n }\n\n writeTeamMember(input: WriteTeamMemberInput): string {\n const relativePath = `team/${input.user}.md`;\n\n const frontmatter: Record<string, unknown> = {\n type: 'team-member',\n user: input.user,\n joined: new Date().toISOString(),\n tags: buildTags('team', '', [`user/${input.user}`]),\n };\n if (input.role) frontmatter.role = input.role;\n\n const body = formatTeamBody({\n user: input.user,\n role: input.role,\n });\n\n this.writeMarkdown(relativePath, frontmatter, body);\n return relativePath;\n }\n\n /**\n * Update frontmatter fields on an existing note without touching the body.\n * By default only adds fields that don't exist. Set overwrite=true to replace existing values.\n * Returns true if the update was applied, false if the file doesn't exist.\n */\n updateNoteFrontmatter(relativePath: string, fields: Record<string, unknown>, overwrite = false): boolean {\n const fullPath = path.join(this.vaultDir, relativePath);\n let fileContent: string;\n try {\n fileContent = fs.readFileSync(fullPath, 'utf-8');\n } catch {\n return false;\n }\n\n const fmMatch = fileContent.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!fmMatch) return false;\n\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n for (const [key, value] of Object.entries(fields)) {\n if (overwrite || parsed[key] === undefined) {\n parsed[key] = value;\n }\n }\n\n const body = fileContent.slice(fmMatch[0].length);\n const fmYaml = YAML.stringify(parsed, { defaultStringType: 'QUOTE_DOUBLE', defaultKeyType: 'PLAIN' }).trim();\n this.atomicWrite(fullPath, `---\\n${fmYaml}\\n---${body}`);\n return true;\n }\n\n /** @deprecated Use updateNoteFrontmatter instead */\n updateSessionFrontmatter(relativePath: string, fields: Record<string, unknown>): boolean {\n return this.updateNoteFrontmatter(relativePath, fields);\n }\n\n private writeMarkdown(relativePath: string, frontmatter: Record<string, unknown>, content: string): void {\n const fullPath = path.join(this.vaultDir, relativePath);\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n\n const fmYaml = YAML.stringify(frontmatter, { defaultStringType: 'QUOTE_DOUBLE', defaultKeyType: 'PLAIN' }).trim();\n const file = `---\\n${fmYaml}\\n---\\n\\n${content}\\n`;\n this.atomicWrite(fullPath, file);\n }\n\n /** Write to a temp file then rename — prevents Obsidian from seeing a truncated file mid-write. */\n private atomicWrite(fullPath: string, content: string): void {\n const tmp = `${fullPath}.tmp`;\n fs.writeFileSync(tmp, content, 'utf-8');\n fs.renameSync(tmp, fullPath);\n }\n}\n"],"mappings":"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","names":["YAML"]}
@@ -1,19 +0,0 @@
1
- import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
-
3
- // src/vault/frontmatter.ts
4
- function planFm(note) {
5
- return note.frontmatter;
6
- }
7
- function sessionFm(note) {
8
- return note.frontmatter;
9
- }
10
- function memoryFm(note) {
11
- return note.frontmatter;
12
- }
13
-
14
- export {
15
- planFm,
16
- sessionFm,
17
- memoryFm
18
- };
19
- //# sourceMappingURL=chunk-YMYJ7FNH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/vault/frontmatter.ts"],"sourcesContent":["import type { IndexedNote } from '../index/sqlite.js';\nimport type { PlanFrontmatter, SessionFrontmatter, MemoryFrontmatter } from './types.js';\n\nexport function planFm(note: IndexedNote): PlanFrontmatter {\n return note.frontmatter as unknown as PlanFrontmatter;\n}\n\nexport function sessionFm(note: IndexedNote): SessionFrontmatter {\n return note.frontmatter as unknown as SessionFrontmatter;\n}\n\nexport function memoryFm(note: IndexedNote): MemoryFrontmatter {\n return note.frontmatter as unknown as MemoryFrontmatter;\n}\n"],"mappings":";;;AAGO,SAAS,OAAO,MAAoC;AACzD,SAAO,KAAK;AACd;AAEO,SAAS,UAAU,MAAuC;AAC/D,SAAO,KAAK;AACd;AAEO,SAAS,SAAS,MAAsC;AAC7D,SAAO,KAAK;AACd;","names":[]}
@@ -1 +0,0 @@
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 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.user.json (gitignored) — vault paths contain the\n // user's home directory and should not be committed to the repository.\n const settingsPath = path.join(settingsDir, 'settings.user.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 +0,0 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { loadEnv } from './cli/shared.js';\nimport { resolveVaultDir } from './vault/resolve.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nloadEnv();\n\nconst USAGE = `Usage: myco <command> [args]\n\nCommands:\n init [options] Initialize a new vault\n config <get|set> [args] Get or set vault config values\n detect-providers Detect available LLM/embedding providers (JSON)\n verify Test LLM and embedding connectivity\n stats Vault health, index counts, vector count\n search <query> Combined FTS + vector search with scores\n vectors <query> Raw vector search with similarity scores\n session [id|latest] Show a session note\n logs [options] View daemon logs\n restart Restart the daemon\n rebuild Reindex the entire vault\n reprocess [options] Re-extract observations and re-index sessions\n version Show plugin version\n`;\n\nasync function main(): Promise<void> {\n const [cmd, ...args] = process.argv.slice(2);\n if (!cmd || cmd === '--help' || cmd === '-h') {\n process.stdout.write(USAGE);\n return;\n }\n\n if (cmd === 'init') return (await import('./cli/init.js')).run(args);\n if (cmd === 'detect-providers') return (await import('./cli/detect-providers.js')).run(args);\n if (cmd === 'version' || cmd === '--version' || cmd === '-v') {\n const { getPluginVersion } = await import('./version.js');\n console.log(getPluginVersion());\n return;\n }\n\n const vaultDir = resolveVaultDir();\n if (!fs.existsSync(path.join(vaultDir, 'myco.yaml'))) {\n console.error(`No myco.yaml found in ${vaultDir}. Run 'myco init' first.`);\n process.exit(1);\n }\n\n switch (cmd) {\n case 'config': return (await import('./cli/config.js')).run(args, vaultDir);\n case 'verify': return (await import('./cli/verify.js')).run(args, vaultDir);\n case 'stats': return (await import('./cli/stats.js')).run(args, vaultDir);\n case 'search': return (await import('./cli/search.js')).run(args, vaultDir);\n case 'vectors': return (await import('./cli/search.js')).runVectors(args, vaultDir);\n case 'session': return (await import('./cli/session.js')).run(args, vaultDir);\n case 'restart': return (await import('./cli/restart.js')).run(args, vaultDir);\n case 'rebuild': return (await import('./cli/rebuild.js')).run(args, vaultDir);\n case 'reprocess': return (await import('./cli/reprocess.js')).run(args, vaultDir);\n case 'logs': return (await import('./cli/logs.js')).run(args, vaultDir);\n default:\n console.error(`Unknown command: ${cmd}`);\n process.stdout.write(USAGE);\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error(`myco: ${(err as Error).message}`);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;AAGA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,QAAQ;AAER,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBd,eAAe,OAAsB;AACnC,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC3C,MAAI,CAAC,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC5C,YAAQ,OAAO,MAAM,KAAK;AAC1B;AAAA,EACF;AAEA,MAAI,QAAQ,OAAQ,SAAQ,MAAM,OAAO,oBAAe,GAAG,IAAI,IAAI;AACnE,MAAI,QAAQ,mBAAoB,SAAQ,MAAM,OAAO,gCAA2B,GAAG,IAAI,IAAI;AAC3F,MAAI,QAAQ,aAAa,QAAQ,eAAe,QAAQ,MAAM;AAC5D,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,uBAAc;AACxD,YAAQ,IAAI,iBAAiB,CAAC;AAC9B;AAAA,EACF;AAEA,QAAM,WAAW,gBAAgB;AACjC,MAAI,CAAC,GAAG,WAAW,KAAK,KAAK,UAAU,WAAW,CAAC,GAAG;AACpD,YAAQ,MAAM,yBAAyB,QAAQ,0BAA0B;AACzE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,KAAK;AAAA,IACX,KAAK;AAAU,cAAQ,MAAM,OAAO,sBAAiB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC1E,KAAK;AAAU,cAAQ,MAAM,OAAO,sBAAiB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC1E,KAAK;AAAS,cAAQ,MAAM,OAAO,qBAAgB,GAAG,IAAI,MAAM,QAAQ;AAAA,IACxE,KAAK;AAAU,cAAQ,MAAM,OAAO,sBAAiB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC1E,KAAK;AAAW,cAAQ,MAAM,OAAO,sBAAiB,GAAG,WAAW,MAAM,QAAQ;AAAA,IAClF,KAAK;AAAW,cAAQ,MAAM,OAAO,uBAAkB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC5E,KAAK;AAAW,cAAQ,MAAM,OAAO,uBAAkB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC5E,KAAK;AAAW,cAAQ,MAAM,OAAO,uBAAkB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC5E,KAAK;AAAa,cAAQ,MAAM,OAAO,yBAAoB,GAAG,IAAI,MAAM,QAAQ;AAAA,IAChF,KAAK;AAAQ,cAAQ,MAAM,OAAO,oBAAe,GAAG,IAAI,MAAM,QAAQ;AAAA,IACtE;AACE,cAAQ,MAAM,oBAAoB,GAAG,EAAE;AACvC,cAAQ,OAAO,MAAM,KAAK;AAC1B,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,SAAU,IAAc,OAAO,EAAE;AAC/C,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/init.ts"],"sourcesContent":["import { MycoIndex } from '../index/sqlite.js';\nimport { initFts } from '../index/fts.js';\nimport { resolveVaultDir } from '../vault/resolve.js';\nimport {\n parseStringFlag,\n PROVIDER_DEFAULTS,\n DASHBOARD_CONTENT,\n VAULT_GITIGNORE,\n configureVaultEnv,\n} from './shared.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport YAML from 'yaml';\n\nexport async function run(args: string[]): Promise<void> {\n const vaultPath = parseStringFlag(args, '--vault');\n const llmProvider = parseStringFlag(args, '--llm-provider') ?? 'ollama';\n const llmModel = parseStringFlag(args, '--llm-model') ?? 'gpt-oss';\n const llmUrl = parseStringFlag(args, '--llm-url') ?? PROVIDER_DEFAULTS[llmProvider]?.base_url;\n const embeddingProvider = parseStringFlag(args, '--embedding-provider') ?? 'ollama';\n const embeddingModel = parseStringFlag(args, '--embedding-model') ?? 'bge-m3';\n const embeddingUrl = parseStringFlag(args, '--embedding-url') ?? PROVIDER_DEFAULTS[embeddingProvider]?.base_url;\n const user = parseStringFlag(args, '--user') ?? '';\n const teamEnabled = args.includes('--team');\n\n // Resolve vault directory\n const vaultDir = vaultPath\n ? (vaultPath.startsWith('~/') ? path.join(os.homedir(), vaultPath.slice(2)) : path.resolve(vaultPath))\n : path.join(resolveVaultDir());\n\n // Check if already initialized\n if (fs.existsSync(path.join(vaultDir, 'myco.yaml'))) {\n console.log(`Vault already initialized at ${vaultDir}`);\n return;\n }\n\n console.log(`Initializing Myco vault at ${vaultDir}`);\n\n // Create directory structure\n const dirs = ['sessions', 'plans', 'memories', 'artifacts', 'team', 'buffer', 'logs'];\n for (const dir of dirs) {\n fs.mkdirSync(path.join(vaultDir, dir), { recursive: true });\n }\n\n // Write myco.yaml — all values explicit, no hidden defaults\n const config: Record<string, unknown> = {\n version: 2,\n intelligence: {\n llm: {\n provider: llmProvider,\n model: llmModel,\n ...(llmUrl ? { base_url: llmUrl } : {}),\n context_window: 8192,\n max_tokens: 1024,\n },\n embedding: {\n provider: embeddingProvider,\n model: embeddingModel,\n ...(embeddingUrl ? { base_url: embeddingUrl } : {}),\n },\n },\n daemon: {\n log_level: 'info',\n grace_period: 30,\n max_log_size: 5242880,\n },\n capture: {\n transcript_paths: [],\n artifact_watch: ['.claude/plans/', '.cursor/plans/'],\n artifact_extensions: ['.md'],\n buffer_max_events: 500,\n },\n context: {\n max_tokens: 1200,\n layers: { plans: 200, sessions: 500, memories: 300, team: 200 },\n },\n team: {\n enabled: teamEnabled,\n user,\n sync: 'git',\n },\n };\n\n fs.writeFileSync(\n path.join(vaultDir, 'myco.yaml'),\n YAML.stringify(config),\n 'utf-8',\n );\n\n // Write .gitignore\n fs.writeFileSync(path.join(vaultDir, '.gitignore'), VAULT_GITIGNORE, 'utf-8');\n\n // Write Obsidian dashboard\n fs.writeFileSync(path.join(vaultDir, '_dashboard.md'), DASHBOARD_CONTENT, 'utf-8');\n\n // Initialize FTS index\n const index = new MycoIndex(path.join(vaultDir, 'index.db'));\n initFts(index);\n index.close();\n\n // Summary\n console.log('');\n console.log('=== Myco Vault Initialized ===');\n console.log(`Path: ${vaultDir}`);\n console.log(`LLM provider: ${llmProvider} / ${llmModel}`);\n console.log(`Embedding provider: ${embeddingProvider} / ${embeddingModel}`);\n console.log(`Team mode: ${teamEnabled ? 'enabled' : 'disabled'}`);\n if (user) console.log(`User: ${user}`);\n console.log('');\n\n // If vault is outside the project, configure MYCO_VAULT_DIR for the current agent\n const projectRoot = path.resolve('.');\n const isProjectLocal = vaultDir.startsWith(projectRoot);\n if (!isProjectLocal) {\n configureVaultEnv(projectRoot, vaultDir);\n }\n\n console.log('Next: start a coding session — Myco will begin capturing automatically.');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAaA,kBAAiB;AAHjB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAGf,eAAsB,IAAI,MAA+B;AACvD,QAAM,YAAY,gBAAgB,MAAM,SAAS;AACjD,QAAM,cAAc,gBAAgB,MAAM,gBAAgB,KAAK;AAC/D,QAAM,WAAW,gBAAgB,MAAM,aAAa,KAAK;AACzD,QAAM,SAAS,gBAAgB,MAAM,WAAW,KAAK,kBAAkB,WAAW,GAAG;AACrF,QAAM,oBAAoB,gBAAgB,MAAM,sBAAsB,KAAK;AAC3E,QAAM,iBAAiB,gBAAgB,MAAM,mBAAmB,KAAK;AACrE,QAAM,eAAe,gBAAgB,MAAM,iBAAiB,KAAK,kBAAkB,iBAAiB,GAAG;AACvG,QAAM,OAAO,gBAAgB,MAAM,QAAQ,KAAK;AAChD,QAAM,cAAc,KAAK,SAAS,QAAQ;AAG1C,QAAM,WAAW,YACZ,UAAU,WAAW,IAAI,IAAI,KAAK,KAAK,GAAG,QAAQ,GAAG,UAAU,MAAM,CAAC,CAAC,IAAI,KAAK,QAAQ,SAAS,IAClG,KAAK,KAAK,gBAAgB,CAAC;AAG/B,MAAI,GAAG,WAAW,KAAK,KAAK,UAAU,WAAW,CAAC,GAAG;AACnD,YAAQ,IAAI,gCAAgC,QAAQ,EAAE;AACtD;AAAA,EACF;AAEA,UAAQ,IAAI,8BAA8B,QAAQ,EAAE;AAGpD,QAAM,OAAO,CAAC,YAAY,SAAS,YAAY,aAAa,QAAQ,UAAU,MAAM;AACpF,aAAW,OAAO,MAAM;AACtB,OAAG,UAAU,KAAK,KAAK,UAAU,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EAC5D;AAGA,QAAM,SAAkC;AAAA,IACtC,SAAS;AAAA,IACT,cAAc;AAAA,MACZ,KAAK;AAAA,QACH,UAAU;AAAA,QACV,OAAO;AAAA,QACP,GAAI,SAAS,EAAE,UAAU,OAAO,IAAI,CAAC;AAAA,QACrC,gBAAgB;AAAA,QAChB,YAAY;AAAA,MACd;AAAA,MACA,WAAW;AAAA,QACT,UAAU;AAAA,QACV,OAAO;AAAA,QACP,GAAI,eAAe,EAAE,UAAU,aAAa,IAAI,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,cAAc;AAAA,MACd,cAAc;AAAA,IAChB;AAAA,IACA,SAAS;AAAA,MACP,kBAAkB,CAAC;AAAA,MACnB,gBAAgB,CAAC,kBAAkB,gBAAgB;AAAA,MACnD,qBAAqB,CAAC,KAAK;AAAA,MAC3B,mBAAmB;AAAA,IACrB;AAAA,IACA,SAAS;AAAA,MACP,YAAY;AAAA,MACZ,QAAQ,EAAE,OAAO,KAAK,UAAU,KAAK,UAAU,KAAK,MAAM,IAAI;AAAA,IAChE;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT;AAAA,MACA,MAAM;AAAA,IACR;AAAA,EACF;AAEA,KAAG;AAAA,IACD,KAAK,KAAK,UAAU,WAAW;AAAA,IAC/B,YAAAA,QAAK,UAAU,MAAM;AAAA,IACrB;AAAA,EACF;AAGA,KAAG,cAAc,KAAK,KAAK,UAAU,YAAY,GAAG,iBAAiB,OAAO;AAG5E,KAAG,cAAc,KAAK,KAAK,UAAU,eAAe,GAAG,mBAAmB,OAAO;AAGjF,QAAM,QAAQ,IAAI,UAAU,KAAK,KAAK,UAAU,UAAU,CAAC;AAC3D,UAAQ,KAAK;AACb,QAAM,MAAM;AAGZ,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,gCAAgC;AAC5C,UAAQ,IAAI,uBAAuB,QAAQ,EAAE;AAC7C,UAAQ,IAAI,uBAAuB,WAAW,MAAM,QAAQ,EAAE;AAC9D,UAAQ,IAAI,uBAAuB,iBAAiB,MAAM,cAAc,EAAE;AAC1E,UAAQ,IAAI,uBAAuB,cAAc,YAAY,UAAU,EAAE;AACzE,MAAI,KAAM,SAAQ,IAAI,uBAAuB,IAAI,EAAE;AACnD,UAAQ,IAAI,EAAE;AAGd,QAAM,cAAc,KAAK,QAAQ,GAAG;AACpC,QAAM,iBAAiB,SAAS,WAAW,WAAW;AACtD,MAAI,CAAC,gBAAgB;AACnB,sBAAkB,aAAa,QAAQ;AAAA,EACzC;AAEA,UAAQ,IAAI,8EAAyE;AACvF;","names":["YAML"]}