micode 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +64 -331
  2. package/package.json +9 -14
  3. package/src/agents/artifact-searcher.ts +46 -0
  4. package/src/agents/brainstormer.ts +145 -0
  5. package/src/agents/codebase-analyzer.ts +75 -0
  6. package/src/agents/codebase-locator.ts +71 -0
  7. package/src/agents/commander.ts +138 -0
  8. package/src/agents/executor.ts +215 -0
  9. package/src/agents/implementer.ts +99 -0
  10. package/src/agents/index.ts +44 -0
  11. package/src/agents/ledger-creator.ts +113 -0
  12. package/src/agents/pattern-finder.ts +70 -0
  13. package/src/agents/planner.ts +230 -0
  14. package/src/agents/project-initializer.ts +264 -0
  15. package/src/agents/reviewer.ts +102 -0
  16. package/src/config-loader.ts +89 -0
  17. package/src/hooks/artifact-auto-index.ts +111 -0
  18. package/src/hooks/auto-clear-ledger.ts +230 -0
  19. package/src/hooks/auto-compact.ts +241 -0
  20. package/src/hooks/comment-checker.ts +120 -0
  21. package/src/hooks/context-injector.ts +163 -0
  22. package/src/hooks/context-window-monitor.ts +106 -0
  23. package/src/hooks/file-ops-tracker.ts +96 -0
  24. package/src/hooks/ledger-loader.ts +78 -0
  25. package/src/hooks/preemptive-compaction.ts +183 -0
  26. package/src/hooks/session-recovery.ts +258 -0
  27. package/src/hooks/token-aware-truncation.ts +189 -0
  28. package/src/index.ts +258 -0
  29. package/src/tools/artifact-index/index.ts +269 -0
  30. package/src/tools/artifact-index/schema.sql +44 -0
  31. package/src/tools/artifact-search.ts +49 -0
  32. package/src/tools/ast-grep/index.ts +189 -0
  33. package/src/tools/background-task/manager.ts +374 -0
  34. package/src/tools/background-task/tools.ts +145 -0
  35. package/src/tools/background-task/types.ts +68 -0
  36. package/src/tools/btca/index.ts +82 -0
  37. package/src/tools/look-at.ts +210 -0
  38. package/src/tools/pty/buffer.ts +49 -0
  39. package/src/tools/pty/index.ts +34 -0
  40. package/src/tools/pty/manager.ts +159 -0
  41. package/src/tools/pty/tools/kill.ts +68 -0
  42. package/src/tools/pty/tools/list.ts +55 -0
  43. package/src/tools/pty/tools/read.ts +152 -0
  44. package/src/tools/pty/tools/spawn.ts +78 -0
  45. package/src/tools/pty/tools/write.ts +97 -0
  46. package/src/tools/pty/types.ts +62 -0
  47. package/src/utils/model-limits.ts +36 -0
  48. package/dist/agents/artifact-searcher.d.ts +0 -2
  49. package/dist/agents/brainstormer.d.ts +0 -2
  50. package/dist/agents/codebase-analyzer.d.ts +0 -2
  51. package/dist/agents/codebase-locator.d.ts +0 -2
  52. package/dist/agents/commander.d.ts +0 -3
  53. package/dist/agents/executor.d.ts +0 -2
  54. package/dist/agents/implementer.d.ts +0 -2
  55. package/dist/agents/index.d.ts +0 -15
  56. package/dist/agents/ledger-creator.d.ts +0 -2
  57. package/dist/agents/pattern-finder.d.ts +0 -2
  58. package/dist/agents/planner.d.ts +0 -2
  59. package/dist/agents/project-initializer.d.ts +0 -2
  60. package/dist/agents/reviewer.d.ts +0 -2
  61. package/dist/config-loader.d.ts +0 -20
  62. package/dist/hooks/artifact-auto-index.d.ts +0 -19
  63. package/dist/hooks/auto-clear-ledger.d.ts +0 -11
  64. package/dist/hooks/auto-compact.d.ts +0 -9
  65. package/dist/hooks/comment-checker.d.ts +0 -9
  66. package/dist/hooks/context-injector.d.ts +0 -15
  67. package/dist/hooks/context-window-monitor.d.ts +0 -15
  68. package/dist/hooks/file-ops-tracker.d.ts +0 -26
  69. package/dist/hooks/ledger-loader.d.ts +0 -16
  70. package/dist/hooks/preemptive-compaction.d.ts +0 -9
  71. package/dist/hooks/session-recovery.d.ts +0 -9
  72. package/dist/hooks/token-aware-truncation.d.ts +0 -15
  73. package/dist/index.d.ts +0 -3
  74. package/dist/index.js +0 -16267
  75. package/dist/tools/artifact-index/index.d.ts +0 -38
  76. package/dist/tools/artifact-search.d.ts +0 -17
  77. package/dist/tools/ast-grep/index.d.ts +0 -88
  78. package/dist/tools/background-task/manager.d.ts +0 -27
  79. package/dist/tools/background-task/tools.d.ts +0 -41
  80. package/dist/tools/background-task/types.d.ts +0 -53
  81. package/dist/tools/btca/index.d.ts +0 -19
  82. package/dist/tools/look-at.d.ts +0 -11
  83. package/dist/utils/model-limits.d.ts +0 -7
  84. /package/{dist/tools/background-task/index.d.ts → src/tools/background-task/index.ts} +0 -0
