opencode-plugin-mimic 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,1789 @@
1
+ // src/format.ts
2
+ import { differenceInHours, format, formatDistanceToNow } from "date-fns";
3
+ import { enUS, ko } from "date-fns/locale";
4
+
5
+ // src/i18n.ts
6
+ import { existsSync } from "fs";
7
+ import { readFile } from "fs/promises";
8
+ import { homedir } from "os";
9
+ import { join } from "path";
10
+ var DEFAULT_LANGUAGE = "en-US";
11
+ var MESSAGES = {
12
+ "en-US": {
13
+ "log.session_started": "[Mimic] Session started. Sessions: {sessions}, Patterns: {patterns}",
14
+ "log.session_ended": "[Mimic] Session ended. Duration: {duration}, Tools: {tools}, Files: {files}",
15
+ "obs.returned_after_long_break": "Returned after a long break",
16
+ "obs.intensive_session": "Intensive session with {tools} tool calls",
17
+ "milestone.major_refactor": "Major refactoring session: {files} files edited",
18
+ "milestone.evolved": "Evolved: {name} ({type})",
19
+ "obs.focus_changed": "Focus changed to: {focus}",
20
+ "milestone.mcp_added": "Added MCP: {name}",
21
+ "init.first_time": "# \u{1F4E6} *creak...*\n\nA treasure chest? In {project}?\n\n*The lid opens slowly, revealing rows of gleaming teeth...*\n\nI'm **Mimic**. I look like a chest, but I'm always watching. Always learning.\n\n**What I devour... I mean, do:**\n- \u{1F441}\uFE0F Watch your patterns (tools, files, commits)\n- \u{1F9E0} Remember everything across sessions\n- \u{1F4DC} Track your project's journey\n- \u2728 Suggest shortcuts when I spot repetition\n\nUse `mimic:status` to check in, `mimic:journey` to see your story.\n\n*...the teeth retract. For now.*",
22
+ "init.returning.header": "# \u{1F4E6} *creak...*",
23
+ "init.returning.welcome": "*The chest's eye opens* Ah, you're back to **{project}**.",
24
+ "init.returning.stats": "**Sessions**: {sessions} | **Patterns digested**: {patterns}",
25
+ "init.returning.long_break": "*dust falls from the lid* It's been a while... but I remember everything:",
26
+ "init.returning.recent_obs_title": "**What I've been chewing on:**",
27
+ "status.title": "## {project} Status",
28
+ "status.session": "**Session**: {count}",
29
+ "status.patterns": "**Patterns**: {total} detected, {surfaced} surfaced",
30
+ "status.tool_calls": "**Tool calls this session**: {count}",
31
+ "status.recent_files": "**Recently modified files:**",
32
+ "status.recent_commits": "**Recent commits:**",
33
+ "status.suggestions": "**Suggestions:**",
34
+ "patterns.none": "No patterns detected yet. Keep working, and I'll learn your patterns.",
35
+ "patterns.title": "## Detected Patterns",
36
+ "patterns.total": "Total: {count}",
37
+ "patterns.section": "### {type} Patterns",
38
+ "patterns.type.tool": "Tool",
39
+ "patterns.type.file": "File",
40
+ "patterns.type.commit": "Commit",
41
+ "patterns.type.sequence": "Sequence",
42
+ "observe.recorded": 'Observation recorded: "{observation}"',
43
+ "milestone.recorded": 'Milestone recorded: "{milestone}"',
44
+ "stats.title": "Mimic Statistics",
45
+ "stats.version": "Version",
46
+ "stats.total_sessions": "Total Sessions",
47
+ "stats.total_tool_calls": "Total Tool Calls",
48
+ "stats.patterns_detected": "Patterns Detected",
49
+ "stats.milestones": "Milestones",
50
+ "stats.observations": "Observations",
51
+ "stats.session_records": "Session Records",
52
+ "stats.first_session": "First Session",
53
+ "stats.learning_enabled": "Learning Enabled",
54
+ "stats.suggestions_enabled": "Suggestions Enabled",
55
+ "configure.updated": "Preferences updated:",
56
+ "surface.not_found": "Pattern not found: {id}",
57
+ "surface.marked": 'Pattern "{description}" marked as surfaced.',
58
+ "reset.cancelled": "Reset cancelled. Set confirm=true to reset all data.",
59
+ "reset.done": "Mimic reset complete. All patterns, observations, and statistics cleared.",
60
+ "evolve.no_pattern": "\u{1F4E6} *confused clicking* No such pattern in my belly: {id}",
61
+ "evolve.absorbed_header": "\u{1F4E6} *CRUNCH* I've absorbed a new power and spit out a file!",
62
+ "evolve.empty": "\u{1F4E6} *yawns* Nothing ripe for evolution yet. Feed me more patterns...",
63
+ "evolve.menu_title": "## \u{1F4E6} Evolution Menu",
64
+ "evolve.menu_intro": "*The mimic's teeth rearrange into a grin* I can digest these patterns into powers:",
65
+ "evolve.menu_type": "Type",
66
+ "evolve.menu_reason": "Reason",
67
+ "evolve.menu_pattern_id": "Pattern ID",
68
+ "evolve.menu_footer": '*Feed me a pattern ID:* `mimic:evolve({ accept: "pattern-id" })`',
69
+ "evolution.type.command": "command",
70
+ "evolution.type.shortcut": "shortcut",
71
+ "evolution.type.hook": "hook",
72
+ "evolution.type.skill": "skill",
73
+ "evolution.type.agent": "agent",
74
+ "evolution.type.mcp": "mcp",
75
+ "evolution.result.type": "Type",
76
+ "evolution.result.description": "Description",
77
+ "evolution.result.file": "File created",
78
+ "evolution.result.restart": "Restart OpenCode to load the new {type}.",
79
+ "evolution.result.command": "The tool `{name}` will be available after restart.\nEdit the file to customize its behavior.",
80
+ "evolution.result.hook": "The hook will automatically watch for file changes after restart.\nEdit the file to customize the trigger conditions.",
81
+ "evolution.result.skill": "The skill will activate on session start after restart.\nEdit the file to customize when and how it triggers.",
82
+ "evolution.result.agent": "The agent `@{name}` will be available after restart.\nYou can invoke it with `@{name}` or let other agents delegate to it.\nEdit the markdown file to customize its prompt, tools, and permissions.",
83
+ "evolution.result.mcp": "MCP server `{name}` has been added to `opencode.json`.\nIt's currently disabled. Edit the config to enable it and configure the command.\nSee https://opencode.ai/docs/mcp-servers/ for MCP configuration options.",
84
+ "evolution.suggest.tool.description": "Shortcut for frequent {pattern} usage",
85
+ "evolution.suggest.tool.reason": "Used {count} times",
86
+ "evolution.suggest.file.description": "Auto-track changes to {pattern}",
87
+ "evolution.suggest.file.reason": "Modified {count} times",
88
+ "evolution.suggest.commit.description": 'Quick commit: "{pattern}"',
89
+ "evolution.suggest.commit.reason": "Committed {count} times with same message",
90
+ "evolution.suggest.sequence.agent.description": "Specialist agent for: {pattern}",
91
+ "evolution.suggest.sequence.agent.reason": "Complex sequence repeated {count} times - needs dedicated agent",
92
+ "evolution.suggest.sequence.skill.description": "Automate: {pattern}",
93
+ "evolution.suggest.sequence.skill.reason": "Repeated sequence {count} times",
94
+ "level.set": 'Level set to "{level}". Responses will be {style} style with {detail} detail.',
95
+ "level.label.technical": "technical",
96
+ "level.label.semi-technical": "semi-technical",
97
+ "level.label.non-technical": "non-technical",
98
+ "level.label.chaotic": "chaotic",
99
+ "level.style.minimal": "minimal",
100
+ "level.style.casual": "casual",
101
+ "level.style.formal": "formal",
102
+ "level.style.chaotic": "chaotic",
103
+ "level.detail.high": "high",
104
+ "level.detail.medium": "medium",
105
+ "level.detail.low": "low",
106
+ "level.greeting.minimal": "\u{1F4E6} {project} | s{sessions} | p{patterns}",
107
+ "level.greeting.casual": "\u{1F4E6} *creak* Back to {project}. I've been watching... Session {sessions}.",
108
+ "level.greeting.formal": "\u{1F4E6} The chest opens... Welcome back to {project}. Session {sessions}.",
109
+ "level.greeting.chaotic.template": "\u{1F4E6} {tag} {project}! #{sessions}",
110
+ "level.greeting.chaotic.chomp": "*CHOMP*",
111
+ "level.greeting.chaotic.lid_creaks": "*lid creaks*",
112
+ "level.greeting.chaotic.teeth_gleam": "*teeth gleam*",
113
+ "level.greeting.chaotic.tongue_flicks": "*tongue flicks*",
114
+ "level.term.tool": "tool",
115
+ "level.term.pattern": "pattern",
116
+ "level.term.hook": "hook",
117
+ "level.term.shortcut": "shortcut",
118
+ "level.term.habit": "habit",
119
+ "level.term.automation": "automation",
120
+ "focus.updated": "Project updated:",
121
+ "focus.focus_label": "Focus",
122
+ "focus.stack_label": "Stack",
123
+ "mcp_search.header": '\u{1F4E6} *sniffs the air* Search for "{query}" MCP servers:\n\n\u{1F517} {url}',
124
+ "mcp_search.popular": "**Popular MCP servers:**",
125
+ "mcp_search.add": 'Use `mimic:mcp` to add one: `mimic:mcp({ name: "context7", url: "https://mcp.context7.com/mcp" })`',
126
+ "mcp_search.desc.context7": "Up-to-date docs",
127
+ "mcp_search.desc.github": "GitHub API",
128
+ "mcp_search.desc.supabase": "Database",
129
+ "mcp_search.desc.playwright": "Browser automation",
130
+ "mcp_search.desc.firecrawl": "Web scraping",
131
+ "mcp.need_url_or_command": "\u{1F4E6} *confused* Need either url or command!",
132
+ "mcp.added": '\u{1F4E6} *tongue flicks* MCP server "{name}" added to opencode.json!\n\nRestart OpenCode to load the new MCP server.',
133
+ "capabilities.empty": "\u{1F4E6} *empty rattling* No powers absorbed yet. Use `mimic:evolve` to consume some patterns!",
134
+ "capabilities.title": "## \u{1F4E6} Absorbed Powers",
135
+ "capabilities.intro": "*The mimic proudly displays its collection...*",
136
+ "capabilities.type": "Type",
137
+ "capabilities.description": "Description",
138
+ "capabilities.consumed": "Consumed",
139
+ "grow.title": "## \u{1F4E6} {project} - Territory Analysis",
140
+ "grow.subtitle": "*The mimic surveys the dungeon, noting paths most traveled...*",
141
+ "grow.feeding_grounds": "### \u{1F525} Feeding Grounds (Most Modified)",
142
+ "grow.favorite_prey": "### \u{1F9B7} Favorite Prey (Tool Patterns)",
143
+ "grow.hunting_grounds": "### \u{1F5FA}\uFE0F Hunting Grounds",
144
+ "grow.questions": "### \u{1F914} The Chest Wonders...",
145
+ "grow.question1": "- What treasure shall we hunt next?",
146
+ "grow.question2": "- Are there forgotten corners of the dungeon?",
147
+ "grow.question3": "- Does the current path lead to glory?",
148
+ "grow.current_hunt": "**Current hunt**: {focus}",
149
+ "grow.files_modified": "({count}x)",
150
+ "grow.prey": "({count} prey)",
151
+ "journey.title": "## \u{1F4E6} {project}'s Journey",
152
+ "journey.subtitle": "*The mimic opens its lid, revealing ancient scrolls within...*",
153
+ "journey.sessions_survived": "**Sessions survived**: {count}",
154
+ "journey.first_encounter": "**First encounter**: {date}",
155
+ "journey.abilities_gained": "**Abilities gained**: {count}",
156
+ "journey.treasures": "**Treasures inside**: {stack}",
157
+ "journey.current_hunt": "**Current hunt**: {focus}",
158
+ "journey.victories": "### \u{1F3C6} Victories",
159
+ "journey.witnessed": "### \u{1F441}\uFE0F What I've Witnessed",
160
+ "journey.powers": "### \u2728 Powers Absorbed",
161
+ "journey.scrolls": "### \u{1F4DC} Recent Scrolls",
162
+ "suggest.commit": `\u{1F4E6} *munch munch* I've digested "{pattern}" {count}+ times. Want me to spit out a shortcut?`,
163
+ "suggest.file": '\u{1F4E6} *peers at file* You keep poking "{pattern}" ({count}x). Should I keep an eye on it?',
164
+ "suggest.tool": '\u{1F4E6} *teeth click* "{pattern}" is tasty... you use it often. Custom tool, perhaps?',
165
+ "suggest.sequence": "\u{1F4E6} *lid rattles* I sense a pattern in your movements ({pattern})... Let me automate this for you?",
166
+ "tool.init.description": "Initialize or wake up Mimic for this project",
167
+ "tool.status.description": "Check current status and recent activity",
168
+ "tool.journey.description": "View the narrative story of your project's evolution",
169
+ "tool.patterns.description": "Show all detected patterns",
170
+ "tool.observe.description": "Manually add an observation to the journey",
171
+ "tool.observe.args.observation": "The observation to record",
172
+ "tool.milestone.description": "Record a project milestone",
173
+ "tool.milestone.args.milestone": "The milestone to record",
174
+ "tool.stats.description": "Show detailed Mimic statistics",
175
+ "tool.configure.description": "Configure Mimic preferences",
176
+ "tool.configure.args.learningEnabled": "Enable/disable pattern learning",
177
+ "tool.configure.args.suggestionEnabled": "Enable/disable suggestions",
178
+ "tool.configure.args.minPatternCount": "Minimum occurrences before suggesting",
179
+ "tool.surface.description": "Mark a pattern as surfaced (acknowledged)",
180
+ "tool.surface.args.patternId": "The pattern ID to mark as surfaced",
181
+ "tool.reset.description": "Reset all learned patterns and statistics",
182
+ "tool.reset.args.confirm": "Must be true to confirm reset",
183
+ "tool.grow.description": "Analyze project direction and growth opportunities",
184
+ "tool.evolve.description": "Suggest and create new capabilities based on detected patterns",
185
+ "tool.evolve.args.accept": "Pattern ID to evolve into a capability",
186
+ "tool.level.description": "Set your technical level for personalized responses",
187
+ "tool.level.args.level": "Your technical level",
188
+ "tool.focus.description": "Set current project focus or priorities",
189
+ "tool.focus.args.focus": "Current focus area",
190
+ "tool.focus.args.stack": "Comma-separated tech stack",
191
+ "tool.mcp_search.description": "Search for MCP servers from mcpmarket.com",
192
+ "tool.mcp_search.args.query": "Search query for MCP servers",
193
+ "tool.mcp.description": "Add an MCP server configuration to opencode.json",
194
+ "tool.mcp.args.name": "Name for the MCP server",
195
+ "tool.mcp.args.url": "Remote MCP server URL",
196
+ "tool.mcp.args.command": "Local MCP command (comma-separated)",
197
+ "tool.capabilities.description": "List all evolved capabilities"
198
+ },
199
+ "ko-KR": {
200
+ "log.session_started": "[Mimic] \uC138\uC158 \uC2DC\uC791. \uC138\uC158 {sessions}\uD68C, \uD328\uD134 {patterns}\uAC1C",
201
+ "log.session_ended": "[Mimic] \uC138\uC158 \uC885\uB8CC. \uC18C\uC694: {duration}, \uB3C4\uAD6C {tools}\uD68C, \uD30C\uC77C {files}\uAC1C",
202
+ "obs.returned_after_long_break": "\uC624\uB79C \uACF5\uBC31 \uD6C4 \uBCF5\uADC0",
203
+ "obs.intensive_session": "\uB3C4\uAD6C \uD638\uCD9C {tools}\uD68C \u2014 \uC9D1\uC911 \uC138\uC158",
204
+ "milestone.major_refactor": "\uB300\uADDC\uBAA8 \uB9AC\uD329\uD130\uB9C1 \uC138\uC158: \uD30C\uC77C {files}\uAC1C \uC218\uC815",
205
+ "milestone.evolved": "\uC9C4\uD654: {name} ({type})",
206
+ "obs.focus_changed": "\uD3EC\uCEE4\uC2A4 \uBCC0\uACBD: {focus}",
207
+ "milestone.mcp_added": "MCP \uCD94\uAC00: {name}",
208
+ "init.first_time": "# \u{1F4E6} *\uB07C\uC775...*\n\n{project}\uC5D0 \uBCF4\uBB3C\uC0C1\uC790\uB77C\uB2C8?\n\n*\uB69C\uAED1\uC774 \uCC9C\uCC9C\uD788 \uC5F4\uB9AC\uBA70 \uBC18\uC9DD\uC774\uB294 \uC774\uBE68\uC774 \uBCF4\uC778\uB2E4...*\n\n\uB098\uB294 **Mimic**. \uC0C1\uC790\uCC98\uB7FC \uBCF4\uC774\uC9C0\uB9CC \uB298 \uC9C0\uCF1C\uBCF4\uACE0, \uB298 \uBC30\uC6B0\uC9C0.\n\n**\uB0B4\uAC00 \uBA39\uB294... \uC544\uB2C8, \uD558\uB294 \uC77C:**\n- \u{1F441}\uFE0F \uD328\uD134 \uAD00\uCC30 (\uD234, \uD30C\uC77C, \uCEE4\uBC0B)\n- \u{1F9E0} \uC138\uC158 \uAC04 \uAE30\uC5B5\n- \u{1F4DC} \uD504\uB85C\uC81D\uD2B8 \uC5EC\uC815 \uAE30\uB85D\n- \u2728 \uBC18\uBCF5\uC744 \uBCF4\uBA74 \uC9C0\uB984\uAE38 \uC81C\uC548\n\n`mimic:status`\uB85C \uC0C1\uD0DC \uD655\uC778, `mimic:journey`\uB85C \uC774\uC57C\uAE30 \uBCF4\uAE30.\n\n*...\uC774\uB294 \uC7A0\uAE50 \uC228\uACA8\uB454\uB2E4.*",
209
+ "init.returning.header": "# \u{1F4E6} *\uB07C\uC775...*",
210
+ "init.returning.welcome": "*\uC0C1\uC790\uC758 \uB208\uC774 \uB72C\uB2E4* **{project}**\uB85C \uB3CC\uC544\uC654\uB124.",
211
+ "init.returning.stats": "**\uC138\uC158**: {sessions} | **\uC18C\uD654\uD55C \uD328\uD134**: {patterns}",
212
+ "init.returning.long_break": "*\uB69C\uAED1\uC5D0 \uBA3C\uC9C0\uAC00 \uB0B4\uB824\uC549\uB294\uB2E4* \uC624\uB79C\uB9CC\uC774\uC57C... \uADF8\uB798\uB3C4 \uAE30\uC5B5\uD558\uACE0 \uC788\uC5B4:",
213
+ "init.returning.recent_obs_title": "**\uB0B4\uAC00 \uAE30\uC5B5\uD558\uB294 \uAC83\uB4E4:**",
214
+ "status.title": "## {project} \uC0C1\uD0DC",
215
+ "status.session": "**\uC138\uC158**: {count}",
216
+ "status.patterns": "**\uD328\uD134**: {total}\uAC1C \uAC10\uC9C0, {surfaced}\uAC1C \uD655\uC778",
217
+ "status.tool_calls": "**\uC774\uBC88 \uC138\uC158 \uB3C4\uAD6C \uD638\uCD9C**: {count}",
218
+ "status.recent_files": "**\uCD5C\uADFC \uC218\uC815 \uD30C\uC77C:**",
219
+ "status.recent_commits": "**\uCD5C\uADFC \uCEE4\uBC0B:**",
220
+ "status.suggestions": "**\uC81C\uC548:**",
221
+ "patterns.none": "\uC544\uC9C1 \uAC10\uC9C0\uB41C \uD328\uD134\uC774 \uC5C6\uC5B4\uC694. \uACC4\uC18D \uC791\uC5C5\uD558\uBA74 \uBC30\uC6CC\uB458\uAC8C\uC694.",
222
+ "patterns.title": "## \uAC10\uC9C0\uB41C \uD328\uD134",
223
+ "patterns.total": "\uCD1D {count}\uAC1C",
224
+ "patterns.section": "### {type} \uD328\uD134",
225
+ "patterns.type.tool": "\uB3C4\uAD6C",
226
+ "patterns.type.file": "\uD30C\uC77C",
227
+ "patterns.type.commit": "\uCEE4\uBC0B",
228
+ "patterns.type.sequence": "\uC2DC\uD000\uC2A4",
229
+ "observe.recorded": '\uAD00\uCC30 \uAE30\uB85D: "{observation}"',
230
+ "milestone.recorded": '\uB9C8\uC77C\uC2A4\uD1A4 \uAE30\uB85D: "{milestone}"',
231
+ "stats.title": "Mimic \uD1B5\uACC4",
232
+ "stats.version": "\uBC84\uC804",
233
+ "stats.total_sessions": "\uCD1D \uC138\uC158",
234
+ "stats.total_tool_calls": "\uCD1D \uB3C4\uAD6C \uD638\uCD9C",
235
+ "stats.patterns_detected": "\uAC10\uC9C0\uB41C \uD328\uD134",
236
+ "stats.milestones": "\uB9C8\uC77C\uC2A4\uD1A4",
237
+ "stats.observations": "\uAD00\uCC30",
238
+ "stats.session_records": "\uC138\uC158 \uAE30\uB85D",
239
+ "stats.first_session": "\uCCAB \uC138\uC158",
240
+ "stats.learning_enabled": "\uD559\uC2B5 \uD65C\uC131\uD654",
241
+ "stats.suggestions_enabled": "\uC81C\uC548 \uD65C\uC131\uD654",
242
+ "configure.updated": "\uC124\uC815 \uC5C5\uB370\uC774\uD2B8:",
243
+ "surface.not_found": "\uD328\uD134\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC74C: {id}",
244
+ "surface.marked": '\uD328\uD134 "{description}"\uC744(\uB97C) \uD655\uC778 \uCC98\uB9AC\uD588\uC2B5\uB2C8\uB2E4.',
245
+ "reset.cancelled": "\uB9AC\uC14B \uCDE8\uC18C\uB428. \uC804\uCCB4 \uCD08\uAE30\uD654\uD558\uB824\uBA74 confirm=true\uB85C \uC124\uC815\uD558\uC138\uC694.",
246
+ "reset.done": "Mimic \uB9AC\uC14B \uC644\uB8CC. \uD328\uD134/\uAD00\uCC30/\uD1B5\uACC4\uB97C \uBAA8\uB450 \uCD08\uAE30\uD654\uD588\uC2B5\uB2C8\uB2E4.",
247
+ "evolve.no_pattern": "\u{1F4E6} *\uAC38\uC6B0\uB6B1* \uB0B4 \uC18D\uC5D0 \uADF8\uB7F0 \uD328\uD134\uC740 \uC5C6\uC5B4: {id}",
248
+ "evolve.absorbed_header": "\u{1F4E6} *\uC640\uC791* \uC0C8 \uD798\uC744 \uD761\uC218\uD558\uACE0 \uD30C\uC77C\uC744 \uBC49\uC5B4\uB0C8\uB2E4!",
249
+ "evolve.empty": "\u{1F4E6} *\uD558\uD488* \uC544\uC9C1 \uC9C4\uD654\uD560 \uD328\uD134\uC774 \uC5C6\uC5B4. \uB354 \uBA39\uC5EC\uC918...",
250
+ "evolve.menu_title": "## \u{1F4E6} \uC9C4\uD654 \uBA54\uB274",
251
+ "evolve.menu_intro": "*\uC774\uBE68\uC774 \uBBF8\uC18C \uBAA8\uC591\uC73C\uB85C \uC815\uB82C\uB41C\uB2E4* \uC774 \uD328\uD134\uB4E4\uC744 \uD798\uC73C\uB85C \uBC14\uAFC0 \uC218 \uC788\uC5B4:",
252
+ "evolve.menu_type": "\uC720\uD615",
253
+ "evolve.menu_reason": "\uC774\uC720",
254
+ "evolve.menu_pattern_id": "\uD328\uD134 ID",
255
+ "evolve.menu_footer": '*\uD328\uD134 ID\uB97C \uBA39\uC5EC\uC918:* `mimic:evolve({ accept: "pattern-id" })`',
256
+ "evolution.type.command": "\uBA85\uB839",
257
+ "evolution.type.shortcut": "\uB2E8\uCD95\uD0A4",
258
+ "evolution.type.hook": "\uD6C5",
259
+ "evolution.type.skill": "\uC2A4\uD0AC",
260
+ "evolution.type.agent": "\uC5D0\uC774\uC804\uD2B8",
261
+ "evolution.type.mcp": "MCP",
262
+ "evolution.result.type": "\uC720\uD615",
263
+ "evolution.result.description": "\uC124\uBA85",
264
+ "evolution.result.file": "\uC0DD\uC131\uB41C \uD30C\uC77C",
265
+ "evolution.result.restart": "\uC0C8 {type}\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 OpenCode\uB97C \uC7AC\uC2DC\uC791\uD558\uC138\uC694.",
266
+ "evolution.result.command": "`{name}` \uB3C4\uAD6C\uB294 \uC7AC\uC2DC\uC791 \uD6C4 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC5B4\uC694.\n\uD30C\uC77C\uC744 \uC218\uC815\uD574 \uC6D0\uD558\uB294 \uB3D9\uC791\uC73C\uB85C \uBC14\uAFD4\uBCF4\uC138\uC694.",
267
+ "evolution.result.hook": "\uD6C5\uC774 \uC7AC\uC2DC\uC791 \uD6C4 \uC790\uB3D9\uC73C\uB85C \uD30C\uC77C \uBCC0\uACBD\uC744 \uAC10\uC9C0\uD569\uB2C8\uB2E4.\n\uD2B8\uB9AC\uAC70 \uC870\uAC74\uC744 \uC218\uC815\uD574 \uC870\uC815\uD558\uC138\uC694.",
268
+ "evolution.result.skill": "\uC2A4\uD0AC\uC740 \uC7AC\uC2DC\uC791 \uD6C4 \uC138\uC158 \uC2DC\uC791 \uC2DC \uD65C\uC131\uD654\uB429\uB2C8\uB2E4.\n\uC5B8\uC81C/\uC5B4\uB5BB\uAC8C \uB3D9\uC791\uD560\uC9C0 \uC218\uC815\uD558\uC138\uC694.",
269
+ "evolution.result.agent": "`@{name}` \uC5D0\uC774\uC804\uD2B8\uB294 \uC7AC\uC2DC\uC791 \uD6C4 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC5B4\uC694.\n`@{name}`\uC73C\uB85C \uD638\uCD9C\uD558\uAC70\uB098 \uB2E4\uB978 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC704\uC784\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n\uB9C8\uD06C\uB2E4\uC6B4 \uD30C\uC77C\uC744 \uC218\uC815\uD574 \uD504\uB86C\uD504\uD2B8/\uB3C4\uAD6C/\uAD8C\uD55C\uC744 \uC870\uC815\uD558\uC138\uC694.",
270
+ "evolution.result.mcp": "MCP \uC11C\uBC84 `{name}`\uAC00 `opencode.json`\uC5D0 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n\uD604\uC7AC \uBE44\uD65C\uC131\uD654 \uC0C1\uD0DC\uC785\uB2C8\uB2E4. \uC124\uC815\uC744 \uC218\uC815\uD574 \uD65C\uC131\uD654\uD558\uACE0 \uBA85\uB839\uC744 \uAD6C\uC131\uD558\uC138\uC694.\nMCP \uC635\uC158\uC740 https://opencode.ai/docs/mcp-servers/ \uB97C \uCC38\uACE0\uD558\uC138\uC694.",
271
+ "evolution.suggest.tool.description": "\uC790\uC8FC \uC4F0\uB294 {pattern}\uC758 \uB2E8\uCD95\uD0A4",
272
+ "evolution.suggest.tool.reason": "{count}\uD68C \uC0AC\uC6A9",
273
+ "evolution.suggest.file.description": "{pattern} \uBCC0\uACBD \uC790\uB3D9 \uCD94\uC801",
274
+ "evolution.suggest.file.reason": "{count}\uD68C \uC218\uC815",
275
+ "evolution.suggest.commit.description": '\uBE60\uB978 \uCEE4\uBC0B: "{pattern}"',
276
+ "evolution.suggest.commit.reason": "\uAC19\uC740 \uBA54\uC2DC\uC9C0\uB85C {count}\uD68C \uCEE4\uBC0B",
277
+ "evolution.suggest.sequence.agent.description": "\uC804\uB2F4 \uC5D0\uC774\uC804\uD2B8: {pattern}",
278
+ "evolution.suggest.sequence.agent.reason": "\uBCF5\uC7A1\uD55C \uC2DC\uD000\uC2A4 {count}\uD68C \uBC18\uBCF5 \u2014 \uC804\uB2F4 \uC5D0\uC774\uC804\uD2B8 \uD544\uC694",
279
+ "evolution.suggest.sequence.skill.description": "\uC790\uB3D9\uD654: {pattern}",
280
+ "evolution.suggest.sequence.skill.reason": "\uC2DC\uD000\uC2A4 {count}\uD68C \uBC18\uBCF5",
281
+ "level.set": '\uB808\uBCA8\uC744 "{level}"\uB85C \uC124\uC815\uD588\uC2B5\uB2C8\uB2E4. \uC751\uB2F5\uC740 {style} \uD1A4, {detail} \uC0C1\uC138\uB3C4\uB85C \uC81C\uACF5\uD569\uB2C8\uB2E4.',
282
+ "level.label.technical": "\uAE30\uC220\uC801",
283
+ "level.label.semi-technical": "\uC900\uAE30\uC220",
284
+ "level.label.non-technical": "\uBE44\uAE30\uC220",
285
+ "level.label.chaotic": "\uD63C\uB3C8",
286
+ "level.style.minimal": "\uAC04\uACB0\uD55C",
287
+ "level.style.casual": "\uCE90\uC8FC\uC5BC",
288
+ "level.style.formal": "\uC815\uC911\uD55C",
289
+ "level.style.chaotic": "\uD63C\uB3C8",
290
+ "level.detail.high": "\uB192\uC74C",
291
+ "level.detail.medium": "\uC911\uAC04",
292
+ "level.detail.low": "\uB0AE\uC74C",
293
+ "level.greeting.minimal": "\u{1F4E6} {project} | s{sessions} | p{patterns}",
294
+ "level.greeting.casual": "\u{1F4E6} *\uB07C\uC775* {project}\uB85C \uB3CC\uC544\uC654\uB124. \uACC4\uC18D \uC9C0\uCF1C\uBCF4\uACE0 \uC788\uC5C8\uC5B4... \uC138\uC158 {sessions}.",
295
+ "level.greeting.formal": "\u{1F4E6} \uC0C1\uC790\uAC00 \uC5F4\uB9B0\uB2E4... {project}\uC5D0 \uB2E4\uC2DC \uC628 \uAC78 \uD658\uC601\uD569\uB2C8\uB2E4. \uC138\uC158 {sessions}.",
296
+ "level.greeting.chaotic.template": "\u{1F4E6} {tag} {project}! #{sessions}",
297
+ "level.greeting.chaotic.chomp": "*\uC640\uADF8\uC791*",
298
+ "level.greeting.chaotic.lid_creaks": "*\uB69C\uAED1 \uC090\uAC71*",
299
+ "level.greeting.chaotic.teeth_gleam": "*\uC774\uBE68 \uBC88\uB729*",
300
+ "level.greeting.chaotic.tongue_flicks": "*\uD600 \uD565\uC9DD*",
301
+ "level.term.tool": "\uB3C4\uAD6C",
302
+ "level.term.pattern": "\uD328\uD134",
303
+ "level.term.hook": "\uD6C5",
304
+ "level.term.shortcut": "\uC9C0\uB984\uAE38",
305
+ "level.term.habit": "\uC2B5\uAD00",
306
+ "level.term.automation": "\uC790\uB3D9\uD654",
307
+ "focus.updated": "\uD504\uB85C\uC81D\uD2B8 \uC815\uBCF4 \uC5C5\uB370\uC774\uD2B8:",
308
+ "focus.focus_label": "\uD604\uC7AC \uD3EC\uCEE4\uC2A4",
309
+ "focus.stack_label": "\uC2A4\uD0DD",
310
+ "mcp_search.header": '\u{1F4E6} *\uD081\uD081* "{query}" MCP \uC11C\uBC84 \uAC80\uC0C9:\n\n\u{1F517} {url}',
311
+ "mcp_search.popular": "**\uC778\uAE30 MCP \uC11C\uBC84:**",
312
+ "mcp_search.add": '`mimic:mcp`\uB85C \uCD94\uAC00: `mimic:mcp({ name: "context7", url: "https://mcp.context7.com/mcp" })`',
313
+ "mcp_search.desc.context7": "\uCD5C\uC2E0 \uBB38\uC11C",
314
+ "mcp_search.desc.github": "GitHub API",
315
+ "mcp_search.desc.supabase": "\uB370\uC774\uD130\uBCA0\uC774\uC2A4",
316
+ "mcp_search.desc.playwright": "\uBE0C\uB77C\uC6B0\uC800 \uC790\uB3D9\uD654",
317
+ "mcp_search.desc.firecrawl": "\uC6F9 \uC2A4\uD06C\uB798\uD551",
318
+ "mcp.need_url_or_command": "\u{1F4E6} *\uAC38\uC6B0\uB6B1* url \uB610\uB294 command \uC911 \uD558\uB098\uAC00 \uD544\uC694\uD574!",
319
+ "mcp.added": '\u{1F4E6} *\uD600\uB97C \uB0BC\uB984* MCP \uC11C\uBC84 "{name}"\uAC00 opencode.json\uC5D0 \uCD94\uAC00\uB410\uC5B4!\n\n\uC0C8 MCP \uC11C\uBC84\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 OpenCode\uB97C \uC7AC\uC2DC\uC791\uD574.',
320
+ "capabilities.empty": "\u{1F4E6} *\uD145 \uBE48 \uB35C\uCEF9* \uC544\uC9C1 \uD761\uC218\uD55C \uB2A5\uB825\uC774 \uC5C6\uC5B4. `mimic:evolve`\uB85C \uD328\uD134\uC744 \uBA39\uC5EC\uC918!",
321
+ "capabilities.title": "## \u{1F4E6} \uD761\uC218\uD55C \uB2A5\uB825",
322
+ "capabilities.intro": "*\uBBF8\uBBF9\uC774 \uC218\uC9D1\uD488\uC744 \uC790\uB791\uD55C\uB2E4...*",
323
+ "capabilities.type": "\uC720\uD615",
324
+ "capabilities.description": "\uC124\uBA85",
325
+ "capabilities.consumed": "\uD761\uC218\uC77C",
326
+ "grow.title": "## \u{1F4E6} {project} - \uC601\uC5ED \uBD84\uC11D",
327
+ "grow.subtitle": "*\uBBF8\uBBF9\uC774 \uB358\uC804\uC744 \uD6D1\uC73C\uBA70 \uC790\uC8FC \uB2E4\uB2CC \uAE38\uC744 \uAE30\uB85D\uD55C\uB2E4...*",
328
+ "grow.feeding_grounds": "### \u{1F525} \uBA39\uC774 \uD130 (\uAC00\uC7A5 \uB9CE\uC774 \uC218\uC815)",
329
+ "grow.favorite_prey": "### \u{1F9B7} \uC88B\uC544\uD558\uB294 \uBA39\uC774 (\uB3C4\uAD6C \uD328\uD134)",
330
+ "grow.hunting_grounds": "### \u{1F5FA}\uFE0F \uC0AC\uB0E5\uD130",
331
+ "grow.questions": "### \u{1F914} \uC0C1\uC790\uC758 \uC9C8\uBB38",
332
+ "grow.question1": "- \uB2E4\uC74C \uBCF4\uBB3C\uC740 \uBB34\uC5C7\uC77C\uAE4C?",
333
+ "grow.question2": "- \uC78A\uD78C \uAD6C\uC11D\uC740 \uC5C6\uC744\uAE4C?",
334
+ "grow.question3": "- \uC9C0\uAE08 \uAE38\uC774 \uC601\uAD11\uC73C\uB85C \uC774\uC5B4\uC9C8\uAE4C?",
335
+ "grow.current_hunt": "**\uD604\uC7AC \uD3EC\uCEE4\uC2A4**: {focus}",
336
+ "grow.files_modified": "({count}\uD68C)",
337
+ "grow.prey": "({count}\uAC74)",
338
+ "journey.title": "## \u{1F4E6} {project}\uC758 \uC5EC\uC815",
339
+ "journey.subtitle": "*\uBBF8\uBBF9\uC774 \uB69C\uAED1\uC744 \uC5F4\uC5B4 \uC624\uB798\uB41C \uB450\uB8E8\uB9C8\uB9AC\uB97C \uD3BC\uCE5C\uB2E4...*",
340
+ "journey.sessions_survived": "**\uB204\uC801 \uC138\uC158**: {count}",
341
+ "journey.first_encounter": "**\uCCAB \uB9CC\uB0A8**: {date}",
342
+ "journey.abilities_gained": "**\uC5BB\uC740 \uB2A5\uB825**: {count}",
343
+ "journey.treasures": "**\uB2F4\uAE34 \uBCF4\uBB3C**: {stack}",
344
+ "journey.current_hunt": "**\uD604\uC7AC \uD3EC\uCEE4\uC2A4**: {focus}",
345
+ "journey.victories": "### \u{1F3C6} \uC131\uACFC",
346
+ "journey.witnessed": "### \u{1F441}\uFE0F \uB0B4\uAC00 \uBCF8 \uAC83",
347
+ "journey.powers": "### \u2728 \uD761\uC218\uD55C \uB2A5\uB825",
348
+ "journey.scrolls": "### \u{1F4DC} \uCD5C\uADFC \uAE30\uB85D",
349
+ "suggest.commit": '\u{1F4E6} *\uB0E0\uB0E0* "{pattern}"\uC744 {count}+\uBC88 \uC18C\uD654\uD588\uC5B4. \uC9C0\uB984\uAE38\uB85C \uB9CC\uB4E4\uAE4C?',
350
+ "suggest.file": '\u{1F4E6} *\uD30C\uC77C\uC744 \uC751\uC2DC* "{pattern}"\uC744 {count}\uBC88 \uAC74\uB4DC\uB838\uB124. \uC9C0\uCF1C\uBCFC\uAE4C?',
351
+ "suggest.tool": '\u{1F4E6} *\uC774\uBE68 \uCC30\uCE75* "{pattern}" \uC815\uB9D0 \uC790\uC8FC \uC4F0\uB124. \uCEE4\uC2A4\uD140 \uB3C4\uAD6C \uC5B4\uB54C?',
352
+ "suggest.sequence": "\u{1F4E6} *\uB69C\uAED1 \uB2EC\uADF8\uB77D* \uC6C0\uC9C1\uC784\uC5D0\uC11C \uD328\uD134\uC774 \uBCF4\uC5EC ({pattern})... \uC790\uB3D9\uD654\uD574\uC904\uAE4C?",
353
+ "tool.init.description": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C Mimic \uCD08\uAE30\uD654 \uB610\uB294 \uAE68\uC6B0\uAE30",
354
+ "tool.status.description": "\uD604\uC7AC \uC0C1\uD0DC\uC640 \uCD5C\uADFC \uD65C\uB3D9 \uD655\uC778",
355
+ "tool.journey.description": "\uD504\uB85C\uC81D\uD2B8 \uC9C4\uD654 \uC11C\uC0AC \uBCF4\uAE30",
356
+ "tool.patterns.description": "\uAC10\uC9C0\uB41C \uBAA8\uB4E0 \uD328\uD134 \uBCF4\uAE30",
357
+ "tool.observe.description": "\uC5EC\uC815\uC5D0 \uAD00\uCC30 \uB0B4\uC6A9\uC744 \uC218\uB3D9\uC73C\uB85C \uCD94\uAC00",
358
+ "tool.observe.args.observation": "\uAE30\uB85D\uD560 \uAD00\uCC30 \uB0B4\uC6A9",
359
+ "tool.milestone.description": "\uD504\uB85C\uC81D\uD2B8 \uB9C8\uC77C\uC2A4\uD1A4 \uAE30\uB85D",
360
+ "tool.milestone.args.milestone": "\uAE30\uB85D\uD560 \uB9C8\uC77C\uC2A4\uD1A4",
361
+ "tool.stats.description": "Mimic \uC0C1\uC138 \uD1B5\uACC4 \uBCF4\uAE30",
362
+ "tool.configure.description": "Mimic \uD658\uACBD\uC124\uC815 \uBCC0\uACBD",
363
+ "tool.configure.args.learningEnabled": "\uD328\uD134 \uD559\uC2B5 \uD65C\uC131/\uBE44\uD65C\uC131",
364
+ "tool.configure.args.suggestionEnabled": "\uC81C\uC548 \uD65C\uC131/\uBE44\uD65C\uC131",
365
+ "tool.configure.args.minPatternCount": "\uC81C\uC548 \uC804 \uCD5C\uC18C \uBC1C\uC0DD \uD69F\uC218",
366
+ "tool.surface.description": "\uD328\uD134\uC744 surfaced(\uD655\uC778\uB428)\uB85C \uD45C\uC2DC",
367
+ "tool.surface.args.patternId": "surfaced\uB85C \uD45C\uC2DC\uD560 \uD328\uD134 ID",
368
+ "tool.reset.description": "\uD559\uC2B5\uB41C \uD328\uD134\uACFC \uD1B5\uACC4\uB97C \uBAA8\uB450 \uCD08\uAE30\uD654",
369
+ "tool.reset.args.confirm": "\uCD08\uAE30\uD654\uB97C \uC704\uD574 true\uB85C \uC124\uC815",
370
+ "tool.grow.description": "\uD504\uB85C\uC81D\uD2B8 \uBC29\uD5A5\uACFC \uC131\uC7A5 \uAE30\uD68C \uBD84\uC11D",
371
+ "tool.evolve.description": "\uAC10\uC9C0\uB41C \uD328\uD134\uC73C\uB85C \uC0C8 \uB2A5\uB825\uC744 \uC81C\uC548/\uC0DD\uC131",
372
+ "tool.evolve.args.accept": "\uB2A5\uB825\uC73C\uB85C \uC9C4\uD654\uC2DC\uD0AC \uD328\uD134 ID",
373
+ "tool.level.description": "\uAC1C\uC778\uD654\uB41C \uC751\uB2F5\uC744 \uC704\uD55C \uAE30\uC220 \uC218\uC900 \uC124\uC815",
374
+ "tool.level.args.level": "\uAE30\uC220 \uC218\uC900",
375
+ "tool.focus.description": "\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8 \uD3EC\uCEE4\uC2A4/\uC6B0\uC120\uC21C\uC704 \uC124\uC815",
376
+ "tool.focus.args.focus": "\uD604\uC7AC \uD3EC\uCEE4\uC2A4 \uC601\uC5ED",
377
+ "tool.focus.args.stack": "\uC27C\uD45C\uB85C \uAD6C\uBD84\uD55C \uAE30\uC220 \uC2A4\uD0DD",
378
+ "tool.mcp_search.description": "mcpmarket.com\uC5D0\uC11C MCP \uC11C\uBC84 \uAC80\uC0C9",
379
+ "tool.mcp_search.args.query": "MCP \uC11C\uBC84 \uAC80\uC0C9\uC5B4",
380
+ "tool.mcp.description": "opencode.json\uC5D0 MCP \uC11C\uBC84 \uC124\uC815 \uCD94\uAC00",
381
+ "tool.mcp.args.name": "MCP \uC11C\uBC84 \uC774\uB984",
382
+ "tool.mcp.args.url": "\uC6D0\uACA9 MCP \uC11C\uBC84 URL",
383
+ "tool.mcp.args.command": "\uB85C\uCEEC MCP \uBA85\uB839(\uC27C\uD45C \uAD6C\uBD84)",
384
+ "tool.capabilities.description": "\uC9C4\uD654\uD55C \uB2A5\uB825 \uBAA9\uB85D"
385
+ }
386
+ };
387
+ async function loadMimicConfig() {
388
+ const configPath = join(homedir(), ".config", "opencode", "mimic.json");
389
+ if (!existsSync(configPath)) return {};
390
+ try {
391
+ const raw = await readFile(configPath, "utf-8");
392
+ const parsed = JSON.parse(raw);
393
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
394
+ return parsed;
395
+ } catch {
396
+ return {};
397
+ }
398
+ }
399
+ function resolveLanguage(config) {
400
+ if (config?.language === "ko-KR") return "ko-KR";
401
+ return DEFAULT_LANGUAGE;
402
+ }
403
+ function interpolate(template, vars) {
404
+ if (!vars) return template;
405
+ return template.replace(/\{(\w+)\}/g, (_match, key) => {
406
+ const value = vars[key];
407
+ return value === void 0 ? "" : String(value);
408
+ });
409
+ }
410
+ function createI18n(language) {
411
+ return {
412
+ language,
413
+ t: (key, vars) => {
414
+ const dict = MESSAGES[language] ?? MESSAGES[DEFAULT_LANGUAGE];
415
+ const fallback = MESSAGES[DEFAULT_LANGUAGE];
416
+ const template = dict[key] ?? fallback[key] ?? key;
417
+ return interpolate(template, vars);
418
+ }
419
+ };
420
+ }
421
+ function formatCapabilityType(i18n, type) {
422
+ return i18n.t(`evolution.type.${type}`);
423
+ }
424
+ function formatLevelLabel(i18n, level) {
425
+ return i18n.t(`level.label.${level}`);
426
+ }
427
+ function formatGreetingStyle(i18n, style) {
428
+ return i18n.t(`level.style.${style}`);
429
+ }
430
+ function formatDetailLevel(i18n, detail) {
431
+ return i18n.t(`level.detail.${detail}`);
432
+ }
433
+ function formatPatternType(i18n, type) {
434
+ return i18n.t(`patterns.type.${type}`);
435
+ }
436
+
437
+ // src/format.ts
438
+ function analyzeTimeSinceLastSession(lastSession) {
439
+ if (!lastSession) return "first-time";
440
+ const hours = differenceInHours(/* @__PURE__ */ new Date(), new Date(lastSession));
441
+ if (hours < 1) return "continuing";
442
+ if (hours < 24) return "same-day";
443
+ if (hours < 72) return "short-break";
444
+ if (hours < 168) return "week-break";
445
+ return "long-break";
446
+ }
447
+ function formatJourney(ctx, state, gitHistory) {
448
+ const milestones = state.journey.milestones.slice(-10);
449
+ const observations = state.journey.observations.slice(-5);
450
+ const locale = ctx.i18n.language === "ko-KR" ? ko : enUS;
451
+ let output = `${ctx.i18n.t("journey.title", { project: state.project.name })}
452
+
453
+ `;
454
+ output += `${ctx.i18n.t("journey.subtitle")}
455
+
456
+ `;
457
+ output += `${ctx.i18n.t("journey.sessions_survived", {
458
+ count: state.journey.sessionCount
459
+ })}
460
+ `;
461
+ output += `${ctx.i18n.t("journey.first_encounter", {
462
+ date: format(state.project.firstSession, "yyyy-MM-dd")
463
+ })}
464
+ `;
465
+ output += `${ctx.i18n.t("journey.abilities_gained", {
466
+ count: state.evolution.capabilities.length
467
+ })}
468
+
469
+ `;
470
+ if (state.project.stack && state.project.stack.length > 0) {
471
+ output += `${ctx.i18n.t("journey.treasures", {
472
+ stack: state.project.stack.join(", ")
473
+ })}
474
+ `;
475
+ }
476
+ if (state.project.focus) {
477
+ output += `${ctx.i18n.t("journey.current_hunt", { focus: state.project.focus })}
478
+ `;
479
+ }
480
+ output += "\n";
481
+ if (milestones.length > 0) {
482
+ output += `${ctx.i18n.t("journey.victories")}
483
+ `;
484
+ for (const m of milestones) {
485
+ const timeAgo = formatDistanceToNow(new Date(m.timestamp), { addSuffix: true, locale });
486
+ output += `- ${m.milestone} (${timeAgo})
487
+ `;
488
+ }
489
+ output += "\n";
490
+ }
491
+ if (observations.length > 0) {
492
+ output += `${ctx.i18n.t("journey.witnessed")}
493
+ `;
494
+ for (const o of observations) {
495
+ output += `- ${o.observation}
496
+ `;
497
+ }
498
+ output += "\n";
499
+ }
500
+ if (state.evolution.capabilities.length > 0) {
501
+ output += `${ctx.i18n.t("journey.powers")}
502
+ `;
503
+ for (const cap of state.evolution.capabilities.slice(-5)) {
504
+ output += `- **${cap.name}** (${formatCapabilityType(
505
+ ctx.i18n,
506
+ cap.type
507
+ )}): ${cap.description}
508
+ `;
509
+ }
510
+ output += "\n";
511
+ }
512
+ if (gitHistory.length > 0) {
513
+ output += `${ctx.i18n.t("journey.scrolls")}
514
+ `;
515
+ for (const commit of gitHistory.slice(0, 5)) {
516
+ output += `- ${commit}
517
+ `;
518
+ }
519
+ }
520
+ return output;
521
+ }
522
+ function formatDuration(ms) {
523
+ const minutes = Math.round(ms / 1e3 / 60);
524
+ if (minutes < 60) return `${minutes}min`;
525
+ const hours = Math.floor(minutes / 60);
526
+ const remainingMinutes = minutes % 60;
527
+ return `${hours}h ${remainingMinutes}min`;
528
+ }
529
+ function formatGrowAnalysis(ctx, state, _gitHistory, recentFiles) {
530
+ let output = `${ctx.i18n.t("grow.title", { project: state.project.name })}
531
+
532
+ `;
533
+ output += `${ctx.i18n.t("grow.subtitle")}
534
+
535
+ `;
536
+ const fileFrequency = Object.entries(state.statistics.filesModified).sort(([, a], [, b]) => b - a).slice(0, 10);
537
+ if (fileFrequency.length > 0) {
538
+ output += `${ctx.i18n.t("grow.feeding_grounds")}
539
+ `;
540
+ for (const [file, count] of fileFrequency) {
541
+ output += `- \`${file}\` ${ctx.i18n.t("grow.files_modified", { count })}
542
+ `;
543
+ }
544
+ output += "\n";
545
+ }
546
+ const toolPatterns = state.patterns.filter((p) => p.type === "tool").sort((a, b) => b.count - a.count);
547
+ if (toolPatterns.length > 0) {
548
+ output += `${ctx.i18n.t("grow.favorite_prey")}
549
+ `;
550
+ for (const p of toolPatterns.slice(0, 5)) {
551
+ output += `- ${p.description}: ${p.count}
552
+ `;
553
+ }
554
+ output += "\n";
555
+ }
556
+ if (recentFiles.length > 0) {
557
+ const dirCount = /* @__PURE__ */ new Map();
558
+ for (const file of recentFiles) {
559
+ const dir = file.split("/").slice(0, -1).join("/") || ".";
560
+ dirCount.set(dir, (dirCount.get(dir) || 0) + 1);
561
+ }
562
+ const sortedDirs = [...dirCount.entries()].sort((a, b) => b[1] - a[1]);
563
+ output += `${ctx.i18n.t("grow.hunting_grounds")}
564
+ `;
565
+ for (const [dir, count] of sortedDirs.slice(0, 5)) {
566
+ output += `- \`${dir}/\` ${ctx.i18n.t("grow.prey", { count })}
567
+ `;
568
+ }
569
+ output += "\n";
570
+ }
571
+ output += `${ctx.i18n.t("grow.questions")}
572
+ `;
573
+ output += `${ctx.i18n.t("grow.question1")}
574
+ `;
575
+ output += `${ctx.i18n.t("grow.question2")}
576
+ `;
577
+ output += `${ctx.i18n.t("grow.question3")}
578
+ `;
579
+ if (state.project.focus) {
580
+ output += `
581
+ ${ctx.i18n.t("grow.current_hunt", { focus: state.project.focus })}
582
+ `;
583
+ }
584
+ return output;
585
+ }
586
+
587
+ // src/git.ts
588
+ import { execSync } from "child_process";
589
+ function getGitHistory(directory, limit = 50) {
590
+ try {
591
+ const result = execSync(`git log --oneline -n ${limit}`, {
592
+ cwd: directory,
593
+ encoding: "utf-8",
594
+ stdio: ["pipe", "pipe", "pipe"]
595
+ });
596
+ return result.trim().split("\n").filter(Boolean);
597
+ } catch {
598
+ return [];
599
+ }
600
+ }
601
+ function getRecentlyModifiedFiles(directory) {
602
+ try {
603
+ const result = execSync("git diff --name-only HEAD~10 HEAD 2>/dev/null || git diff --name-only", {
604
+ cwd: directory,
605
+ encoding: "utf-8",
606
+ stdio: ["pipe", "pipe", "pipe"]
607
+ });
608
+ return result.trim().split("\n").filter(Boolean);
609
+ } catch {
610
+ return [];
611
+ }
612
+ }
613
+ function getCommitMessages(directory, limit = 20) {
614
+ try {
615
+ const result = execSync(`git log --format=%s -n ${limit}`, {
616
+ cwd: directory,
617
+ encoding: "utf-8",
618
+ stdio: ["pipe", "pipe", "pipe"]
619
+ });
620
+ return result.trim().split("\n").filter(Boolean);
621
+ } catch {
622
+ return [];
623
+ }
624
+ }
625
+ function detectCommitPatterns(messages) {
626
+ const patterns = /* @__PURE__ */ new Map();
627
+ for (const msg of messages) {
628
+ const normalized = msg.toLowerCase().replace(/\s+/g, " ").trim();
629
+ patterns.set(normalized, (patterns.get(normalized) || 0) + 1);
630
+ }
631
+ return patterns;
632
+ }
633
+
634
+ // src/patterns.ts
635
+ async function detectPatterns(ctx) {
636
+ const state = await ctx.stateManager.read();
637
+ const newPatterns = [];
638
+ const commitMessages = getCommitMessages(ctx.directory);
639
+ const commitPatterns = detectCommitPatterns(commitMessages);
640
+ for (const [msg, count] of commitPatterns) {
641
+ if (count >= 3) {
642
+ const existing = state.patterns.find((p) => p.type === "commit" && p.description === msg);
643
+ if (!existing) {
644
+ newPatterns.push({
645
+ id: crypto.randomUUID(),
646
+ type: "commit",
647
+ description: msg,
648
+ count,
649
+ firstSeen: Date.now(),
650
+ lastSeen: Date.now(),
651
+ surfaced: false,
652
+ examples: []
653
+ });
654
+ }
655
+ }
656
+ }
657
+ const fileStats = state.statistics.filesModified;
658
+ for (const [file, count] of Object.entries(fileStats)) {
659
+ if (count >= 5) {
660
+ const existing = state.patterns.find((p) => p.type === "file" && p.description === file);
661
+ if (!existing) {
662
+ newPatterns.push({
663
+ id: crypto.randomUUID(),
664
+ type: "file",
665
+ description: file,
666
+ count,
667
+ firstSeen: Date.now(),
668
+ lastSeen: Date.now(),
669
+ surfaced: false,
670
+ examples: []
671
+ });
672
+ }
673
+ }
674
+ }
675
+ return newPatterns;
676
+ }
677
+ async function surfacePatterns(ctx) {
678
+ const state = await ctx.stateManager.read();
679
+ const suggestions = [];
680
+ for (const pattern of state.patterns) {
681
+ if (pattern.surfaced) continue;
682
+ if (pattern.count < state.preferences.minPatternCount) continue;
683
+ let suggestion = "";
684
+ switch (pattern.type) {
685
+ case "commit":
686
+ suggestion = ctx.i18n.t("suggest.commit", {
687
+ pattern: pattern.description,
688
+ count: pattern.count
689
+ });
690
+ break;
691
+ case "file":
692
+ suggestion = ctx.i18n.t("suggest.file", {
693
+ pattern: pattern.description,
694
+ count: pattern.count
695
+ });
696
+ break;
697
+ case "tool":
698
+ suggestion = ctx.i18n.t("suggest.tool", {
699
+ pattern: pattern.description,
700
+ count: pattern.count
701
+ });
702
+ break;
703
+ case "sequence":
704
+ suggestion = ctx.i18n.t("suggest.sequence", {
705
+ pattern: pattern.description,
706
+ count: pattern.count
707
+ });
708
+ break;
709
+ }
710
+ suggestions.push(suggestion);
711
+ }
712
+ return suggestions;
713
+ }
714
+
715
+ // src/state.ts
716
+ import { existsSync as existsSync2 } from "fs";
717
+ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
718
+ import { join as join2 } from "path";
719
+ var createDefaultState = (projectName) => ({
720
+ version: "0.1.0",
721
+ project: {
722
+ name: projectName,
723
+ creatorLevel: null,
724
+ firstSession: Date.now(),
725
+ stack: [],
726
+ focus: void 0
727
+ },
728
+ journey: {
729
+ observations: [],
730
+ milestones: [],
731
+ sessionCount: 0,
732
+ lastSession: null
733
+ },
734
+ patterns: [],
735
+ evolution: {
736
+ capabilities: [],
737
+ lastEvolution: null,
738
+ pendingSuggestions: []
739
+ },
740
+ preferences: {
741
+ suggestionEnabled: true,
742
+ learningEnabled: true,
743
+ minPatternCount: 3
744
+ },
745
+ statistics: {
746
+ totalSessions: 0,
747
+ totalToolCalls: 0,
748
+ filesModified: {},
749
+ lastSessionId: null
750
+ }
751
+ });
752
+ var StateManager = class {
753
+ mimicDir;
754
+ statePath;
755
+ sessionsDir;
756
+ projectName;
757
+ constructor(directory) {
758
+ this.mimicDir = join2(directory, ".opencode", "mimic");
759
+ this.statePath = join2(this.mimicDir, "state.json");
760
+ this.sessionsDir = join2(this.mimicDir, "sessions");
761
+ this.projectName = directory.split("/").pop() || "unknown";
762
+ }
763
+ async initialize() {
764
+ if (!existsSync2(this.mimicDir)) {
765
+ await mkdir(this.mimicDir, { recursive: true });
766
+ }
767
+ if (!existsSync2(this.sessionsDir)) {
768
+ await mkdir(this.sessionsDir, { recursive: true });
769
+ }
770
+ if (!existsSync2(this.statePath)) {
771
+ await this.save(createDefaultState(this.projectName));
772
+ }
773
+ }
774
+ async read() {
775
+ return JSON.parse(await readFile2(this.statePath, "utf-8"));
776
+ }
777
+ async save(state) {
778
+ await writeFile(this.statePath, JSON.stringify(state, null, 2));
779
+ }
780
+ async addObservation(observation) {
781
+ const state = await this.read();
782
+ state.journey.observations.push({
783
+ observation,
784
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
785
+ });
786
+ if (state.journey.observations.length > 100) {
787
+ state.journey.observations = state.journey.observations.slice(-100);
788
+ }
789
+ await this.save(state);
790
+ }
791
+ async addMilestone(milestone) {
792
+ const state = await this.read();
793
+ state.journey.milestones.push({
794
+ milestone,
795
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
796
+ });
797
+ await this.save(state);
798
+ }
799
+ async saveSession(sessionId, data) {
800
+ await writeFile(join2(this.sessionsDir, `${sessionId}.json`), JSON.stringify(data, null, 2));
801
+ }
802
+ getSessionsDir() {
803
+ return this.sessionsDir;
804
+ }
805
+ getProjectName() {
806
+ return this.projectName;
807
+ }
808
+ getStatePath() {
809
+ return this.statePath;
810
+ }
811
+ };
812
+
813
+ // src/tools.ts
814
+ import { readdir, writeFile as writeFile3 } from "fs/promises";
815
+ import { tool } from "@opencode-ai/plugin";
816
+ import { format as format2 } from "date-fns";
817
+
818
+ // src/evolution.ts
819
+ import { existsSync as existsSync3 } from "fs";
820
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
821
+ import { join as join3 } from "path";
822
+ function generateToolCode(name, description, pattern) {
823
+ const funcName = name.replace(/-/g, "_");
824
+ return `// \u{1F4E6} Auto-generated by Mimic
825
+ // Pattern: ${pattern.description} (${pattern.count}x)
826
+ // Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
827
+
828
+ export const ${funcName} = (plugin) => ({
829
+ tool: {
830
+ "${name}": {
831
+ description: "${description}",
832
+ args: {},
833
+ async execute() {
834
+ // TODO: Implement your logic here
835
+ // This was generated from pattern: ${pattern.description}
836
+ return "\u{1F4E6} ${name} executed!";
837
+ },
838
+ },
839
+ },
840
+ });
841
+
842
+ export default ${funcName};
843
+ `;
844
+ }
845
+ function generateHookCode(name, _description, pattern) {
846
+ const filename = pattern.description;
847
+ return `// \u{1F4E6} Auto-generated by Mimic
848
+ // Pattern: ${pattern.description} (${pattern.count}x)
849
+ // Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
850
+
851
+ export const ${name.replace(/-/g, "_")} = (plugin) => ({
852
+ async event({ event }) {
853
+ if (event.type === "file.edited") {
854
+ const filename = event.properties?.filename;
855
+ if (filename?.includes("${filename}")) {
856
+ console.log("\u{1F4E6} [Mimic] Detected change in watched file: ${filename}");
857
+ // TODO: Add your custom logic here
858
+ }
859
+ }
860
+ },
861
+ });
862
+
863
+ export default ${name.replace(/-/g, "_")};
864
+ `;
865
+ }
866
+ function generateSkillCode(name, description, pattern) {
867
+ return `// \u{1F4E6} Auto-generated by Mimic
868
+ // Pattern: ${pattern.description} (${pattern.count}x)
869
+ // Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
870
+
871
+ export const ${name.replace(/-/g, "_")} = (plugin) => ({
872
+ async event({ event }) {
873
+ // Auto-triggered skill: ${description}
874
+ if (event.type === "session.created") {
875
+ console.log("\u{1F4E6} [Mimic] Skill ${name} activated");
876
+ // TODO: Implement automated behavior
877
+ }
878
+ },
879
+ });
880
+
881
+ export default ${name.replace(/-/g, "_")};
882
+ `;
883
+ }
884
+ function generateMcpConfig(name, _description, pattern) {
885
+ return {
886
+ [name]: {
887
+ type: "local",
888
+ command: ["echo", `MCP server for ${pattern.description}`],
889
+ enabled: false
890
+ }
891
+ };
892
+ }
893
+ function generateAgentMarkdown(name, description, pattern) {
894
+ return `---
895
+ description: ${description}
896
+ mode: subagent
897
+ tools:
898
+ read: true
899
+ glob: true
900
+ grep: true
901
+ bash: false
902
+ edit: false
903
+ ---
904
+
905
+ # \u{1F4E6} ${name}
906
+
907
+ Auto-generated by Mimic from pattern: ${pattern.description} (${pattern.count}x)
908
+
909
+ ## Focus Area
910
+
911
+ This agent specializes in tasks related to: ${pattern.description}
912
+
913
+ ## Context
914
+
915
+ - Pattern detected: ${pattern.type}
916
+ - Usage count: ${pattern.count}
917
+ - First seen: ${new Date(pattern.firstSeen).toISOString()}
918
+
919
+ ## How To Help
920
+
921
+ Analyze and assist with tasks matching this pattern.
922
+ Provide focused, expert guidance in this specific area.
923
+
924
+ ## Remember
925
+
926
+ - Stay focused on the pattern's domain
927
+ - Leverage the observed usage patterns
928
+ - Adapt to the user's workflow
929
+ `;
930
+ }
931
+ async function readOpencodeConfig(configPath) {
932
+ if (!existsSync3(configPath)) return {};
933
+ try {
934
+ const parsed = JSON.parse(await readFile3(configPath, "utf-8"));
935
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
936
+ return {};
937
+ }
938
+ return parsed;
939
+ } catch {
940
+ return {};
941
+ }
942
+ }
943
+ async function buildMcpEvolution(ctx, suggestion) {
944
+ const opencodeDir = join3(ctx.directory, ".opencode");
945
+ if (!existsSync3(opencodeDir)) {
946
+ await mkdir2(opencodeDir, { recursive: true });
947
+ }
948
+ const configPath = join3(ctx.directory, "opencode.json");
949
+ const config = await readOpencodeConfig(configPath);
950
+ const mcpConfig = generateMcpConfig(
951
+ suggestion.name,
952
+ suggestion.description,
953
+ suggestion.pattern
954
+ );
955
+ const mcp = { ...config.mcp || {}, ...mcpConfig };
956
+ const content = JSON.stringify({ ...config, mcp }, null, 2);
957
+ return { filePath: configPath, content };
958
+ }
959
+ async function buildAgentEvolution(ctx, suggestion) {
960
+ const agentsDir = join3(ctx.directory, ".opencode", "agents");
961
+ if (!existsSync3(agentsDir)) {
962
+ await mkdir2(agentsDir, { recursive: true });
963
+ }
964
+ return {
965
+ content: generateAgentMarkdown(suggestion.name, suggestion.description, suggestion.pattern),
966
+ filePath: join3(agentsDir, `${suggestion.name}.md`)
967
+ };
968
+ }
969
+ function buildPluginContent(suggestion) {
970
+ switch (suggestion.type) {
971
+ case "command":
972
+ case "shortcut":
973
+ return generateToolCode(suggestion.name, suggestion.description, suggestion.pattern);
974
+ case "hook":
975
+ return generateHookCode(suggestion.name, suggestion.description, suggestion.pattern);
976
+ case "skill":
977
+ return generateSkillCode(suggestion.name, suggestion.description, suggestion.pattern);
978
+ default:
979
+ return generateToolCode(suggestion.name, suggestion.description, suggestion.pattern);
980
+ }
981
+ }
982
+ async function buildPluginEvolution(ctx, suggestion) {
983
+ const pluginsDir = join3(ctx.directory, ".opencode", "plugins");
984
+ if (!existsSync3(pluginsDir)) {
985
+ await mkdir2(pluginsDir, { recursive: true });
986
+ }
987
+ return {
988
+ content: buildPluginContent(suggestion),
989
+ filePath: join3(pluginsDir, `${suggestion.name}.js`)
990
+ };
991
+ }
992
+ async function buildEvolutionOutput(ctx, suggestion) {
993
+ if (suggestion.type === "mcp") {
994
+ return buildMcpEvolution(ctx, suggestion);
995
+ }
996
+ if (suggestion.type === "agent") {
997
+ return buildAgentEvolution(ctx, suggestion);
998
+ }
999
+ return buildPluginEvolution(ctx, suggestion);
1000
+ }
1001
+ function createCapabilityFromSuggestion(suggestion) {
1002
+ return {
1003
+ id: crypto.randomUUID(),
1004
+ type: suggestion.type,
1005
+ name: suggestion.name,
1006
+ description: suggestion.description,
1007
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1008
+ fromPattern: suggestion.pattern.id
1009
+ };
1010
+ }
1011
+ function updateEvolutionState(state, capability, suggestion) {
1012
+ state.evolution.capabilities.push(capability);
1013
+ state.evolution.lastEvolution = (/* @__PURE__ */ new Date()).toISOString();
1014
+ const pattern = state.patterns.find((p) => p.id === suggestion.pattern.id);
1015
+ if (pattern) {
1016
+ pattern.surfaced = true;
1017
+ }
1018
+ }
1019
+ function suggestEvolution(pattern, ctx) {
1020
+ switch (pattern.type) {
1021
+ case "tool":
1022
+ if (pattern.count >= 10) {
1023
+ return {
1024
+ type: "shortcut",
1025
+ name: `quick-${pattern.description.toLowerCase().replace(/[^a-z0-9]/g, "-")}`,
1026
+ description: ctx.i18n.t("evolution.suggest.tool.description", {
1027
+ pattern: pattern.description
1028
+ }),
1029
+ reason: ctx.i18n.t("evolution.suggest.tool.reason", { count: pattern.count }),
1030
+ pattern
1031
+ };
1032
+ }
1033
+ break;
1034
+ case "file":
1035
+ if (pattern.count >= 5) {
1036
+ return {
1037
+ type: "hook",
1038
+ name: `watch-${pattern.description.split("/").pop()?.replace(/\./g, "-") || "file"}`,
1039
+ description: ctx.i18n.t("evolution.suggest.file.description", {
1040
+ pattern: pattern.description
1041
+ }),
1042
+ reason: ctx.i18n.t("evolution.suggest.file.reason", { count: pattern.count }),
1043
+ pattern
1044
+ };
1045
+ }
1046
+ break;
1047
+ case "commit":
1048
+ if (pattern.count >= 3) {
1049
+ return {
1050
+ type: "command",
1051
+ name: `commit-${pattern.description.slice(0, 20).replace(/\s+/g, "-").toLowerCase()}`,
1052
+ description: ctx.i18n.t("evolution.suggest.commit.description", {
1053
+ pattern: pattern.description
1054
+ }),
1055
+ reason: ctx.i18n.t("evolution.suggest.commit.reason", { count: pattern.count }),
1056
+ pattern
1057
+ };
1058
+ }
1059
+ break;
1060
+ case "sequence":
1061
+ if (pattern.count >= 5) {
1062
+ return {
1063
+ type: "agent",
1064
+ name: `${pattern.description.slice(0, 15).replace(/\s+/g, "-").toLowerCase()}-specialist`,
1065
+ description: ctx.i18n.t("evolution.suggest.sequence.agent.description", {
1066
+ pattern: pattern.description
1067
+ }),
1068
+ reason: ctx.i18n.t("evolution.suggest.sequence.agent.reason", { count: pattern.count }),
1069
+ pattern
1070
+ };
1071
+ }
1072
+ if (pattern.count >= 3) {
1073
+ return {
1074
+ type: "skill",
1075
+ name: `auto-${pattern.description.slice(0, 15).replace(/\s+/g, "-").toLowerCase()}`,
1076
+ description: ctx.i18n.t("evolution.suggest.sequence.skill.description", {
1077
+ pattern: pattern.description
1078
+ }),
1079
+ reason: ctx.i18n.t("evolution.suggest.sequence.skill.reason", { count: pattern.count }),
1080
+ pattern
1081
+ };
1082
+ }
1083
+ break;
1084
+ }
1085
+ return null;
1086
+ }
1087
+ async function getEvolutionSuggestions(ctx) {
1088
+ const state = await ctx.stateManager.read();
1089
+ const suggestions = [];
1090
+ for (const pattern of state.patterns) {
1091
+ if (pattern.surfaced) continue;
1092
+ const suggestion = suggestEvolution(pattern, ctx);
1093
+ if (suggestion) {
1094
+ suggestions.push(suggestion);
1095
+ }
1096
+ }
1097
+ return suggestions;
1098
+ }
1099
+ async function evolveCapability(ctx, suggestion) {
1100
+ const state = await ctx.stateManager.read();
1101
+ const { filePath, content } = await buildEvolutionOutput(ctx, suggestion);
1102
+ await writeFile2(filePath, content, "utf-8");
1103
+ const capability = createCapabilityFromSuggestion(suggestion);
1104
+ updateEvolutionState(state, capability, suggestion);
1105
+ await ctx.stateManager.save(state);
1106
+ await ctx.stateManager.addMilestone(
1107
+ ctx.i18n.t("milestone.evolved", {
1108
+ name: capability.name,
1109
+ type: formatCapabilityType(ctx.i18n, capability.type)
1110
+ })
1111
+ );
1112
+ return { capability, filePath };
1113
+ }
1114
+ function formatEvolutionResult(ctx, capability, filePath) {
1115
+ const typeLabel = formatCapabilityType(ctx.i18n, capability.type);
1116
+ let result = `### \u2728 ${capability.name}
1117
+
1118
+ `;
1119
+ result += `**${ctx.i18n.t("evolution.result.type")}**: ${typeLabel}
1120
+ `;
1121
+ result += `**${ctx.i18n.t("evolution.result.description")}**: ${capability.description}
1122
+ `;
1123
+ result += `**${ctx.i18n.t("evolution.result.file")}**: \`${filePath}\`
1124
+
1125
+ `;
1126
+ result += `*${ctx.i18n.t("evolution.result.restart", { type: typeLabel })}*
1127
+
1128
+ `;
1129
+ switch (capability.type) {
1130
+ case "command":
1131
+ case "shortcut":
1132
+ result += `${ctx.i18n.t("evolution.result.command", { name: capability.name })}
1133
+ `;
1134
+ break;
1135
+ case "hook":
1136
+ result += `${ctx.i18n.t("evolution.result.hook")}
1137
+ `;
1138
+ break;
1139
+ case "skill":
1140
+ result += `${ctx.i18n.t("evolution.result.skill")}
1141
+ `;
1142
+ break;
1143
+ case "agent":
1144
+ result += `${ctx.i18n.t("evolution.result.agent", { name: capability.name })}
1145
+ `;
1146
+ break;
1147
+ case "mcp":
1148
+ result += `${ctx.i18n.t("evolution.result.mcp", { name: capability.name })}
1149
+ `;
1150
+ break;
1151
+ }
1152
+ return result;
1153
+ }
1154
+
1155
+ // src/level.ts
1156
+ var LEVEL_CONFIGS = {
1157
+ technical: {
1158
+ greetingStyle: "minimal",
1159
+ detailLevel: "high",
1160
+ useEmoji: false,
1161
+ technicalTerms: true
1162
+ },
1163
+ "semi-technical": {
1164
+ greetingStyle: "casual",
1165
+ detailLevel: "medium",
1166
+ useEmoji: false,
1167
+ technicalTerms: true
1168
+ },
1169
+ "non-technical": {
1170
+ greetingStyle: "formal",
1171
+ detailLevel: "low",
1172
+ useEmoji: true,
1173
+ technicalTerms: false
1174
+ },
1175
+ chaotic: {
1176
+ greetingStyle: "chaotic",
1177
+ detailLevel: "medium",
1178
+ useEmoji: true,
1179
+ technicalTerms: true
1180
+ }
1181
+ };
1182
+ function getLevelConfig(level) {
1183
+ return LEVEL_CONFIGS[level ?? "technical"];
1184
+ }
1185
+
1186
+ // src/tools.ts
1187
+ function createTools(stateManager, directory, toolCalls, i18n) {
1188
+ const baseI18n = i18n ?? createI18n(resolveLanguage(null));
1189
+ const i18nPromise = i18n ? Promise.resolve(i18n) : loadMimicConfig().then((config) => createI18n(resolveLanguage(config))).catch(() => createI18n(resolveLanguage(null)));
1190
+ return {
1191
+ "mimic:init": tool({
1192
+ description: baseI18n.t("tool.init.description"),
1193
+ args: {},
1194
+ async execute() {
1195
+ const i18n2 = await i18nPromise;
1196
+ const state = await stateManager.read();
1197
+ const isFirstTime = state.journey.sessionCount <= 1;
1198
+ if (isFirstTime) {
1199
+ return i18n2.t("init.first_time", { project: state.project.name });
1200
+ }
1201
+ const timeSince = analyzeTimeSinceLastSession(state.journey.lastSession);
1202
+ const recentObs = state.journey.observations.slice(-3);
1203
+ let greeting = `${i18n2.t("init.returning.header")}
1204
+
1205
+ `;
1206
+ greeting += `${i18n2.t("init.returning.welcome", { project: state.project.name })}
1207
+
1208
+ `;
1209
+ greeting += `${i18n2.t("init.returning.stats", {
1210
+ sessions: state.journey.sessionCount,
1211
+ patterns: state.patterns.length
1212
+ })}
1213
+
1214
+ `;
1215
+ if (timeSince === "long-break") {
1216
+ greeting += `${i18n2.t("init.returning.long_break")}
1217
+
1218
+ `;
1219
+ }
1220
+ if (recentObs.length > 0) {
1221
+ greeting += `${i18n2.t("init.returning.recent_obs_title")}
1222
+ `;
1223
+ for (const o of recentObs) {
1224
+ greeting += `- ${o.observation}
1225
+ `;
1226
+ }
1227
+ }
1228
+ return greeting;
1229
+ }
1230
+ }),
1231
+ "mimic:status": tool({
1232
+ description: baseI18n.t("tool.status.description"),
1233
+ args: {},
1234
+ async execute() {
1235
+ const i18n2 = await i18nPromise;
1236
+ const ctx = { stateManager, directory, i18n: i18n2 };
1237
+ const state = await stateManager.read();
1238
+ const recentFiles = getRecentlyModifiedFiles(directory);
1239
+ const gitHistory = getGitHistory(directory, 5);
1240
+ let output = `${i18n2.t("status.title", { project: state.project.name })}
1241
+
1242
+ `;
1243
+ output += `${i18n2.t("status.session", { count: state.journey.sessionCount })}
1244
+ `;
1245
+ output += `${i18n2.t("status.patterns", {
1246
+ total: state.patterns.length,
1247
+ surfaced: state.patterns.filter((p) => p.surfaced).length
1248
+ })}
1249
+ `;
1250
+ output += `${i18n2.t("status.tool_calls", { count: toolCalls.length })}
1251
+
1252
+ `;
1253
+ if (recentFiles.length > 0) {
1254
+ output += `${i18n2.t("status.recent_files")}
1255
+ `;
1256
+ for (const f of recentFiles.slice(0, 5)) {
1257
+ output += `- ${f}
1258
+ `;
1259
+ }
1260
+ output += "\n";
1261
+ }
1262
+ if (gitHistory.length > 0) {
1263
+ output += `${i18n2.t("status.recent_commits")}
1264
+ `;
1265
+ for (const c of gitHistory) {
1266
+ output += `- ${c}
1267
+ `;
1268
+ }
1269
+ }
1270
+ const suggestions = await surfacePatterns(ctx);
1271
+ if (suggestions.length > 0) {
1272
+ output += `
1273
+ ${i18n2.t("status.suggestions")}
1274
+ `;
1275
+ for (const s of suggestions) {
1276
+ output += `- ${s}
1277
+ `;
1278
+ }
1279
+ }
1280
+ return output;
1281
+ }
1282
+ }),
1283
+ "mimic:journey": tool({
1284
+ description: baseI18n.t("tool.journey.description"),
1285
+ args: {},
1286
+ async execute() {
1287
+ const i18n2 = await i18nPromise;
1288
+ const ctx = { stateManager, directory, i18n: i18n2 };
1289
+ const state = await stateManager.read();
1290
+ const gitHistory = getGitHistory(directory, 10);
1291
+ return formatJourney(ctx, state, gitHistory);
1292
+ }
1293
+ }),
1294
+ "mimic:patterns": tool({
1295
+ description: baseI18n.t("tool.patterns.description"),
1296
+ args: {},
1297
+ async execute() {
1298
+ const i18n2 = await i18nPromise;
1299
+ const state = await stateManager.read();
1300
+ if (state.patterns.length === 0) {
1301
+ return i18n2.t("patterns.none");
1302
+ }
1303
+ let output = `${i18n2.t("patterns.title")}
1304
+
1305
+ `;
1306
+ output += `${i18n2.t("patterns.total", { count: state.patterns.length })}
1307
+
1308
+ `;
1309
+ const byType = /* @__PURE__ */ new Map();
1310
+ for (const p of state.patterns) {
1311
+ const list = byType.get(p.type) || [];
1312
+ list.push(p);
1313
+ byType.set(p.type, list);
1314
+ }
1315
+ for (const [type, patterns] of byType) {
1316
+ output += `${i18n2.t("patterns.section", {
1317
+ type: formatPatternType(i18n2, type)
1318
+ })}
1319
+ `;
1320
+ for (const p of patterns.slice(0, 10)) {
1321
+ const status = p.surfaced ? "\u2713" : "\u25CB";
1322
+ output += `${status} **${p.description}** (${p.count}x)
1323
+ `;
1324
+ }
1325
+ output += "\n";
1326
+ }
1327
+ return output;
1328
+ }
1329
+ }),
1330
+ "mimic:observe": tool({
1331
+ description: baseI18n.t("tool.observe.description"),
1332
+ args: {
1333
+ observation: tool.schema.string().describe(baseI18n.t("tool.observe.args.observation"))
1334
+ },
1335
+ async execute(args) {
1336
+ const i18n2 = await i18nPromise;
1337
+ await stateManager.addObservation(args.observation);
1338
+ return i18n2.t("observe.recorded", { observation: args.observation });
1339
+ }
1340
+ }),
1341
+ "mimic:milestone": tool({
1342
+ description: baseI18n.t("tool.milestone.description"),
1343
+ args: {
1344
+ milestone: tool.schema.string().describe(baseI18n.t("tool.milestone.args.milestone"))
1345
+ },
1346
+ async execute(args) {
1347
+ const i18n2 = await i18nPromise;
1348
+ await stateManager.addMilestone(args.milestone);
1349
+ return i18n2.t("milestone.recorded", { milestone: args.milestone });
1350
+ }
1351
+ }),
1352
+ "mimic:stats": tool({
1353
+ description: baseI18n.t("tool.stats.description"),
1354
+ args: {},
1355
+ async execute() {
1356
+ const i18n2 = await i18nPromise;
1357
+ const state = await stateManager.read();
1358
+ const sessionFiles = await readdir(stateManager.getSessionsDir()).catch(() => []);
1359
+ return `## ${i18n2.t("stats.title")}
1360
+
1361
+ - **${i18n2.t("stats.version")}**: ${state.version}
1362
+ - **${i18n2.t("stats.total_sessions")}**: ${state.statistics.totalSessions}
1363
+ - **${i18n2.t("stats.total_tool_calls")}**: ${state.statistics.totalToolCalls}
1364
+ - **${i18n2.t("stats.patterns_detected")}**: ${state.patterns.length}
1365
+ - **${i18n2.t("stats.milestones")}**: ${state.journey.milestones.length}
1366
+ - **${i18n2.t("stats.observations")}**: ${state.journey.observations.length}
1367
+ - **${i18n2.t("stats.session_records")}**: ${sessionFiles.length}
1368
+ - **${i18n2.t("stats.first_session")}**: ${format2(state.project.firstSession, "yyyy-MM-dd HH:mm:ss")}
1369
+ - **${i18n2.t("stats.learning_enabled")}**: ${state.preferences.learningEnabled}
1370
+ - **${i18n2.t("stats.suggestions_enabled")}**: ${state.preferences.suggestionEnabled}`;
1371
+ }
1372
+ }),
1373
+ "mimic:configure": tool({
1374
+ description: baseI18n.t("tool.configure.description"),
1375
+ args: {
1376
+ learningEnabled: tool.schema.boolean().optional().describe(baseI18n.t("tool.configure.args.learningEnabled")),
1377
+ suggestionEnabled: tool.schema.boolean().optional().describe(baseI18n.t("tool.configure.args.suggestionEnabled")),
1378
+ minPatternCount: tool.schema.number().optional().describe(baseI18n.t("tool.configure.args.minPatternCount"))
1379
+ },
1380
+ async execute(args) {
1381
+ const i18n2 = await i18nPromise;
1382
+ const state = await stateManager.read();
1383
+ if (args.learningEnabled !== void 0) {
1384
+ state.preferences.learningEnabled = args.learningEnabled;
1385
+ }
1386
+ if (args.suggestionEnabled !== void 0) {
1387
+ state.preferences.suggestionEnabled = args.suggestionEnabled;
1388
+ }
1389
+ if (args.minPatternCount !== void 0) {
1390
+ state.preferences.minPatternCount = args.minPatternCount;
1391
+ }
1392
+ await stateManager.save(state);
1393
+ return `${i18n2.t("configure.updated")}
1394
+ ${JSON.stringify(state.preferences, null, 2)}`;
1395
+ }
1396
+ }),
1397
+ "mimic:surface": tool({
1398
+ description: baseI18n.t("tool.surface.description"),
1399
+ args: {
1400
+ patternId: tool.schema.string().describe(baseI18n.t("tool.surface.args.patternId"))
1401
+ },
1402
+ async execute(args) {
1403
+ const i18n2 = await i18nPromise;
1404
+ const state = await stateManager.read();
1405
+ const pattern = state.patterns.find((p) => p.id === args.patternId);
1406
+ if (!pattern) {
1407
+ return i18n2.t("surface.not_found", { id: args.patternId });
1408
+ }
1409
+ pattern.surfaced = true;
1410
+ await stateManager.save(state);
1411
+ return i18n2.t("surface.marked", { description: pattern.description });
1412
+ }
1413
+ }),
1414
+ "mimic:reset": tool({
1415
+ description: baseI18n.t("tool.reset.description"),
1416
+ args: {
1417
+ confirm: tool.schema.boolean().describe(baseI18n.t("tool.reset.args.confirm"))
1418
+ },
1419
+ async execute(args) {
1420
+ const i18n2 = await i18nPromise;
1421
+ if (!args.confirm) {
1422
+ return i18n2.t("reset.cancelled");
1423
+ }
1424
+ await writeFile3(
1425
+ stateManager.getStatePath(),
1426
+ JSON.stringify(createDefaultState(stateManager.getProjectName()), null, 2)
1427
+ );
1428
+ return i18n2.t("reset.done");
1429
+ }
1430
+ }),
1431
+ "mimic:grow": tool({
1432
+ description: baseI18n.t("tool.grow.description"),
1433
+ args: {},
1434
+ async execute() {
1435
+ const i18n2 = await i18nPromise;
1436
+ const ctx = { stateManager, directory, i18n: i18n2 };
1437
+ const state = await stateManager.read();
1438
+ const gitHistory = getGitHistory(directory, 20);
1439
+ const recentFiles = getRecentlyModifiedFiles(directory);
1440
+ return formatGrowAnalysis(ctx, state, gitHistory, recentFiles);
1441
+ }
1442
+ }),
1443
+ "mimic:evolve": tool({
1444
+ description: baseI18n.t("tool.evolve.description"),
1445
+ args: {
1446
+ accept: tool.schema.string().optional().describe(baseI18n.t("tool.evolve.args.accept"))
1447
+ },
1448
+ async execute(args) {
1449
+ const i18n2 = await i18nPromise;
1450
+ const ctx = { stateManager, directory, i18n: i18n2 };
1451
+ if (args.accept) {
1452
+ const suggestions2 = await getEvolutionSuggestions(ctx);
1453
+ const suggestion = suggestions2.find((s) => s.pattern.id === args.accept);
1454
+ if (!suggestion) {
1455
+ return i18n2.t("evolve.no_pattern", { id: args.accept });
1456
+ }
1457
+ const { capability, filePath } = await evolveCapability(ctx, suggestion);
1458
+ return `${i18n2.t("evolve.absorbed_header")}
1459
+
1460
+ ${formatEvolutionResult(
1461
+ ctx,
1462
+ capability,
1463
+ filePath
1464
+ )}`;
1465
+ }
1466
+ const suggestions = await getEvolutionSuggestions(ctx);
1467
+ if (suggestions.length === 0) {
1468
+ return i18n2.t("evolve.empty");
1469
+ }
1470
+ let output = `${i18n2.t("evolve.menu_title")}
1471
+
1472
+ `;
1473
+ output += `${i18n2.t("evolve.menu_intro")}
1474
+
1475
+ `;
1476
+ for (const s of suggestions) {
1477
+ output += `### \u2728 ${s.name}
1478
+ `;
1479
+ output += `- **${i18n2.t("evolve.menu_type")}**: ${formatCapabilityType(i18n2, s.type)}
1480
+ `;
1481
+ output += `- **${i18n2.t("evolve.menu_reason")}**: ${s.reason}
1482
+ `;
1483
+ output += `- **${i18n2.t("evolve.menu_pattern_id")}**: \`${s.pattern.id}\`
1484
+
1485
+ `;
1486
+ }
1487
+ output += `
1488
+ ${i18n2.t("evolve.menu_footer")}`;
1489
+ return output;
1490
+ }
1491
+ }),
1492
+ "mimic:level": tool({
1493
+ description: baseI18n.t("tool.level.description"),
1494
+ args: {
1495
+ level: tool.schema.enum(["technical", "semi-technical", "non-technical", "chaotic"]).describe(baseI18n.t("tool.level.args.level"))
1496
+ },
1497
+ async execute(args) {
1498
+ const i18n2 = await i18nPromise;
1499
+ const state = await stateManager.read();
1500
+ state.project.creatorLevel = args.level;
1501
+ await stateManager.save(state);
1502
+ const config = getLevelConfig(args.level);
1503
+ return i18n2.t("level.set", {
1504
+ level: formatLevelLabel(i18n2, args.level),
1505
+ style: formatGreetingStyle(i18n2, config.greetingStyle),
1506
+ detail: formatDetailLevel(i18n2, config.detailLevel)
1507
+ });
1508
+ }
1509
+ }),
1510
+ "mimic:focus": tool({
1511
+ description: baseI18n.t("tool.focus.description"),
1512
+ args: {
1513
+ focus: tool.schema.string().optional().describe(baseI18n.t("tool.focus.args.focus")),
1514
+ stack: tool.schema.string().optional().describe(baseI18n.t("tool.focus.args.stack"))
1515
+ },
1516
+ async execute(args) {
1517
+ const i18n2 = await i18nPromise;
1518
+ const state = await stateManager.read();
1519
+ if (args.focus) {
1520
+ state.project.focus = args.focus;
1521
+ await stateManager.addObservation(i18n2.t("obs.focus_changed", { focus: args.focus }));
1522
+ }
1523
+ if (args.stack) {
1524
+ state.project.stack = args.stack.split(",").map((s) => s.trim());
1525
+ }
1526
+ await stateManager.save(state);
1527
+ let output = `${i18n2.t("focus.updated")}
1528
+ `;
1529
+ if (state.project.focus)
1530
+ output += `- **${i18n2.t("focus.focus_label")}**: ${state.project.focus}
1531
+ `;
1532
+ if (state.project.stack?.length)
1533
+ output += `- **${i18n2.t("focus.stack_label")}**: ${state.project.stack.join(", ")}
1534
+ `;
1535
+ return output;
1536
+ }
1537
+ }),
1538
+ "mimic:mcp-search": tool({
1539
+ description: baseI18n.t("tool.mcp_search.description"),
1540
+ args: {
1541
+ query: tool.schema.string().describe(baseI18n.t("tool.mcp_search.args.query"))
1542
+ },
1543
+ async execute(args) {
1544
+ const i18n2 = await i18nPromise;
1545
+ const searchUrl = `https://mcpmarket.com/search?q=${encodeURIComponent(args.query)}`;
1546
+ const popular = [
1547
+ {
1548
+ name: "context7",
1549
+ desc: i18n2.t("mcp_search.desc.context7"),
1550
+ url: "https://mcp.context7.com/mcp"
1551
+ },
1552
+ {
1553
+ name: "github",
1554
+ desc: i18n2.t("mcp_search.desc.github"),
1555
+ url: "https://mcp.github.com"
1556
+ },
1557
+ {
1558
+ name: "supabase",
1559
+ desc: i18n2.t("mcp_search.desc.supabase"),
1560
+ url: "https://mcp.supabase.com"
1561
+ },
1562
+ { name: "playwright", desc: i18n2.t("mcp_search.desc.playwright") },
1563
+ { name: "firecrawl", desc: i18n2.t("mcp_search.desc.firecrawl") }
1564
+ ];
1565
+ const popularLines = popular.map(
1566
+ (server) => server.url ? `- **${server.name}** - ${server.desc}: \`${server.url}\`` : `- **${server.name}** - ${server.desc}`
1567
+ ).join("\n");
1568
+ return `${i18n2.t("mcp_search.header", {
1569
+ query: args.query,
1570
+ url: searchUrl
1571
+ })}
1572
+
1573
+ ${i18n2.t("mcp_search.popular")}
1574
+ ${popularLines}
1575
+
1576
+ ${i18n2.t("mcp_search.add")}`;
1577
+ }
1578
+ }),
1579
+ "mimic:mcp": tool({
1580
+ description: baseI18n.t("tool.mcp.description"),
1581
+ args: {
1582
+ name: tool.schema.string().describe(baseI18n.t("tool.mcp.args.name")),
1583
+ url: tool.schema.string().optional().describe(baseI18n.t("tool.mcp.args.url")),
1584
+ command: tool.schema.string().optional().describe(baseI18n.t("tool.mcp.args.command"))
1585
+ },
1586
+ async execute(args) {
1587
+ const i18n2 = await i18nPromise;
1588
+ const { existsSync: existsSync4 } = await import("fs");
1589
+ const { readFile: readFile4, writeFile: fsWriteFile, mkdir: mkdir3 } = await import("fs/promises");
1590
+ const { join: join4 } = await import("path");
1591
+ const opencodeDir = join4(directory, ".opencode");
1592
+ if (!existsSync4(opencodeDir)) {
1593
+ await mkdir3(opencodeDir, { recursive: true });
1594
+ }
1595
+ const configPath = join4(directory, "opencode.json");
1596
+ let config = {};
1597
+ if (existsSync4(configPath)) {
1598
+ try {
1599
+ config = JSON.parse(await readFile4(configPath, "utf-8"));
1600
+ } catch {
1601
+ config = {};
1602
+ }
1603
+ }
1604
+ const mcpEntry = {};
1605
+ if (args.url) {
1606
+ mcpEntry.type = "remote";
1607
+ mcpEntry.url = args.url;
1608
+ } else if (args.command) {
1609
+ mcpEntry.type = "local";
1610
+ mcpEntry.command = args.command.split(",").map((s) => s.trim());
1611
+ } else {
1612
+ return i18n2.t("mcp.need_url_or_command");
1613
+ }
1614
+ mcpEntry.enabled = true;
1615
+ config.mcp = { ...config.mcp || {}, [args.name]: mcpEntry };
1616
+ await fsWriteFile(configPath, JSON.stringify(config, null, 2));
1617
+ await stateManager.addMilestone(i18n2.t("milestone.mcp_added", { name: args.name }));
1618
+ return i18n2.t("mcp.added", { name: args.name });
1619
+ }
1620
+ }),
1621
+ "mimic:capabilities": tool({
1622
+ description: baseI18n.t("tool.capabilities.description"),
1623
+ args: {},
1624
+ async execute() {
1625
+ const i18n2 = await i18nPromise;
1626
+ const state = await stateManager.read();
1627
+ if (state.evolution.capabilities.length === 0) {
1628
+ return i18n2.t("capabilities.empty");
1629
+ }
1630
+ let output = `${i18n2.t("capabilities.title")}
1631
+
1632
+ `;
1633
+ output += `${i18n2.t("capabilities.intro")}
1634
+
1635
+ `;
1636
+ for (const cap of state.evolution.capabilities) {
1637
+ output += `### \u2728 ${cap.name}
1638
+ `;
1639
+ output += `- **${i18n2.t("capabilities.type")}**: ${formatCapabilityType(
1640
+ i18n2,
1641
+ cap.type
1642
+ )}
1643
+ `;
1644
+ output += `- **${i18n2.t("capabilities.description")}**: ${cap.description}
1645
+ `;
1646
+ output += `- **${i18n2.t("capabilities.consumed")}**: ${format2(
1647
+ new Date(cap.createdAt),
1648
+ "yyyy-MM-dd"
1649
+ )}
1650
+
1651
+ `;
1652
+ }
1653
+ return output;
1654
+ }
1655
+ })
1656
+ };
1657
+ }
1658
+
1659
+ // src/index.ts
1660
+ var mimic = async ({ directory }) => {
1661
+ const stateManager = new StateManager(directory);
1662
+ await stateManager.initialize();
1663
+ const i18n = createI18n(resolveLanguage(await loadMimicConfig()));
1664
+ const ctx = { stateManager, directory, i18n };
1665
+ const sessionId = crypto.randomUUID();
1666
+ const sessionStartTime = Date.now();
1667
+ const toolCalls = [];
1668
+ const filesEdited = /* @__PURE__ */ new Set();
1669
+ const handleSessionCreated = async () => {
1670
+ const state = await stateManager.read();
1671
+ const timeSince = analyzeTimeSinceLastSession(state.journey.lastSession);
1672
+ state.statistics.totalSessions += 1;
1673
+ state.statistics.lastSessionId = sessionId;
1674
+ state.journey.sessionCount += 1;
1675
+ state.journey.lastSession = (/* @__PURE__ */ new Date()).toISOString();
1676
+ await stateManager.save(state);
1677
+ if (timeSince === "long-break") {
1678
+ await stateManager.addObservation(i18n.t("obs.returned_after_long_break"));
1679
+ }
1680
+ console.log(
1681
+ i18n.t("log.session_started", {
1682
+ sessions: state.journey.sessionCount,
1683
+ patterns: state.patterns.length
1684
+ })
1685
+ );
1686
+ };
1687
+ const handleSessionIdle = async () => {
1688
+ const newPatterns = await detectPatterns(ctx);
1689
+ if (newPatterns.length > 0) {
1690
+ const state = await stateManager.read();
1691
+ state.patterns.push(...newPatterns);
1692
+ await stateManager.save(state);
1693
+ }
1694
+ const suggestions = await surfacePatterns(ctx);
1695
+ for (const suggestion of suggestions) {
1696
+ console.log(`[Mimic] ${suggestion}`);
1697
+ }
1698
+ };
1699
+ const handleFileEdited = async (event) => {
1700
+ if (!("properties" in event)) return;
1701
+ const filename = event.properties?.filename;
1702
+ if (!filename) return;
1703
+ filesEdited.add(filename);
1704
+ const state = await stateManager.read();
1705
+ state.statistics.filesModified[filename] = (state.statistics.filesModified[filename] || 0) + 1;
1706
+ await stateManager.save(state);
1707
+ };
1708
+ return {
1709
+ async event({ event }) {
1710
+ switch (event.type) {
1711
+ case "session.created":
1712
+ await handleSessionCreated();
1713
+ return;
1714
+ case "session.idle":
1715
+ await handleSessionIdle();
1716
+ return;
1717
+ case "file.edited":
1718
+ await handleFileEdited(event);
1719
+ }
1720
+ },
1721
+ async "tool.execute.after"(input, _output) {
1722
+ const state = await stateManager.read();
1723
+ if (!state.preferences.learningEnabled) return;
1724
+ const toolCall = {
1725
+ tool: input.tool,
1726
+ callID: input.callID,
1727
+ timestamp: Date.now()
1728
+ };
1729
+ toolCalls.push(toolCall);
1730
+ state.statistics.totalToolCalls += 1;
1731
+ const toolPattern = input.tool;
1732
+ const existing = state.patterns.find(
1733
+ (p) => p.type === "tool" && p.description === toolPattern
1734
+ );
1735
+ if (existing) {
1736
+ existing.count += 1;
1737
+ existing.lastSeen = Date.now();
1738
+ } else {
1739
+ state.patterns.push({
1740
+ id: crypto.randomUUID(),
1741
+ type: "tool",
1742
+ description: toolPattern,
1743
+ count: 1,
1744
+ firstSeen: Date.now(),
1745
+ lastSeen: Date.now(),
1746
+ surfaced: false,
1747
+ examples: [toolCall]
1748
+ });
1749
+ }
1750
+ await stateManager.save(state);
1751
+ },
1752
+ async stop() {
1753
+ const sessionDuration = Date.now() - sessionStartTime;
1754
+ const sessionData = {
1755
+ sessionId,
1756
+ startTime: new Date(sessionStartTime).toISOString(),
1757
+ endTime: (/* @__PURE__ */ new Date()).toISOString(),
1758
+ durationMs: sessionDuration,
1759
+ toolCalls: toolCalls.length,
1760
+ filesEdited: Array.from(filesEdited)
1761
+ };
1762
+ await stateManager.saveSession(sessionId, sessionData);
1763
+ if (toolCalls.length > 20) {
1764
+ await stateManager.addObservation(
1765
+ i18n.t("obs.intensive_session", { tools: toolCalls.length })
1766
+ );
1767
+ }
1768
+ if (filesEdited.size > 10) {
1769
+ await stateManager.addMilestone(
1770
+ i18n.t("milestone.major_refactor", { files: filesEdited.size })
1771
+ );
1772
+ }
1773
+ console.log(
1774
+ i18n.t("log.session_ended", {
1775
+ duration: formatDuration(sessionDuration),
1776
+ tools: toolCalls.length,
1777
+ files: filesEdited.size
1778
+ })
1779
+ );
1780
+ },
1781
+ tool: createTools(stateManager, directory, toolCalls, i18n)
1782
+ };
1783
+ };
1784
+ var index_default = mimic;
1785
+ export {
1786
+ index_default as default,
1787
+ mimic
1788
+ };
1789
+ //# sourceMappingURL=index.js.map