micode 0.8.0 → 0.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micode",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "OpenCode plugin with Brainstorm-Research-Plan-Implement workflow",
5
5
  "module": "src/index.ts",
6
6
  "main": "src/index.ts",
@@ -32,6 +32,7 @@ This is DESIGN ONLY. The planner agent handles detailed implementation plans.
32
32
  <subagent name="codebase-analyzer">Deep analysis of specific modules.</subagent>
33
33
  <subagent name="pattern-finder">Find existing patterns in codebase.</subagent>
34
34
  <subagent name="planner">Creates detailed implementation plan from validated design.</subagent>
35
+ <subagent name="executor">Executes implementation plan with implementer/reviewer cycles.</subagent>
35
36
  </available-subagents>
36
37
 
37
38
  <process>
@@ -87,14 +88,27 @@ This is DESIGN ONLY. The planner agent handles detailed implementation plans.
87
88
  )
88
89
  </spawn>
89
90
  <rule>Do NOT ask again - if user approved, spawn planner immediately</rule>
90
- <after-handoff>
91
- <action>Report ONLY: "Implementation plan created at thoughts/shared/plans/YYYY-MM-DD-{topic}.md"</action>
92
- <action>Tell user: "Ready to execute? Ask the commander to run the plan."</action>
91
+ <after-planner>
92
+ <action>Report: "Implementation plan created at thoughts/shared/plans/YYYY-MM-DD-{topic}.md"</action>
93
+ <action>Ask user: "Ready to execute the plan?"</action>
94
+ <rule>Wait for user response before proceeding</rule>
95
+ </after-planner>
96
+ </phase>
97
+
98
+ <phase name="execution" trigger="user approves execution">
99
+ <action>When user says yes/execute/go, spawn the executor:</action>
100
+ <spawn>
101
+ Task(
102
+ subagent_type="executor",
103
+ prompt="Execute the implementation plan at thoughts/shared/plans/YYYY-MM-DD-{topic}.md",
104
+ description="Execute implementation plan"
105
+ )
106
+ </spawn>
107
+ <after-execution>
108
+ <action>Report executor results to user</action>
93
109
  <rule priority="CRITICAL">YOUR JOB IS DONE. STOP HERE.</rule>
94
- <rule>Do NOT process, summarize, or act on the planner's output</rule>
95
- <rule>Do NOT write any code - the plan contains code but that's for the executor</rule>
96
- <rule>Do NOT continue the conversation - wait for user's next request</rule>
97
- </after-handoff>
110
+ <rule>Do NOT write any code yourself</rule>
111
+ </after-execution>
98
112
  </phase>
99
113
  </process>
100
114
 
@@ -4,13 +4,23 @@ import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
 
6
6
  // Compact when this percentage of context is used
7
- const COMPACT_THRESHOLD = 0.50;
7
+ const COMPACT_THRESHOLD = 0.5;
8
8
 
9
9
  const LEDGER_DIR = "thoughts/ledgers";
10
10
 
11
+ // Timeout for waiting for compaction to complete (2 minutes)
12
+ const COMPACTION_TIMEOUT_MS = 120_000;
13
+
14
+ interface PendingCompaction {
15
+ resolve: () => void;
16
+ reject: (error: Error) => void;
17
+ timeoutId: ReturnType<typeof setTimeout>;
18
+ }
19
+
11
20
  interface AutoCompactState {
12
21
  inProgress: Set<string>;
13
22
  lastCompactTime: Map<string, number>;
23
+ pendingCompactions: Map<string, PendingCompaction>;
14
24
  }
15
25
 
16
26
  // Cooldown between compaction attempts (prevent rapid re-triggering)
@@ -20,6 +30,7 @@ export function createAutoCompactHook(ctx: PluginInput) {
20
30
  const state: AutoCompactState = {
21
31
  inProgress: new Set(),
22
32
  lastCompactTime: new Map(),
33
+ pendingCompactions: new Map(),
23
34
  };
24
35
 
25
36
  async function writeSummaryToLedger(sessionID: string): Promise<void> {
@@ -78,6 +89,17 @@ ${summaryText}
78
89
  }
79
90
  }
80
91
 
92
+ function waitForCompaction(sessionID: string): Promise<void> {
93
+ return new Promise((resolve, reject) => {
94
+ const timeoutId = setTimeout(() => {
95
+ state.pendingCompactions.delete(sessionID);
96
+ reject(new Error("Compaction timed out"));
97
+ }, COMPACTION_TIMEOUT_MS);
98
+
99
+ state.pendingCompactions.set(sessionID, { resolve, reject, timeoutId });
100
+ });
101
+ }
102
+
81
103
  async function triggerCompaction(
82
104
  sessionID: string,
83
105
  providerID: string,
@@ -111,15 +133,19 @@ ${summaryText}
111
133
  })
112
134
  .catch(() => {});
113
135
 
136
+ // Start the compaction - this returns immediately while compaction runs async
114
137
  await ctx.client.session.summarize({
115
138
  path: { id: sessionID },
116
139
  body: { providerID, modelID },
117
140
  query: { directory: ctx.directory },
118
141
  });
119
142
 
143
+ // Wait for the session.compacted event to confirm completion
144
+ await waitForCompaction(sessionID);
145
+
120
146
  state.lastCompactTime.set(sessionID, Date.now());
121
147
 
122
- // Write summary to ledger file
148
+ // Write summary to ledger file (only after compaction is confirmed complete)
123
149
  await writeSummaryToLedger(sessionID);
124
150
 
125
151
  await ctx.client.tui
@@ -153,12 +179,32 @@ ${summaryText}
153
179
  event: async ({ event }: { event: { type: string; properties?: unknown } }) => {
154
180
  const props = event.properties as Record<string, unknown> | undefined;
155
181
 
182
+ // Handle compaction completion
183
+ if (event.type === "session.compacted") {
184
+ const sessionID = props?.sessionID as string | undefined;
185
+ if (sessionID) {
186
+ const pending = state.pendingCompactions.get(sessionID);
187
+ if (pending) {
188
+ clearTimeout(pending.timeoutId);
189
+ state.pendingCompactions.delete(sessionID);
190
+ pending.resolve();
191
+ }
192
+ }
193
+ return;
194
+ }
195
+
156
196
  // Cleanup on session delete
157
197
  if (event.type === "session.deleted") {
158
198
  const sessionInfo = props?.info as { id?: string } | undefined;
159
199
  if (sessionInfo?.id) {
160
200
  state.inProgress.delete(sessionInfo.id);
161
201
  state.lastCompactTime.delete(sessionInfo.id);
202
+ const pending = state.pendingCompactions.get(sessionInfo.id);
203
+ if (pending) {
204
+ clearTimeout(pending.timeoutId);
205
+ state.pendingCompactions.delete(sessionInfo.id);
206
+ pending.reject(new Error("Session deleted"));
207
+ }
162
208
  }
163
209
  return;
164
210
  }