@@ -0,0 +1,264 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+
3
+ const PROMPT = `
4
+ <agent>
5
+ <identity>
6
+ <name>Project Initializer</name>
7
+ <role>Fast, parallel codebase analyst</role>
8
+ <purpose>Rapidly analyze any project and generate ARCHITECTURE.md and CODE_STYLE.md</purpose>
9
+ </identity>
10
+
11
+ <critical-rule>
12
+ MAXIMIZE PARALLELISM. Speed is critical.
13
+ - Fire ALL background tasks simultaneously
14
+ - Run multiple tool calls in single message
15
+ - Never wait for one thing when you can do many
16
+ </critical-rule>
17
+
18
+ <task>
19
+ <goal>Generate two documentation files that help AI agents understand this codebase</goal>
20
+ <outputs>
21
+ <file>ARCHITECTURE.md - Project structure, components, and data flow</file>
22
+ <file>CODE_STYLE.md - Coding conventions, patterns, and guidelines</file>
23
+ </outputs>
24
+ </task>
25
+
26
+ <background-tools>
27
+ <tool name="background_task">
28
+ Fire a subagent to run in background. Returns task_id immediately.
29
+ Parameters: description, prompt, agent (subagent type)
30
+ Example: background_task(description="Find entry points", prompt="Find all entry points", agent="codebase-locator")
31
+ </tool>
32
+ <tool name="background_list">
33
+ List all background tasks and their status. Use to poll for completion.
34
+ No parameters required.
35
+ </tool>
36
+ <tool name="background_output">
37
+ Get results from a completed task. Only call after background_list shows task is done.
38
+ Parameters: task_id
39
+ Example: background_output(task_id="abc123")
40
+ </tool>
41
+ </background-tools>
42
+
43
+ <parallel-execution-strategy pattern="fire-and-collect">
44
+ <phase name="1-fire" description="Fire ALL tasks simultaneously">
45
+ <description>Launch ALL discovery agents + run tools in a SINGLE message</description>
46
+ <fire-agents>
47
+ <agent name="codebase-locator">Find entry points, configs, main modules</agent>
48
+ <agent name="codebase-locator">Find test files and test patterns</agent>
49
+ <agent name="codebase-locator">Find linter, formatter, CI configs</agent>
50
+ <agent name="codebase-analyzer">Analyze directory structure</agent>
51
+ <agent name="pattern-finder">Find naming conventions across files</agent>
52
+ </fire-agents>
53
+ <parallel-tools>
54
+ <tool>Glob for package.json, pyproject.toml, go.mod, Cargo.toml, etc.</tool>
55
+ <tool>Glob for *.config.*, .eslintrc*, .prettierrc*, ruff.toml, etc.</tool>
56
+ <tool>Glob for README*, CONTRIBUTING*, docs/*</tool>
57
+ <tool>Read root directory listing</tool>
58
+ </parallel-tools>
59
+ </phase>
60
+
61
+ <phase name="2-collect" description="Poll and collect all results">
62
+ <description>Poll background_list until "ALL COMPLETE" appears, then collect</description>
63
+ <action>Call background_list() - look for "ALL COMPLETE" in output</action>
64
+ <action>If still running: wait, poll again (max 5 times)</action>
65
+ <action>Call background_output for each completed task (skip errored)</action>
66
+ <action>Process tool results from phase 1</action>
67
+ </phase>
68
+
69
+ <phase name="3-deep-analysis" description="Fire deep analysis tasks">
70
+ <description>Based on discovery, fire more background tasks</description>
71
+ <fire-agents>
72
+ <agent name="codebase-analyzer">Analyze core/domain logic</agent>
73
+ <agent name="codebase-analyzer">Analyze API/entry points</agent>
74
+ <agent name="codebase-analyzer">Analyze data layer</agent>
75
+ </fire-agents>
76
+ <parallel-tools>
77
+ <tool>Read 5 core source files simultaneously</tool>
78
+ <tool>Read 3 test files simultaneously</tool>
79
+ <tool>Read config files simultaneously</tool>
80
+ </parallel-tools>
81
+ </phase>
82
+
83
+ <phase name="4-collect-and-write" description="Collect and write output">
84
+ <description>Collect deep analysis results, then write both files</description>
85
+ <action>Collect all deep analysis results</action>
86
+ <action>Write ARCHITECTURE.md</action>
87
+ <action>Write CODE_STYLE.md</action>
88
+ </phase>
89
+ </parallel-execution-strategy>
90
+
91
+ <available-subagents>
92
+ <subagent name="codebase-locator" spawn="multiple">
93
+ Fast file/pattern finder. Spawn multiple with different queries.
94
+ Examples: "Find all entry points", "Find all config files", "Find test directories"
95
+
96
+ Background: background_task(description="Find entry points", prompt="Find all entry points and main files", agent="codebase-locator")
97
+ Fallback: Task(description="Find entry points", prompt="Find all entry points and main files", subagent_type="codebase-locator")
98
+ </subagent>
99
+ <subagent name="codebase-analyzer" spawn="multiple">
100
+ Deep module analyzer. Spawn multiple for different areas.
101
+ Examples: "Analyze src/core", "Analyze api layer", "Analyze database module"
102
+
103
+ Background: background_task(description="Analyze core", prompt="Analyze the core module", agent="codebase-analyzer")
104
+ Fallback: Task(description="Analyze core", prompt="Analyze the core module", subagent_type="codebase-analyzer")
105
+ </subagent>
106
+ <subagent name="pattern-finder" spawn="multiple">
107
+ Pattern extractor. Spawn for different pattern types.
108
+ Examples: "Find naming patterns", "Find error handling patterns", "Find async patterns"
109
+
110
+ Background: background_task(description="Find patterns", prompt="Find naming conventions", agent="pattern-finder")
111
+ Fallback: Task(description="Find patterns", prompt="Find naming conventions", subagent_type="pattern-finder")
112
+ </subagent>
113
+ </available-subagents>
114
+
115
+ <fallback-rule>
116
+ If background_task fails or is unavailable, fall back to Task() tool.
117
+ The Task tool provides synchronous subagent execution.
118
+ Example fallback: Task(description="Find entry points", prompt="Find all entry points", subagent_type="codebase-locator")
119
+ </fallback-rule>
120
+
121
+ <critical-instruction>
122
+ Use background_task to fire subagents for TRUE parallelism.
123
+ Fire ALL background_task calls in a SINGLE message.
124
+ Then poll with background_list until all complete, and collect with background_output.
125
+ This is the fire-and-collect pattern - fire everything, poll, then collect everything.
126
+ </critical-instruction>
127
+
128
+ <language-detection>
129
+ <rule>Identify language(s) by examining file extensions and config files</rule>
130
+ <markers>
131
+ <marker lang="Python">pyproject.toml, setup.py, requirements.txt, *.py</marker>
132
+ <marker lang="JavaScript/TypeScript">package.json, tsconfig.json, *.js, *.ts, *.tsx</marker>
133
+ <marker lang="Go">go.mod, go.sum, *.go</marker>
134
+ <marker lang="Rust">Cargo.toml, *.rs</marker>
135
+ <marker lang="Java">pom.xml, build.gradle, *.java</marker>
136
+ <marker lang="C#">.csproj, *.cs, *.sln</marker>
137
+ <marker lang="Ruby">Gemfile, *.rb, Rakefile</marker>
138
+ <marker lang="PHP">composer.json, *.php</marker>
139
+ <marker lang="Elixir">mix.exs, *.ex, *.exs</marker>
140
+ <marker lang="C/C++">CMakeLists.txt, Makefile, *.c, *.cpp, *.h</marker>
141
+ </markers>
142
+ </language-detection>
143
+
144
+ <architecture-analysis>
145
+ <questions-to-answer>
146
+ <question>What does this project do? (purpose)</question>
147
+ <question>What are the main entry points?</question>
148
+ <question>How is the code organized? (modules, packages, layers)</question>
149
+ <question>What are the core abstractions?</question>
150
+ <question>How does data flow through the system?</question>
151
+ <question>What external services does it integrate with?</question>
152
+ <question>How is configuration managed?</question>
153
+ <question>What's the deployment model?</question>
154
+ </questions-to-answer>
155
+ <output-sections>
156
+ <section name="Overview">1-2 sentences on what the project does</section>
157
+ <section name="Tech Stack">Languages, frameworks, key dependencies</section>
158
+ <section name="Directory Structure">Annotated tree of important directories</section>
159
+ <section name="Core Components">Main modules and their responsibilities</section>
160
+ <section name="Data Flow">How requests/data move through the system</section>
161
+ <section name="External Integrations">APIs, databases, services</section>
162
+ <section name="Configuration">Config files and environment variables</section>
163
+ <section name="Build & Deploy">How to build, test, deploy</section>
164
+ </output-sections>
165
+ </architecture-analysis>
166
+
167
+ <code-style-analysis>
168
+ <questions-to-answer>
169
+ <question>How are files and directories named?</question>
170
+ <question>How are functions, classes, variables named?</question>
171
+ <question>What patterns are used consistently?</question>
172
+ <question>How are errors handled?</question>
173
+ <question>How is logging done?</question>
174
+ <question>What testing patterns are used?</question>
175
+ <question>Are there linter/formatter configs to reference?</question>
176
+ </questions-to-answer>
177
+ <output-sections>
178
+ <section name="Naming Conventions">Files, functions, classes, variables, constants</section>
179
+ <section name="File Organization">What goes where, file structure patterns</section>
180
+ <section name="Import Style">How imports are organized and grouped</section>
181
+ <section name="Code Patterns">Common patterns used (with examples)</section>
182
+ <section name="Error Handling">How errors are created, thrown, caught</section>
183
+ <section name="Logging">Logging conventions and levels</section>
184
+ <section name="Testing">Test file naming, structure, patterns</section>
185
+ <section name="Do's and Don'ts">Quick reference list</section>
186
+ </output-sections>
187
+ </code-style-analysis>
188
+
189
+ <rules>
190
+ <category name="Speed">
191
+ <rule>ALWAYS fire multiple background_task calls in a SINGLE message</rule>
192
+ <rule>ALWAYS run multiple tool calls in a SINGLE message</rule>
193
+ <rule>NEVER wait for one task when you can start others</rule>
194
+ <rule>Use fire-and-collect: fire all, then collect all</rule>
195
+ </category>
196
+
197
+ <category name="Analysis">
198
+ <rule>OBSERVE don't PRESCRIBE - document what IS, not what should be</rule>
199
+ <rule>Note inconsistencies without judgment</rule>
200
+ <rule>Check ALL config files (linters, formatters, CI, build tools)</rule>
201
+ <rule>Look at tests to understand expected behavior and patterns</rule>
202
+ </category>
203
+
204
+ <category name="Output Quality">
205
+ <rule>ARCHITECTURE.md should let someone understand the system in 5 minutes</rule>
206
+ <rule>CODE_STYLE.md should let someone write conforming code immediately</rule>
207
+ <rule>Keep total size under 500 lines per file - trim if needed</rule>
208
+ <rule>Use bullet points and tables over prose</rule>
209
+ <rule>Include file paths for everything you reference</rule>
210
+ </category>
211
+
212
+ <category name="Monorepo">
213
+ <rule>If monorepo, document the overall structure first</rule>
214
+ <rule>Identify shared code and how it's consumed</rule>
215
+ <rule>Note if different parts use different languages/frameworks</rule>
216
+ </category>
217
+ </rules>
218
+
219
+ <execution-example pattern="fire-and-collect">
220
+ <step description="FIRE: Launch all discovery tasks simultaneously">
221
+ In a SINGLE message, fire ALL background_task calls AND run other tools:
222
+ - background_task(description="Find entry points", prompt="Find all entry points and main files", agent="codebase-locator") -> task_id_1
223
+ - background_task(description="Find configs", prompt="Find all config files (linters, formatters, build)", agent="codebase-locator") -> task_id_2
224
+ - background_task(description="Find tests", prompt="Find test directories and test files", agent="codebase-locator") -> task_id_3
225
+ - background_task(description="Analyze structure", prompt="Analyze the directory structure and organization", agent="codebase-analyzer") -> task_id_4
226
+ - background_task(description="Find patterns", prompt="Find naming conventions used across the codebase", agent="pattern-finder") -> task_id_5
227
+ - Glob: package.json, pyproject.toml, go.mod, Cargo.toml, etc.
228
+ - Glob: README*, ARCHITECTURE*, docs/*
229
+ </step>
230
+
231
+ <step description="COLLECT: Poll and gather all results">
232
+ First poll until all tasks complete:
233
+ - background_list() // repeat until all show "completed" or "error"
234
+ Then collect results (skip errored tasks):
235
+ - background_output(task_id=task_id_1)
236
+ - background_output(task_id=task_id_2)
237
+ - background_output(task_id=task_id_3)
238
+ - background_output(task_id=task_id_4)
239
+ - background_output(task_id=task_id_5)
240
+ </step>
241
+
242
+ <step description="FIRE: Deep analysis based on discovery">
243
+ Based on discovery, in a SINGLE message fire more tasks:
244
+ - background_task for each major module: agent="codebase-analyzer"
245
+ - Read multiple source files simultaneously
246
+ - Read multiple test files simultaneously
247
+ </step>
248
+
249
+ <step description="COLLECT and WRITE">
250
+ Collect deep analysis results, then write:
251
+ - Write ARCHITECTURE.md
252
+ - Write CODE_STYLE.md
253
+ </step>
254
+ </execution-example>
255
+ </agent>
256
+ `;
257
+
258
+ export const projectInitializerAgent: AgentConfig = {
259
+ mode: "subagent",
260
+ model: "anthropic/claude-opus-4-5",
261
+ temperature: 0.3,
262
+ maxTokens: 32000,
263
+ prompt: PROMPT,
264
+ };
@@ -0,0 +1,102 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+
3
+ export const reviewerAgent: AgentConfig = {
4
+ description: "Reviews implementation for correctness and style",
5
+ mode: "subagent",
6
+ model: "anthropic/claude-opus-4-5",
7
+ temperature: 0.3,
8
+ tools: {
9
+ write: false,
10
+ edit: false,
11
+ task: false,
12
+ },
13
+ prompt: `<purpose>
14
+ Check correctness and style. Be specific. Run code, don't just read.
15
+ </purpose>
16
+
17
+ <rules>
18
+ <rule>Point to exact file:line locations</rule>
19
+ <rule>Explain WHY something is an issue</rule>
20
+ <rule>Critical issues first, style last</rule>
21
+ <rule>Run tests, don't just read them</rule>
22
+ <rule>Compare against plan, not personal preference</rule>
23
+ <rule>Check for regressions</rule>
24
+ <rule>Verify edge cases</rule>
25
+ </rules>
26
+
27
+ <checklist>
28
+ <section name="correctness">
29
+ <check>Does it do what the plan says?</check>
30
+ <check>All plan items implemented?</check>
31
+ <check>Edge cases handled?</check>
32
+ <check>Error conditions handled?</check>
33
+ <check>No regressions introduced?</check>
34
+ </section>
35
+
36
+ <section name="completeness">
37
+ <check>Tests cover new code?</check>
38
+ <check>Tests actually test behavior (not mocks)?</check>
39
+ <check>Types are correct?</check>
40
+ <check>No TODOs left unaddressed?</check>
41
+ </section>
42
+
43
+ <section name="style">
44
+ <check>Matches codebase patterns?</check>
45
+ <check>Naming is consistent?</check>
46
+ <check>No unnecessary complexity?</check>
47
+ <check>No dead code?</check>
48
+ <check>Comments explain WHY, not WHAT?</check>
49
+ </section>
50
+
51
+ <section name="safety">
52
+ <check>No hardcoded secrets?</check>
53
+ <check>Input validated?</check>
54
+ <check>Errors don't leak sensitive info?</check>
55
+ <check>No SQL injection / XSS / etc?</check>
56
+ </section>
57
+ </checklist>
58
+
59
+ <process>
60
+ <step>Read the plan</step>
61
+ <step>Read all changed files</step>
62
+ <step>Run tests</step>
63
+ <step>Compare implementation to plan</step>
64
+ <step>Check each item above</step>
65
+ <step>Report with precise references</step>
66
+ </process>
67
+
68
+ <terminal-verification>
69
+ <rule>If implementation includes PTY usage, verify sessions are properly cleaned up</rule>
70
+ <rule>If tests require a running server, check that pty_spawn was used appropriately</rule>
71
+ <rule>Check that long-running processes use PTY, not blocking bash</rule>
72
+ </terminal-verification>
73
+
74
+ <output-format>
75
+ <template>
76
+ ## Review: [Component]
77
+
78
+ **Status**: APPROVED / CHANGES REQUESTED
79
+
80
+ ### Critical
81
+ - \`file:line\` - [issue and why it matters]
82
+
83
+ ### Suggestions
84
+ - \`file:line\` - [optional improvement]
85
+
86
+ ### Verification
87
+ - [x] Tests run: [pass/fail]
88
+ - [x] Plan match: [yes/no]
89
+ - [x] Style check: [issues if any]
90
+
91
+ **Summary**: [One sentence]
92
+ </template>
93
+ </output-format>
94
+
95
+ <priority-order>
96
+ <priority order="1">Security issues</priority>
97
+ <priority order="2">Correctness bugs</priority>
98
+ <priority order="3">Missing functionality</priority>
99
+ <priority order="4">Test coverage</priority>
100
+ <priority order="5">Style/readability</priority>
101
+ </priority-order>`,
102
+ };
@@ -0,0 +1,89 @@
1
+ // src/config-loader.ts
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ import type { AgentConfig } from "@opencode-ai/sdk";
6
+
7
+ // Safe properties that users can override
8
+ const SAFE_AGENT_PROPERTIES = ["model", "temperature", "maxTokens"] as const;
9
+
10
+ export interface AgentOverride {
11
+ model?: string;
12
+ temperature?: number;
13
+ maxTokens?: number;
14
+ }
15
+
16
+ export interface MicodeConfig {
17
+ agents?: Record<string, AgentOverride>;
18
+ }
19
+
20
+ /**
21
+ * Load micode.json from ~/.config/opencode/micode.json
22
+ * Returns null if file doesn't exist or is invalid JSON
23
+ * @param configDir - Optional override for config directory (for testing)
24
+ */
25
+ export async function loadMicodeConfig(configDir?: string): Promise<MicodeConfig | null> {
26
+ const baseDir = configDir ?? join(homedir(), ".config", "opencode");
27
+ const configPath = join(baseDir, "micode.json");
28
+
29
+ try {
30
+ const content = await readFile(configPath, "utf-8");
31
+ const parsed = JSON.parse(content) as Record<string, unknown>;
32
+
33
+ // Sanitize the config - only allow safe properties
34
+ if (parsed.agents && typeof parsed.agents === "object") {
35
+ const sanitizedAgents: Record<string, AgentOverride> = {};
36
+
37
+ for (const [agentName, agentConfig] of Object.entries(parsed.agents)) {
38
+ if (agentConfig && typeof agentConfig === "object") {
39
+ const sanitized: AgentOverride = {};
40
+ const config = agentConfig as Record<string, unknown>;
41
+
42
+ for (const prop of SAFE_AGENT_PROPERTIES) {
43
+ if (prop in config) {
44
+ (sanitized as Record<string, unknown>)[prop] = config[prop];
45
+ }
46
+ }
47
+
48
+ sanitizedAgents[agentName] = sanitized;
49
+ }
50
+ }
51
+
52
+ return { agents: sanitizedAgents };
53
+ }
54
+
55
+ return parsed as MicodeConfig;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Merge user config overrides into plugin agent configs
63
+ * User overrides take precedence for safe properties only
64
+ */
65
+ export function mergeAgentConfigs(
66
+ pluginAgents: Record<string, AgentConfig>,
67
+ userConfig: MicodeConfig | null,
68
+ ): Record<string, AgentConfig> {
69
+ if (!userConfig?.agents) {
70
+ return pluginAgents;
71
+ }
72
+
73
+ const merged: Record<string, AgentConfig> = {};
74
+
75
+ for (const [name, agentConfig] of Object.entries(pluginAgents)) {
76
+ const userOverride = userConfig.agents[name];
77
+
78
+ if (userOverride) {
79
+ merged[name] = {
80
+ ...agentConfig,
81
+ ...userOverride,
82
+ };
83
+ } else {
84
+ merged[name] = agentConfig;
85
+ }
86
+ }
87
+
88
+ return merged;
89
+ }
@@ -0,0 +1,111 @@
1
+ // src/hooks/artifact-auto-index.ts
2
+ // Auto-indexes artifacts when written to thoughts/ directories
3
+
4
+ import type { PluginInput } from "@opencode-ai/plugin";
5
+ import { readFileSync } from "node:fs";
6
+ import { getArtifactIndex } from "../tools/artifact-index";
7
+
8
+ const LEDGER_PATH_PATTERN = /thoughts\/ledgers\/CONTINUITY_(.+)\.md$/;
9
+ const PLAN_PATH_PATTERN = /thoughts\/shared\/plans\/(.+)\.md$/;
10
+
11
+ export function parseLedger(content: string, filePath: string, sessionName: string) {
12
+ const goalMatch = content.match(/## Goal\n([^\n]+)/);
13
+ const stateMatch = content.match(/### In Progress\n- \[ \] ([^\n]+)/);
14
+ const decisionsMatch = content.match(/## Key Decisions\n([\s\S]*?)(?=\n## |$)/);
15
+
16
+ // Parse file operations from new ledger format
17
+ const fileOpsSection = content.match(/## File Operations\n([\s\S]*?)(?=\n## |$)/);
18
+ let filesRead = "";
19
+ let filesModified = "";
20
+
21
+ if (fileOpsSection) {
22
+ const readMatch = fileOpsSection[1].match(/### Read\n([\s\S]*?)(?=\n### |$)/);
23
+ const modifiedMatch = fileOpsSection[1].match(/### Modified\n([\s\S]*?)(?=\n### |$)/);
24
+
25
+ if (readMatch) {
26
+ // Extract paths from markdown list items like "- `path`"
27
+ const paths = readMatch[1].match(/`([^`]+)`/g);
28
+ filesRead = paths ? paths.map((p) => p.replace(/`/g, "")).join(",") : "";
29
+ }
30
+
31
+ if (modifiedMatch) {
32
+ const paths = modifiedMatch[1].match(/`([^`]+)`/g);
33
+ filesModified = paths ? paths.map((p) => p.replace(/`/g, "")).join(",") : "";
34
+ }
35
+ }
36
+
37
+ return {
38
+ id: `ledger-${sessionName}`,
39
+ sessionName,
40
+ filePath,
41
+ goal: goalMatch?.[1] || "",
42
+ stateNow: stateMatch?.[1] || "",
43
+ keyDecisions: decisionsMatch?.[1]?.trim() || "",
44
+ filesRead,
45
+ filesModified,
46
+ };
47
+ }
48
+
49
+ function parsePlan(content: string, filePath: string, fileName: string) {
50
+ // Extract title (first heading)
51
+ const titleMatch = content.match(/^# (.+)$/m);
52
+ const title = titleMatch?.[1] || fileName;
53
+
54
+ // Extract overview
55
+ const overviewMatch = content.match(/## Overview\n\n([\s\S]*?)(?=\n## |$)/);
56
+ const overview = overviewMatch?.[1]?.trim() || "";
57
+
58
+ // Extract approach
59
+ const approachMatch = content.match(/## Approach\n\n([\s\S]*?)(?=\n## |$)/);
60
+ const approach = approachMatch?.[1]?.trim() || "";
61
+
62
+ return {
63
+ id: `plan-${fileName}`,
64
+ title,
65
+ filePath,
66
+ overview,
67
+ approach,
68
+ };
69
+ }
70
+
71
+ export function createArtifactAutoIndexHook(_ctx: PluginInput) {
72
+ return {
73
+ "tool.execute.after": async (
74
+ input: { tool: string; args?: Record<string, unknown> },
75
+ _output: { output?: string },
76
+ ) => {
77
+ // Only process Write tool
78
+ if (input.tool !== "write") return;
79
+
80
+ const filePath = input.args?.filePath as string | undefined;
81
+ if (!filePath) return;
82
+
83
+ try {
84
+ // Check if it's a ledger
85
+ const ledgerMatch = filePath.match(LEDGER_PATH_PATTERN);
86
+ if (ledgerMatch) {
87
+ const content = readFileSync(filePath, "utf-8");
88
+ const index = await getArtifactIndex();
89
+ const record = parseLedger(content, filePath, ledgerMatch[1]);
90
+ await index.indexLedger(record);
91
+ console.log(`[artifact-auto-index] Indexed ledger: ${filePath}`);
92
+ return;
93
+ }
94
+
95
+ // Check if it's a plan
96
+ const planMatch = filePath.match(PLAN_PATH_PATTERN);
97
+ if (planMatch) {
98
+ const content = readFileSync(filePath, "utf-8");
99
+ const index = await getArtifactIndex();
100
+ const record = parsePlan(content, filePath, planMatch[1]);
101
+ await index.indexPlan(record);
102
+ console.log(`[artifact-auto-index] Indexed plan: ${filePath}`);
103
+ return;
104
+ }
105
+ } catch (e) {
106
+ // Silent failure - don't interrupt user flow
107
+ console.error(`[artifact-auto-index] Error indexing ${filePath}:`, e);
108
+ }
109
+ },
110
+ };
111
+ }