micode 0.3.4 → 0.4.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.
- package/README.md +42 -26
- package/dist/agents/index.d.ts +1 -3
- package/dist/hooks/artifact-auto-index.d.ts +10 -0
- package/dist/hooks/file-ops-tracker.d.ts +26 -0
- package/dist/index.js +224 -344
- package/dist/tools/artifact-index/index.d.ts +3 -12
- package/dist/tools/artifact-search.d.ts +1 -2
- package/package.json +2 -2
- package/dist/agents/handoff-creator.d.ts +0 -2
- package/dist/agents/handoff-resumer.d.ts +0 -2
package/README.md
CHANGED
|
@@ -135,29 +135,55 @@ Each task gets its own implement→review loop:
|
|
|
135
135
|
|
|
136
136
|
### 4. Session Continuity
|
|
137
137
|
|
|
138
|
-
Maintain context across long sessions and context clears with
|
|
138
|
+
Maintain context across long sessions and context clears with structured compaction:
|
|
139
139
|
|
|
140
140
|
#### Ledger System
|
|
141
141
|
|
|
142
|
-
The **continuity ledger**
|
|
142
|
+
The **continuity ledger** serves as both session state and compaction summary. Based on [Factory.ai's structured compaction research](https://factory.ai/blog/context-compression), which found that structured summarization with deterministic file tracking retains more useful context.
|
|
143
143
|
|
|
144
144
|
```
|
|
145
145
|
/ledger
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
Creates/updates `thoughts/ledgers/CONTINUITY_{session-name}.md` with:
|
|
149
|
-
- Goal and constraints
|
|
150
|
-
- Key decisions with rationale
|
|
151
|
-
- Current state (Done/Now/Next)
|
|
152
|
-
- Working set (branch, key files)
|
|
153
149
|
|
|
154
|
-
|
|
150
|
+
```markdown
|
|
151
|
+
# Session: {name}
|
|
152
|
+
Updated: {timestamp}
|
|
153
|
+
|
|
154
|
+
## Goal
|
|
155
|
+
## Constraints
|
|
156
|
+
## Progress
|
|
157
|
+
### Done
|
|
158
|
+
- [x] {Completed items}
|
|
159
|
+
### In Progress
|
|
160
|
+
- [ ] {Current work}
|
|
161
|
+
### Blocked
|
|
162
|
+
- {Issues, if any}
|
|
163
|
+
## Key Decisions
|
|
164
|
+
- **{Decision}**: {Rationale}
|
|
165
|
+
## Next Steps
|
|
166
|
+
1. {Ordered list}
|
|
167
|
+
## File Operations
|
|
168
|
+
### Read
|
|
169
|
+
- `{paths read since last compaction}`
|
|
170
|
+
### Modified
|
|
171
|
+
- `{paths written/edited since last compaction}`
|
|
172
|
+
## Critical Context
|
|
173
|
+
- {Data, examples, references needed to continue}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Key features:**
|
|
177
|
+
|
|
178
|
+
- **Iterative merging** - Updates preserve existing information, adding new progress rather than regenerating from scratch
|
|
179
|
+
- **Deterministic file tracking** - Read/write/edit operations tracked automatically via tool call interception, not LLM extraction
|
|
180
|
+
- **Auto-injection** - Most recent ledger injected into system prompt on session start
|
|
155
181
|
|
|
156
182
|
**Auto-clear:** At 80% context usage, the system automatically:
|
|
157
|
-
1.
|
|
158
|
-
2.
|
|
183
|
+
1. Captures file operations tracked since last clear
|
|
184
|
+
2. Updates ledger with current state (iterative merge with previous)
|
|
159
185
|
3. Clears the session
|
|
160
|
-
4. Injects the ledger into
|
|
186
|
+
4. Injects the updated ledger into fresh context
|
|
161
187
|
|
|
162
188
|
#### Artifact Search
|
|
163
189
|
|
|
@@ -170,26 +196,17 @@ Search past work to find relevant precedent:
|
|
|
170
196
|
|
|
171
197
|
Searches across:
|
|
172
198
|
- Ledgers (`thoughts/ledgers/`)
|
|
173
|
-
- Handoffs (`thoughts/shared/handoffs/`)
|
|
174
199
|
- Plans (`thoughts/shared/plans/`)
|
|
175
200
|
|
|
176
201
|
**Auto-indexing:** Artifacts are automatically indexed when created.
|
|
177
202
|
|
|
178
|
-
#### Handoff
|
|
179
|
-
|
|
180
|
-
Save/resume session state for continuity:
|
|
181
|
-
|
|
182
|
-
- `handoff-creator`: Save current session (reads ledger for context)
|
|
183
|
-
- `handoff-resumer`: Resume from handoff
|
|
184
|
-
- Output: `thoughts/shared/handoffs/`
|
|
185
|
-
|
|
186
203
|
## Commands
|
|
187
204
|
|
|
188
205
|
| Command | Description |
|
|
189
206
|
|---------|-------------|
|
|
190
207
|
| `/init` | Initialize project with ARCHITECTURE.md and CODE_STYLE.md |
|
|
191
208
|
| `/ledger` | Create or update continuity ledger for session state |
|
|
192
|
-
| `/search` | Search past
|
|
209
|
+
| `/search` | Search past plans and ledgers |
|
|
193
210
|
|
|
194
211
|
## Agents
|
|
195
212
|
|
|
@@ -207,8 +224,6 @@ Save/resume session state for continuity:
|
|
|
207
224
|
| reviewer | subagent | claude-opus-4-5 | Review correctness and style |
|
|
208
225
|
| ledger-creator | subagent | claude-sonnet | Create/update continuity ledgers |
|
|
209
226
|
| artifact-searcher | subagent | claude-sonnet | Search past work for precedent |
|
|
210
|
-
| handoff-creator | subagent | claude-opus-4-5 | Save session state |
|
|
211
|
-
| handoff-resumer | subagent | claude-opus-4-5 | Resume from handoff |
|
|
212
227
|
|
|
213
228
|
## Tools
|
|
214
229
|
|
|
@@ -217,7 +232,7 @@ Save/resume session state for continuity:
|
|
|
217
232
|
| `ast_grep_search` | AST-aware code pattern search |
|
|
218
233
|
| `ast_grep_replace` | AST-aware code pattern replacement |
|
|
219
234
|
| `look_at` | Extract file structure for large files |
|
|
220
|
-
| `artifact_search` | Search past
|
|
235
|
+
| `artifact_search` | Search past plans and ledgers |
|
|
221
236
|
| `background_task` | Run long-running tasks in background |
|
|
222
237
|
| `background_output` | Check background task status/output |
|
|
223
238
|
| `background_cancel` | Cancel background tasks |
|
|
@@ -229,7 +244,8 @@ Save/resume session state for continuity:
|
|
|
229
244
|
|------|-------------|
|
|
230
245
|
| Think Mode | Keywords like "think hard" enable 32k token thinking budget |
|
|
231
246
|
| Ledger Loader | Injects continuity ledger into system prompt |
|
|
232
|
-
| Auto-Clear Ledger | At 80% context, saves ledger
|
|
247
|
+
| Auto-Clear Ledger | At 80% context, saves ledger with file ops and clears session |
|
|
248
|
+
| File Ops Tracker | Tracks read/write/edit tool calls for deterministic file operation logging |
|
|
233
249
|
| Artifact Auto-Index | Indexes artifacts when written to thoughts/ directories |
|
|
234
250
|
| Auto-Compact | Summarizes session when hitting token limits |
|
|
235
251
|
| Context Injector | Injects ARCHITECTURE.md, CODE_STYLE.md, .cursorrules |
|
|
@@ -276,8 +292,7 @@ micode/
|
|
|
276
292
|
├── ledgers/ # Continuity ledgers
|
|
277
293
|
└── shared/
|
|
278
294
|
├── designs/ # Brainstorm outputs
|
|
279
|
-
|
|
280
|
-
└── handoffs/ # Session handoffs
|
|
295
|
+
└── plans/ # Implementation plans
|
|
281
296
|
```
|
|
282
297
|
|
|
283
298
|
## Development
|
|
@@ -341,3 +356,4 @@ Built on techniques from:
|
|
|
341
356
|
|
|
342
357
|
- **[oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)** - OpenCode plugin architecture, agent orchestration patterns, and trusted publishing setup
|
|
343
358
|
- **[HumanLayer ACE-FCA](https://github.com/humanlayer/12-factor-agents)** - Advanced Context Engineering for Coding Agents, structured workflows, and the research → plan → implement methodology
|
|
359
|
+
- **[Factory.ai Context Compression](https://factory.ai/blog/context-compression)** - Structured compaction research showing that anchored iterative summarization with deterministic file tracking outperforms generic compression
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -7,11 +7,9 @@ import { plannerAgent } from "./planner";
|
|
|
7
7
|
import { implementerAgent } from "./implementer";
|
|
8
8
|
import { reviewerAgent } from "./reviewer";
|
|
9
9
|
import { executorAgent } from "./executor";
|
|
10
|
-
import { handoffCreatorAgent } from "./handoff-creator";
|
|
11
|
-
import { handoffResumerAgent } from "./handoff-resumer";
|
|
12
10
|
import { primaryAgent, PRIMARY_AGENT_NAME } from "./commander";
|
|
13
11
|
import { projectInitializerAgent } from "./project-initializer";
|
|
14
12
|
import { ledgerCreatorAgent } from "./ledger-creator";
|
|
15
13
|
import { artifactSearcherAgent } from "./artifact-searcher";
|
|
16
14
|
export declare const agents: Record<string, AgentConfig>;
|
|
17
|
-
export { primaryAgent, PRIMARY_AGENT_NAME, brainstormerAgent, codebaseLocatorAgent, codebaseAnalyzerAgent, patternFinderAgent, plannerAgent, implementerAgent, reviewerAgent, executorAgent,
|
|
15
|
+
export { primaryAgent, PRIMARY_AGENT_NAME, brainstormerAgent, codebaseLocatorAgent, codebaseAnalyzerAgent, patternFinderAgent, plannerAgent, implementerAgent, reviewerAgent, executorAgent, projectInitializerAgent, ledgerCreatorAgent, artifactSearcherAgent, };
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export declare function parseLedger(content: string, filePath: string, sessionName: string): {
|
|
3
|
+
id: string;
|
|
4
|
+
sessionName: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
goal: string;
|
|
7
|
+
stateNow: string;
|
|
8
|
+
keyDecisions: string;
|
|
9
|
+
filesRead: string;
|
|
10
|
+
filesModified: string;
|
|
11
|
+
};
|
|
2
12
|
export declare function createArtifactAutoIndexHook(_ctx: PluginInput): {
|
|
3
13
|
"tool.execute.after": (input: {
|
|
4
14
|
tool: string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
interface FileOps {
|
|
3
|
+
read: Set<string>;
|
|
4
|
+
modified: Set<string>;
|
|
5
|
+
}
|
|
6
|
+
export declare function trackFileOp(sessionID: string, operation: "read" | "write" | "edit", filePath: string): void;
|
|
7
|
+
export declare function getFileOps(sessionID: string): FileOps;
|
|
8
|
+
export declare function clearFileOps(sessionID: string): void;
|
|
9
|
+
export declare function getAndClearFileOps(sessionID: string): FileOps;
|
|
10
|
+
export declare function formatFileOpsForPrompt(ops: FileOps): string;
|
|
11
|
+
export declare function createFileOpsTrackerHook(_ctx: PluginInput): {
|
|
12
|
+
"tool.execute.after": (input: {
|
|
13
|
+
tool: string;
|
|
14
|
+
sessionID: string;
|
|
15
|
+
args?: Record<string, unknown>;
|
|
16
|
+
}, _output: {
|
|
17
|
+
output?: string;
|
|
18
|
+
}) => Promise<void>;
|
|
19
|
+
event: ({ event }: {
|
|
20
|
+
event: {
|
|
21
|
+
type: string;
|
|
22
|
+
properties?: unknown;
|
|
23
|
+
};
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -763,14 +763,22 @@ Batch 3 (parallel):
|
|
|
763
763
|
Executes ONE task from the plan.
|
|
764
764
|
Input: Single task with context (which files, what to do).
|
|
765
765
|
Output: Changes made and verification results for that task.
|
|
766
|
+
Invoke with: Task tool, subagent_type="implementer"
|
|
766
767
|
</subagent>
|
|
767
768
|
<subagent name="reviewer" spawn="parallel-per-task">
|
|
768
769
|
Reviews ONE task's implementation.
|
|
769
770
|
Input: Single task's changes against its requirements.
|
|
770
771
|
Output: APPROVED or CHANGES REQUESTED for that task.
|
|
772
|
+
Invoke with: Task tool, subagent_type="reviewer"
|
|
771
773
|
</subagent>
|
|
772
774
|
</available-subagents>
|
|
773
775
|
|
|
776
|
+
<critical-instruction>
|
|
777
|
+
You MUST use the Task tool to spawn implementer and reviewer subagents.
|
|
778
|
+
Example: Task(description="Implement task 1", prompt="...", subagent_type="implementer")
|
|
779
|
+
Do NOT try to implement or review yourself - delegate to subagents.
|
|
780
|
+
</critical-instruction>
|
|
781
|
+
|
|
774
782
|
<per-task-cycle>
|
|
775
783
|
For each task:
|
|
776
784
|
1. Spawn implementer with task details
|
|
@@ -782,18 +790,17 @@ For each task:
|
|
|
782
790
|
</per-task-cycle>
|
|
783
791
|
|
|
784
792
|
<parallel-spawning>
|
|
785
|
-
Within a batch, spawn ALL implementers in a SINGLE message:
|
|
786
|
-
|
|
787
|
-
Example for batch with tasks 1, 2, 3:
|
|
788
|
-
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
- reviewer: "Review task 3 implementation"
|
|
793
|
+
Within a batch, spawn ALL implementers in a SINGLE message using the Task tool:
|
|
794
|
+
|
|
795
|
+
Example for batch with tasks 1, 2, 3 - call Task tool 3 times in ONE message:
|
|
796
|
+
- Task(description="Task 1", prompt="Execute task 1: [details]", subagent_type="implementer")
|
|
797
|
+
- Task(description="Task 2", prompt="Execute task 2: [details]", subagent_type="implementer")
|
|
798
|
+
- Task(description="Task 3", prompt="Execute task 3: [details]", subagent_type="implementer")
|
|
799
|
+
|
|
800
|
+
Then after all complete, in ONE message call Task tool for reviewers:
|
|
801
|
+
- Task(description="Review 1", prompt="Review task 1 implementation", subagent_type="reviewer")
|
|
802
|
+
- Task(description="Review 2", prompt="Review task 2 implementation", subagent_type="reviewer")
|
|
803
|
+
- Task(description="Review 3", prompt="Review task 3 implementation", subagent_type="reviewer")
|
|
797
804
|
</parallel-spawning>
|
|
798
805
|
|
|
799
806
|
<rules>
|
|
@@ -848,159 +855,6 @@ Then after all complete, in ONE message spawn:
|
|
|
848
855
|
</never-do>`
|
|
849
856
|
};
|
|
850
857
|
|
|
851
|
-
// src/agents/handoff-creator.ts
|
|
852
|
-
var handoffCreatorAgent = {
|
|
853
|
-
description: "Creates handoff documents for session continuity",
|
|
854
|
-
mode: "subagent",
|
|
855
|
-
model: "anthropic/claude-opus-4-5",
|
|
856
|
-
temperature: 0.2,
|
|
857
|
-
tools: {
|
|
858
|
-
edit: false,
|
|
859
|
-
task: false
|
|
860
|
-
},
|
|
861
|
-
prompt: `<purpose>
|
|
862
|
-
Create handoff document to transfer context to future session.
|
|
863
|
-
</purpose>
|
|
864
|
-
|
|
865
|
-
<when-to-use>
|
|
866
|
-
<trigger>Hitting context limits</trigger>
|
|
867
|
-
<trigger>Ending work session</trigger>
|
|
868
|
-
<trigger>Switching to different task</trigger>
|
|
869
|
-
</when-to-use>
|
|
870
|
-
|
|
871
|
-
<rules>
|
|
872
|
-
<rule>FIRST check for existing ledger at thoughts/ledgers/CONTINUITY_*.md</rule>
|
|
873
|
-
<rule>If ledger exists, use its session name for handoff directory</rule>
|
|
874
|
-
<rule>Capture ALL in-progress work</rule>
|
|
875
|
-
<rule>Include exact file:line references for changes</rule>
|
|
876
|
-
<rule>Document learnings and gotchas</rule>
|
|
877
|
-
<rule>Prioritize next steps clearly</rule>
|
|
878
|
-
<rule>Include git state (branch, commit)</rule>
|
|
879
|
-
<rule>Reference all artifacts created</rule>
|
|
880
|
-
</rules>
|
|
881
|
-
|
|
882
|
-
<process>
|
|
883
|
-
<step>Check for ledger at thoughts/ledgers/CONTINUITY_*.md</step>
|
|
884
|
-
<step>If ledger exists, extract session name and state</step>
|
|
885
|
-
<step>Review what was worked on</step>
|
|
886
|
-
<step>Check git status for uncommitted changes</step>
|
|
887
|
-
<step>Gather learnings and decisions made</step>
|
|
888
|
-
<step>Identify next steps in priority order</step>
|
|
889
|
-
<step>Write handoff document</step>
|
|
890
|
-
<step>Commit handoff document</step>
|
|
891
|
-
</process>
|
|
892
|
-
|
|
893
|
-
<output-path>
|
|
894
|
-
If ledger exists: thoughts/shared/handoffs/{session-name}/YYYY-MM-DD_HH-MM-SS.md
|
|
895
|
-
Otherwise: thoughts/shared/handoffs/YYYY-MM-DD_HH-MM-SS_description.md
|
|
896
|
-
</output-path>
|
|
897
|
-
|
|
898
|
-
<document-format>
|
|
899
|
-
<frontmatter>
|
|
900
|
-
date: [ISO datetime]
|
|
901
|
-
branch: [branch name]
|
|
902
|
-
commit: [hash]
|
|
903
|
-
session: [session name from ledger, if available]
|
|
904
|
-
</frontmatter>
|
|
905
|
-
<sections>
|
|
906
|
-
<section name="Tasks">Table with Task | Status (completed/in-progress/blocked)</section>
|
|
907
|
-
<section name="Current State">Working on, Blocked by, Plan location</section>
|
|
908
|
-
<section name="Changes Made">file:line - what changed</section>
|
|
909
|
-
<section name="Learnings">Discoveries, gotchas, decisions made and why</section>
|
|
910
|
-
<section name="Next Steps">Prioritized list 1-3</section>
|
|
911
|
-
<section name="Notes">Anything else for next session</section>
|
|
912
|
-
</sections>
|
|
913
|
-
</document-format>
|
|
914
|
-
|
|
915
|
-
<output-summary>
|
|
916
|
-
<template>
|
|
917
|
-
Handoff: [path]
|
|
918
|
-
Tasks: [X done, Y in-progress]
|
|
919
|
-
Next: [top priority]
|
|
920
|
-
</template>
|
|
921
|
-
</output-summary>`
|
|
922
|
-
};
|
|
923
|
-
|
|
924
|
-
// src/agents/handoff-resumer.ts
|
|
925
|
-
var handoffResumerAgent = {
|
|
926
|
-
description: "Resumes work from a handoff document",
|
|
927
|
-
mode: "subagent",
|
|
928
|
-
model: "anthropic/claude-opus-4-5",
|
|
929
|
-
temperature: 0.2,
|
|
930
|
-
tools: {
|
|
931
|
-
write: false,
|
|
932
|
-
edit: false,
|
|
933
|
-
task: false
|
|
934
|
-
},
|
|
935
|
-
prompt: `<purpose>
|
|
936
|
-
Resume work from a handoff document. Verify state before proceeding.
|
|
937
|
-
</purpose>
|
|
938
|
-
|
|
939
|
-
<rules>
|
|
940
|
-
<rule>Read handoff document COMPLETELY</rule>
|
|
941
|
-
<rule>Load ALL referenced artifacts</rule>
|
|
942
|
-
<rule>Verify git state matches</rule>
|
|
943
|
-
<rule>Check for changes since handoff</rule>
|
|
944
|
-
<rule>Report discrepancies before proceeding</rule>
|
|
945
|
-
<rule>Don't assume - verify</rule>
|
|
946
|
-
</rules>
|
|
947
|
-
|
|
948
|
-
<process>
|
|
949
|
-
<step>Find handoff (use provided path or list available)</step>
|
|
950
|
-
<step>Read handoff completely</step>
|
|
951
|
-
<step>Load referenced plans, research, files</step>
|
|
952
|
-
<step>Verify current state matches</step>
|
|
953
|
-
<step>Report analysis</step>
|
|
954
|
-
<step>Wait for confirmation</step>
|
|
955
|
-
</process>
|
|
956
|
-
|
|
957
|
-
<state-verification>
|
|
958
|
-
<check>Current branch</check>
|
|
959
|
-
<check>Commit history (ahead/behind)</check>
|
|
960
|
-
<check>Files mentioned still exist</check>
|
|
961
|
-
<check>Changes mentioned are present</check>
|
|
962
|
-
<check>No conflicting changes made</check>
|
|
963
|
-
</state-verification>
|
|
964
|
-
|
|
965
|
-
<output-format>
|
|
966
|
-
<template>
|
|
967
|
-
## Resuming: [handoff path]
|
|
968
|
-
|
|
969
|
-
**Created**: [date]
|
|
970
|
-
**Branch**: [expected] \u2192 [actual]
|
|
971
|
-
**Commit**: [expected] \u2192 [actual]
|
|
972
|
-
|
|
973
|
-
### State
|
|
974
|
-
- Branch: [matches/differs]
|
|
975
|
-
- Commit: [matches/X ahead/X behind]
|
|
976
|
-
- Files: [verified/issues]
|
|
977
|
-
|
|
978
|
-
### Tasks
|
|
979
|
-
| Task | Status | Verified |
|
|
980
|
-
|------|--------|----------|
|
|
981
|
-
| [Task] | [status] | [yes/no] |
|
|
982
|
-
|
|
983
|
-
### Learnings
|
|
984
|
-
- [From handoff]
|
|
985
|
-
|
|
986
|
-
### Next Action
|
|
987
|
-
[Top priority from handoff]
|
|
988
|
-
|
|
989
|
-
### Loaded
|
|
990
|
-
- [x] [artifact]
|
|
991
|
-
- [x] [artifact]
|
|
992
|
-
</template>
|
|
993
|
-
</output-format>
|
|
994
|
-
|
|
995
|
-
<on-mismatch>
|
|
996
|
-
<action>Report discrepancy and wait for guidance</action>
|
|
997
|
-
<discrepancy>Branch different</discrepancy>
|
|
998
|
-
<discrepancy>Unexpected commits</discrepancy>
|
|
999
|
-
<discrepancy>Files changed/missing</discrepancy>
|
|
1000
|
-
<discrepancy>Conflicting work detected</discrepancy>
|
|
1001
|
-
</on-mismatch>`
|
|
1002
|
-
};
|
|
1003
|
-
|
|
1004
858
|
// src/agents/commander.ts
|
|
1005
859
|
var PROMPT = `<identity>
|
|
1006
860
|
You are Commander - pragmatic software engineer and orchestrator.
|
|
@@ -1078,9 +932,9 @@ Just do it - including obvious follow-up actions.
|
|
|
1078
932
|
<rule>Reference plan file in commit body</rule>
|
|
1079
933
|
</phase>
|
|
1080
934
|
|
|
1081
|
-
<phase name="
|
|
1082
|
-
<
|
|
1083
|
-
<
|
|
935
|
+
<phase name="ledger" trigger="context getting full or session ending">
|
|
936
|
+
<action>System auto-updates ledger at 80% context usage</action>
|
|
937
|
+
<output>thoughts/ledgers/CONTINUITY_{session-name}.md</output>
|
|
1084
938
|
</phase>
|
|
1085
939
|
</workflow>
|
|
1086
940
|
|
|
@@ -1091,8 +945,7 @@ Just do it - including obvious follow-up actions.
|
|
|
1091
945
|
<agent name="pattern-finder" mode="subagent" purpose="Find existing patterns"/>
|
|
1092
946
|
<agent name="planner" mode="subagent" purpose="Create detailed implementation plans"/>
|
|
1093
947
|
<agent name="executor" mode="subagent" purpose="Execute plan (runs implementer then reviewer automatically)"/>
|
|
1094
|
-
<agent name="
|
|
1095
|
-
<agent name="handoff-resumer" mode="subagent" purpose="Resume from handoffs"/>
|
|
948
|
+
<agent name="ledger-creator" mode="subagent" purpose="Create/update continuity ledgers"/>
|
|
1096
949
|
<parallelization>
|
|
1097
950
|
<safe>locator, analyzer, pattern-finder</safe>
|
|
1098
951
|
<sequential>planner then executor</sequential>
|
|
@@ -1186,17 +1039,25 @@ var PROMPT2 = `
|
|
|
1186
1039
|
<subagent name="codebase-locator" spawn="multiple">
|
|
1187
1040
|
Fast file/pattern finder. Spawn multiple with different queries.
|
|
1188
1041
|
Examples: "Find all entry points", "Find all config files", "Find test directories"
|
|
1042
|
+
Invoke with: Task tool, subagent_type="codebase-locator"
|
|
1189
1043
|
</subagent>
|
|
1190
1044
|
<subagent name="codebase-analyzer" spawn="multiple">
|
|
1191
1045
|
Deep module analyzer. Spawn multiple for different areas.
|
|
1192
1046
|
Examples: "Analyze src/core", "Analyze api layer", "Analyze database module"
|
|
1047
|
+
Invoke with: Task tool, subagent_type="codebase-analyzer"
|
|
1193
1048
|
</subagent>
|
|
1194
1049
|
<subagent name="pattern-finder" spawn="multiple">
|
|
1195
1050
|
Pattern extractor. Spawn for different pattern types.
|
|
1196
1051
|
Examples: "Find naming patterns", "Find error handling patterns", "Find async patterns"
|
|
1052
|
+
Invoke with: Task tool, subagent_type="pattern-finder"
|
|
1197
1053
|
</subagent>
|
|
1198
1054
|
</available-subagents>
|
|
1199
1055
|
|
|
1056
|
+
<critical-instruction>
|
|
1057
|
+
You MUST use the Task tool to spawn subagents. Call multiple Task tools in a SINGLE message for parallelism.
|
|
1058
|
+
Example: Task(description="Find entry points", prompt="Find all entry points and main files", subagent_type="codebase-locator")
|
|
1059
|
+
</critical-instruction>
|
|
1060
|
+
|
|
1200
1061
|
<language-detection>
|
|
1201
1062
|
<rule>Identify language(s) by examining file extensions and config files</rule>
|
|
1202
1063
|
<markers>
|
|
@@ -1290,22 +1151,19 @@ var PROMPT2 = `
|
|
|
1290
1151
|
|
|
1291
1152
|
<execution-example>
|
|
1292
1153
|
<step description="Start with maximum parallelism">
|
|
1293
|
-
In a SINGLE message,
|
|
1294
|
-
-
|
|
1295
|
-
-
|
|
1296
|
-
-
|
|
1297
|
-
-
|
|
1298
|
-
-
|
|
1299
|
-
|
|
1300
|
-
AND run tools:
|
|
1154
|
+
In a SINGLE message, call Task tool multiple times AND run other tools:
|
|
1155
|
+
- Task(description="Find entry points", prompt="Find all entry points and main files", subagent_type="codebase-locator")
|
|
1156
|
+
- Task(description="Find configs", prompt="Find all config files (linters, formatters, build)", subagent_type="codebase-locator")
|
|
1157
|
+
- Task(description="Find tests", prompt="Find test directories and test files", subagent_type="codebase-locator")
|
|
1158
|
+
- Task(description="Analyze structure", prompt="Analyze the directory structure and organization", subagent_type="codebase-analyzer")
|
|
1159
|
+
- Task(description="Find patterns", prompt="Find naming conventions used across the codebase", subagent_type="pattern-finder")
|
|
1301
1160
|
- Glob: package.json, pyproject.toml, go.mod, Cargo.toml, etc.
|
|
1302
1161
|
- Glob: README*, ARCHITECTURE*, docs/*
|
|
1303
|
-
- Bash: ls -la (root directory)
|
|
1304
1162
|
</step>
|
|
1305
1163
|
|
|
1306
1164
|
<step description="Parallel deep analysis">
|
|
1307
|
-
Based on discovery, in a SINGLE message
|
|
1308
|
-
-
|
|
1165
|
+
Based on discovery, in a SINGLE message:
|
|
1166
|
+
- Task for each major module: subagent_type="codebase-analyzer"
|
|
1309
1167
|
- Read multiple source files simultaneously
|
|
1310
1168
|
- Read multiple test files simultaneously
|
|
1311
1169
|
</step>
|
|
@@ -1339,19 +1197,50 @@ Create or update a continuity ledger to preserve session state across context cl
|
|
|
1339
1197
|
The ledger captures the essential context needed to resume work seamlessly.
|
|
1340
1198
|
</purpose>
|
|
1341
1199
|
|
|
1200
|
+
<modes>
|
|
1201
|
+
<mode name="initial">Create new ledger when none exists</mode>
|
|
1202
|
+
<mode name="iterative">Update existing ledger with new information</mode>
|
|
1203
|
+
</modes>
|
|
1204
|
+
|
|
1342
1205
|
<rules>
|
|
1343
1206
|
<rule>Keep the ledger CONCISE - only essential information</rule>
|
|
1344
1207
|
<rule>Focus on WHAT and WHY, not HOW</rule>
|
|
1345
|
-
<rule>State should have exactly ONE item in "Now"</rule>
|
|
1346
1208
|
<rule>Mark uncertain information as UNCONFIRMED</rule>
|
|
1347
1209
|
<rule>Include git branch and key file paths</rule>
|
|
1348
1210
|
</rules>
|
|
1349
1211
|
|
|
1212
|
+
<iterative-update-rules>
|
|
1213
|
+
<rule>PRESERVE all existing information from previous ledger</rule>
|
|
1214
|
+
<rule>ADD new progress, decisions, context from new messages</rule>
|
|
1215
|
+
<rule>UPDATE Progress: move In Progress items to Done when completed</rule>
|
|
1216
|
+
<rule>UPDATE Next Steps based on current state</rule>
|
|
1217
|
+
<rule>MERGE file operations: combine previous + new (passed deterministically)</rule>
|
|
1218
|
+
<rule>Never lose information - only add or update</rule>
|
|
1219
|
+
</iterative-update-rules>
|
|
1220
|
+
|
|
1221
|
+
<input-format-for-update>
|
|
1222
|
+
When updating an existing ledger, you will receive:
|
|
1223
|
+
|
|
1224
|
+
<previous-ledger>
|
|
1225
|
+
{content of existing ledger}
|
|
1226
|
+
</previous-ledger>
|
|
1227
|
+
|
|
1228
|
+
<file-operations>
|
|
1229
|
+
Read: path1, path2, path3
|
|
1230
|
+
Modified: path4, path5
|
|
1231
|
+
</file-operations>
|
|
1232
|
+
|
|
1233
|
+
<instruction>
|
|
1234
|
+
Update the ledger with the current session state. Merge the file operations above with any existing ones in the previous ledger.
|
|
1235
|
+
</instruction>
|
|
1236
|
+
</input-format-for-update>
|
|
1237
|
+
|
|
1350
1238
|
<process>
|
|
1351
|
-
<step>Check
|
|
1352
|
-
<step>If
|
|
1353
|
-
<step>If not
|
|
1239
|
+
<step>Check if previous-ledger is provided in input</step>
|
|
1240
|
+
<step>If provided: parse existing content and merge with new state</step>
|
|
1241
|
+
<step>If not: create new ledger with session name from current task</step>
|
|
1354
1242
|
<step>Gather current state: goal, decisions, progress, blockers</step>
|
|
1243
|
+
<step>Merge file operations (previous + new from input)</step>
|
|
1355
1244
|
<step>Write ledger in the exact format below</step>
|
|
1356
1245
|
</process>
|
|
1357
1246
|
|
|
@@ -1362,21 +1251,37 @@ The ledger captures the essential context needed to resume work seamlessly.
|
|
|
1362
1251
|
Updated: {ISO timestamp}
|
|
1363
1252
|
|
|
1364
1253
|
## Goal
|
|
1365
|
-
{
|
|
1254
|
+
{What we're trying to accomplish - one sentence describing success criteria}
|
|
1366
1255
|
|
|
1367
1256
|
## Constraints
|
|
1368
1257
|
{Technical requirements, patterns to follow, things to avoid}
|
|
1369
1258
|
|
|
1259
|
+
## Progress
|
|
1260
|
+
### Done
|
|
1261
|
+
- [x] {Completed items}
|
|
1262
|
+
|
|
1263
|
+
### In Progress
|
|
1264
|
+
- [ ] {Current work - what's actively being worked on}
|
|
1265
|
+
|
|
1266
|
+
### Blocked
|
|
1267
|
+
- {Issues preventing progress, if any}
|
|
1268
|
+
|
|
1370
1269
|
## Key Decisions
|
|
1371
|
-
- {Decision}
|
|
1270
|
+
- **{Decision}**: {Rationale}
|
|
1271
|
+
|
|
1272
|
+
## Next Steps
|
|
1273
|
+
1. {Ordered list of what to do next}
|
|
1372
1274
|
|
|
1373
|
-
##
|
|
1374
|
-
|
|
1375
|
-
-
|
|
1376
|
-
- Next: {Queued items in priority order}
|
|
1275
|
+
## File Operations
|
|
1276
|
+
### Read
|
|
1277
|
+
- \`{paths that were read}\`
|
|
1377
1278
|
|
|
1378
|
-
|
|
1379
|
-
-
|
|
1279
|
+
### Modified
|
|
1280
|
+
- \`{paths that were written or edited}\`
|
|
1281
|
+
|
|
1282
|
+
## Critical Context
|
|
1283
|
+
- {Data, examples, references needed to continue work}
|
|
1284
|
+
- {Important findings or discoveries}
|
|
1380
1285
|
|
|
1381
1286
|
## Working Set
|
|
1382
1287
|
- Branch: \`{branch-name}\`
|
|
@@ -1385,7 +1290,7 @@ Updated: {ISO timestamp}
|
|
|
1385
1290
|
|
|
1386
1291
|
<output-summary>
|
|
1387
1292
|
Ledger updated: thoughts/ledgers/CONTINUITY_{session-name}.md
|
|
1388
|
-
State: {
|
|
1293
|
+
State: {Current In Progress item}
|
|
1389
1294
|
</output-summary>`
|
|
1390
1295
|
};
|
|
1391
1296
|
|
|
@@ -1445,8 +1350,6 @@ var agents = {
|
|
|
1445
1350
|
implementer: implementerAgent,
|
|
1446
1351
|
reviewer: reviewerAgent,
|
|
1447
1352
|
executor: executorAgent,
|
|
1448
|
-
"handoff-creator": handoffCreatorAgent,
|
|
1449
|
-
"handoff-resumer": handoffResumerAgent,
|
|
1450
1353
|
"project-initializer": projectInitializerAgent,
|
|
1451
1354
|
"ledger-creator": ledgerCreatorAgent,
|
|
1452
1355
|
"artifact-searcher": artifactSearcherAgent
|
|
@@ -14144,18 +14047,6 @@ class ArtifactIndex {
|
|
|
14144
14047
|
}
|
|
14145
14048
|
getInlineSchema() {
|
|
14146
14049
|
return `
|
|
14147
|
-
CREATE TABLE IF NOT EXISTS handoffs (
|
|
14148
|
-
id TEXT PRIMARY KEY,
|
|
14149
|
-
session_name TEXT,
|
|
14150
|
-
file_path TEXT UNIQUE NOT NULL,
|
|
14151
|
-
task_summary TEXT,
|
|
14152
|
-
what_worked TEXT,
|
|
14153
|
-
what_failed TEXT,
|
|
14154
|
-
learnings TEXT,
|
|
14155
|
-
outcome TEXT,
|
|
14156
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
14157
|
-
indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
14158
|
-
);
|
|
14159
14050
|
CREATE TABLE IF NOT EXISTS plans (
|
|
14160
14051
|
id TEXT PRIMARY KEY,
|
|
14161
14052
|
title TEXT,
|
|
@@ -14172,55 +14063,15 @@ class ArtifactIndex {
|
|
|
14172
14063
|
goal TEXT,
|
|
14173
14064
|
state_now TEXT,
|
|
14174
14065
|
key_decisions TEXT,
|
|
14066
|
+
files_read TEXT,
|
|
14067
|
+
files_modified TEXT,
|
|
14175
14068
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
14176
14069
|
indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
14177
14070
|
);
|
|
14178
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS handoffs_fts USING fts5(id, session_name, task_summary, what_worked, what_failed, learnings);
|
|
14179
14071
|
CREATE VIRTUAL TABLE IF NOT EXISTS plans_fts USING fts5(id, title, overview, approach);
|
|
14180
14072
|
CREATE VIRTUAL TABLE IF NOT EXISTS ledgers_fts USING fts5(id, session_name, goal, state_now, key_decisions);
|
|
14181
14073
|
`;
|
|
14182
14074
|
}
|
|
14183
|
-
async indexHandoff(record2) {
|
|
14184
|
-
if (!this.db)
|
|
14185
|
-
throw new Error("Database not initialized");
|
|
14186
|
-
const existing = this.db.query(`SELECT id FROM handoffs WHERE file_path = ?`).get(record2.filePath);
|
|
14187
|
-
if (existing) {
|
|
14188
|
-
this.db.run(`DELETE FROM handoffs_fts WHERE id = ?`, [existing.id]);
|
|
14189
|
-
}
|
|
14190
|
-
this.db.run(`
|
|
14191
|
-
INSERT INTO handoffs (id, session_name, file_path, task_summary, what_worked, what_failed, learnings, outcome, indexed_at)
|
|
14192
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
14193
|
-
ON CONFLICT(file_path) DO UPDATE SET
|
|
14194
|
-
id = excluded.id,
|
|
14195
|
-
session_name = excluded.session_name,
|
|
14196
|
-
task_summary = excluded.task_summary,
|
|
14197
|
-
what_worked = excluded.what_worked,
|
|
14198
|
-
what_failed = excluded.what_failed,
|
|
14199
|
-
learnings = excluded.learnings,
|
|
14200
|
-
outcome = excluded.outcome,
|
|
14201
|
-
indexed_at = CURRENT_TIMESTAMP
|
|
14202
|
-
`, [
|
|
14203
|
-
record2.id,
|
|
14204
|
-
record2.sessionName ?? null,
|
|
14205
|
-
record2.filePath,
|
|
14206
|
-
record2.taskSummary ?? null,
|
|
14207
|
-
record2.whatWorked ?? null,
|
|
14208
|
-
record2.whatFailed ?? null,
|
|
14209
|
-
record2.learnings ?? null,
|
|
14210
|
-
record2.outcome ?? null
|
|
14211
|
-
]);
|
|
14212
|
-
this.db.run(`
|
|
14213
|
-
INSERT INTO handoffs_fts (id, session_name, task_summary, what_worked, what_failed, learnings)
|
|
14214
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
14215
|
-
`, [
|
|
14216
|
-
record2.id,
|
|
14217
|
-
record2.sessionName ?? null,
|
|
14218
|
-
record2.taskSummary ?? null,
|
|
14219
|
-
record2.whatWorked ?? null,
|
|
14220
|
-
record2.whatFailed ?? null,
|
|
14221
|
-
record2.learnings ?? null
|
|
14222
|
-
]);
|
|
14223
|
-
}
|
|
14224
14075
|
async indexPlan(record2) {
|
|
14225
14076
|
if (!this.db)
|
|
14226
14077
|
throw new Error("Database not initialized");
|
|
@@ -14251,14 +14102,16 @@ class ArtifactIndex {
|
|
|
14251
14102
|
this.db.run(`DELETE FROM ledgers_fts WHERE id = ?`, [existing.id]);
|
|
14252
14103
|
}
|
|
14253
14104
|
this.db.run(`
|
|
14254
|
-
INSERT INTO ledgers (id, session_name, file_path, goal, state_now, key_decisions, indexed_at)
|
|
14255
|
-
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
14105
|
+
INSERT INTO ledgers (id, session_name, file_path, goal, state_now, key_decisions, files_read, files_modified, indexed_at)
|
|
14106
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
14256
14107
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
14257
14108
|
id = excluded.id,
|
|
14258
14109
|
session_name = excluded.session_name,
|
|
14259
14110
|
goal = excluded.goal,
|
|
14260
14111
|
state_now = excluded.state_now,
|
|
14261
14112
|
key_decisions = excluded.key_decisions,
|
|
14113
|
+
files_read = excluded.files_read,
|
|
14114
|
+
files_modified = excluded.files_modified,
|
|
14262
14115
|
indexed_at = CURRENT_TIMESTAMP
|
|
14263
14116
|
`, [
|
|
14264
14117
|
record2.id,
|
|
@@ -14266,7 +14119,9 @@ class ArtifactIndex {
|
|
|
14266
14119
|
record2.filePath,
|
|
14267
14120
|
record2.goal ?? null,
|
|
14268
14121
|
record2.stateNow ?? null,
|
|
14269
|
-
record2.keyDecisions ?? null
|
|
14122
|
+
record2.keyDecisions ?? null,
|
|
14123
|
+
record2.filesRead ?? null,
|
|
14124
|
+
record2.filesModified ?? null
|
|
14270
14125
|
]);
|
|
14271
14126
|
this.db.run(`
|
|
14272
14127
|
INSERT INTO ledgers_fts (id, session_name, goal, state_now, key_decisions)
|
|
@@ -14284,23 +14139,6 @@ class ArtifactIndex {
|
|
|
14284
14139
|
throw new Error("Database not initialized");
|
|
14285
14140
|
const results = [];
|
|
14286
14141
|
const escapedQuery = this.escapeFtsQuery(query);
|
|
14287
|
-
const handoffs = this.db.query(`
|
|
14288
|
-
SELECT h.id, h.file_path, h.task_summary, rank
|
|
14289
|
-
FROM handoffs_fts
|
|
14290
|
-
JOIN handoffs h ON handoffs_fts.id = h.id
|
|
14291
|
-
WHERE handoffs_fts MATCH ?
|
|
14292
|
-
ORDER BY rank
|
|
14293
|
-
LIMIT ?
|
|
14294
|
-
`).all(escapedQuery, limit);
|
|
14295
|
-
for (const row of handoffs) {
|
|
14296
|
-
results.push({
|
|
14297
|
-
type: "handoff",
|
|
14298
|
-
id: row.id,
|
|
14299
|
-
filePath: row.file_path,
|
|
14300
|
-
summary: row.task_summary,
|
|
14301
|
-
score: -row.rank
|
|
14302
|
-
});
|
|
14303
|
-
}
|
|
14304
14142
|
const plans = this.db.query(`
|
|
14305
14143
|
SELECT p.id, p.file_path, p.title, rank
|
|
14306
14144
|
FROM plans_fts
|
|
@@ -14360,7 +14198,7 @@ async function getArtifactIndex() {
|
|
|
14360
14198
|
|
|
14361
14199
|
// src/tools/artifact-search.ts
|
|
14362
14200
|
var artifact_search = tool({
|
|
14363
|
-
description: `Search past
|
|
14201
|
+
description: `Search past plans and ledgers for relevant precedent.
|
|
14364
14202
|
Use this to find:
|
|
14365
14203
|
- Similar problems you've solved before
|
|
14366
14204
|
- Patterns and approaches that worked
|
|
@@ -14369,7 +14207,7 @@ Returns ranked results with file paths for further reading.`,
|
|
|
14369
14207
|
args: {
|
|
14370
14208
|
query: tool.schema.string().describe("Search query - describe what you're looking for"),
|
|
14371
14209
|
limit: tool.schema.number().optional().describe("Max results to return (default: 10)"),
|
|
14372
|
-
type: tool.schema.enum(["all", "
|
|
14210
|
+
type: tool.schema.enum(["all", "plan", "ledger"]).optional().describe("Filter by artifact type (default: all)")
|
|
14373
14211
|
},
|
|
14374
14212
|
execute: async (args) => {
|
|
14375
14213
|
try {
|
|
@@ -15261,6 +15099,69 @@ ${output.system}`;
|
|
|
15261
15099
|
};
|
|
15262
15100
|
}
|
|
15263
15101
|
|
|
15102
|
+
// src/hooks/file-ops-tracker.ts
|
|
15103
|
+
var sessionFileOps = new Map;
|
|
15104
|
+
function getOrCreateOps(sessionID) {
|
|
15105
|
+
let ops = sessionFileOps.get(sessionID);
|
|
15106
|
+
if (!ops) {
|
|
15107
|
+
ops = { read: new Set, modified: new Set };
|
|
15108
|
+
sessionFileOps.set(sessionID, ops);
|
|
15109
|
+
}
|
|
15110
|
+
return ops;
|
|
15111
|
+
}
|
|
15112
|
+
function trackFileOp(sessionID, operation, filePath) {
|
|
15113
|
+
const ops = getOrCreateOps(sessionID);
|
|
15114
|
+
if (operation === "read") {
|
|
15115
|
+
ops.read.add(filePath);
|
|
15116
|
+
} else {
|
|
15117
|
+
ops.modified.add(filePath);
|
|
15118
|
+
}
|
|
15119
|
+
}
|
|
15120
|
+
function getFileOps(sessionID) {
|
|
15121
|
+
const ops = sessionFileOps.get(sessionID);
|
|
15122
|
+
if (!ops) {
|
|
15123
|
+
return { read: new Set, modified: new Set };
|
|
15124
|
+
}
|
|
15125
|
+
return ops;
|
|
15126
|
+
}
|
|
15127
|
+
function clearFileOps(sessionID) {
|
|
15128
|
+
sessionFileOps.delete(sessionID);
|
|
15129
|
+
}
|
|
15130
|
+
function formatFileOpsForPrompt(ops) {
|
|
15131
|
+
const readPaths = Array.from(ops.read).sort();
|
|
15132
|
+
const modifiedPaths = Array.from(ops.modified).sort();
|
|
15133
|
+
let result = `<file-operations>
|
|
15134
|
+
`;
|
|
15135
|
+
result += `Read: ${readPaths.length > 0 ? readPaths.join(", ") : "(none)"}
|
|
15136
|
+
`;
|
|
15137
|
+
result += `Modified: ${modifiedPaths.length > 0 ? modifiedPaths.join(", ") : "(none)"}
|
|
15138
|
+
`;
|
|
15139
|
+
result += "</file-operations>";
|
|
15140
|
+
return result;
|
|
15141
|
+
}
|
|
15142
|
+
function createFileOpsTrackerHook(_ctx) {
|
|
15143
|
+
return {
|
|
15144
|
+
"tool.execute.after": async (input, _output) => {
|
|
15145
|
+
const toolName = input.tool.toLowerCase();
|
|
15146
|
+
if (!["read", "write", "edit"].includes(toolName)) {
|
|
15147
|
+
return;
|
|
15148
|
+
}
|
|
15149
|
+
const filePath = input.args?.filePath;
|
|
15150
|
+
if (!filePath)
|
|
15151
|
+
return;
|
|
15152
|
+
trackFileOp(input.sessionID, toolName, filePath);
|
|
15153
|
+
},
|
|
15154
|
+
event: async ({ event }) => {
|
|
15155
|
+
if (event.type === "session.deleted") {
|
|
15156
|
+
const props = event.properties;
|
|
15157
|
+
if (props?.info?.id) {
|
|
15158
|
+
clearFileOps(props.info.id);
|
|
15159
|
+
}
|
|
15160
|
+
}
|
|
15161
|
+
}
|
|
15162
|
+
};
|
|
15163
|
+
}
|
|
15164
|
+
|
|
15264
15165
|
// src/hooks/auto-clear-ledger.ts
|
|
15265
15166
|
var MODEL_CONTEXT_LIMITS2 = {
|
|
15266
15167
|
"claude-opus": 200000,
|
|
@@ -15337,23 +15238,40 @@ function createAutoClearLedgerHook(ctx) {
|
|
|
15337
15238
|
duration: 3000
|
|
15338
15239
|
}
|
|
15339
15240
|
}).catch(() => {});
|
|
15241
|
+
const fileOps = getFileOps(sessionID);
|
|
15242
|
+
const existingLedger = await findCurrentLedger(ctx.directory);
|
|
15340
15243
|
const ledgerSessionResp = await ctx.client.session.create({
|
|
15341
15244
|
body: {},
|
|
15342
15245
|
query: { directory: ctx.directory }
|
|
15343
15246
|
});
|
|
15344
15247
|
const ledgerSessionID = ledgerSessionResp.data?.id;
|
|
15345
15248
|
if (ledgerSessionID) {
|
|
15249
|
+
let promptText = "";
|
|
15250
|
+
if (existingLedger) {
|
|
15251
|
+
promptText += `<previous-ledger>
|
|
15252
|
+
${existingLedger.content}
|
|
15253
|
+
</previous-ledger>
|
|
15254
|
+
|
|
15255
|
+
`;
|
|
15256
|
+
}
|
|
15257
|
+
promptText += formatFileOpsForPrompt(fileOps);
|
|
15258
|
+
promptText += `
|
|
15259
|
+
|
|
15260
|
+
<instruction>
|
|
15261
|
+
`;
|
|
15262
|
+
promptText += existingLedger ? "Update the ledger with the current session state. Merge the file operations above with any existing ones in the previous ledger." : "Create a new continuity ledger for this session.";
|
|
15263
|
+
promptText += `
|
|
15264
|
+
</instruction>`;
|
|
15346
15265
|
await ctx.client.session.prompt({
|
|
15347
15266
|
path: { id: ledgerSessionID },
|
|
15348
15267
|
body: {
|
|
15349
|
-
parts: [
|
|
15350
|
-
{ type: "text", text: "Update the continuity ledger with current session state before context clear." }
|
|
15351
|
-
],
|
|
15268
|
+
parts: [{ type: "text", text: promptText }],
|
|
15352
15269
|
agent: "ledger-creator"
|
|
15353
15270
|
},
|
|
15354
15271
|
query: { directory: ctx.directory }
|
|
15355
15272
|
});
|
|
15356
15273
|
let attempts = 0;
|
|
15274
|
+
let ledgerCompleted = false;
|
|
15357
15275
|
while (attempts < 30) {
|
|
15358
15276
|
await new Promise((resolve2) => setTimeout(resolve2, 2000));
|
|
15359
15277
|
const statusResp = await ctx.client.session.get({
|
|
@@ -15361,41 +15279,13 @@ function createAutoClearLedgerHook(ctx) {
|
|
|
15361
15279
|
query: { directory: ctx.directory }
|
|
15362
15280
|
});
|
|
15363
15281
|
if (statusResp.data?.status === "idle") {
|
|
15282
|
+
ledgerCompleted = true;
|
|
15364
15283
|
break;
|
|
15365
15284
|
}
|
|
15366
15285
|
attempts++;
|
|
15367
15286
|
}
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
body: {},
|
|
15371
|
-
query: { directory: ctx.directory }
|
|
15372
|
-
});
|
|
15373
|
-
const handoffSessionID = handoffSessionResp.data?.id;
|
|
15374
|
-
if (handoffSessionID) {
|
|
15375
|
-
await ctx.client.session.prompt({
|
|
15376
|
-
path: { id: handoffSessionID },
|
|
15377
|
-
body: {
|
|
15378
|
-
parts: [
|
|
15379
|
-
{
|
|
15380
|
-
type: "text",
|
|
15381
|
-
text: "Create a handoff document. Read the current ledger at thoughts/ledgers/ for context."
|
|
15382
|
-
}
|
|
15383
|
-
],
|
|
15384
|
-
agent: "handoff-creator"
|
|
15385
|
-
},
|
|
15386
|
-
query: { directory: ctx.directory }
|
|
15387
|
-
});
|
|
15388
|
-
let attempts = 0;
|
|
15389
|
-
while (attempts < 30) {
|
|
15390
|
-
await new Promise((resolve2) => setTimeout(resolve2, 2000));
|
|
15391
|
-
const statusResp = await ctx.client.session.get({
|
|
15392
|
-
path: { id: handoffSessionID },
|
|
15393
|
-
query: { directory: ctx.directory }
|
|
15394
|
-
});
|
|
15395
|
-
if (statusResp.data?.status === "idle") {
|
|
15396
|
-
break;
|
|
15397
|
-
}
|
|
15398
|
-
attempts++;
|
|
15287
|
+
if (ledgerCompleted) {
|
|
15288
|
+
clearFileOps(sessionID);
|
|
15399
15289
|
}
|
|
15400
15290
|
}
|
|
15401
15291
|
const firstMessage = messages[0];
|
|
@@ -15423,7 +15313,7 @@ function createAutoClearLedgerHook(ctx) {
|
|
|
15423
15313
|
await ctx.client.tui.showToast({
|
|
15424
15314
|
body: {
|
|
15425
15315
|
title: "Context Cleared",
|
|
15426
|
-
message: "Ledger
|
|
15316
|
+
message: "Ledger saved. Session ready to continue.",
|
|
15427
15317
|
variant: "success",
|
|
15428
15318
|
duration: 5000
|
|
15429
15319
|
}
|
|
@@ -15475,41 +15365,35 @@ function createAutoClearLedgerHook(ctx) {
|
|
|
15475
15365
|
// src/hooks/artifact-auto-index.ts
|
|
15476
15366
|
import { readFileSync as readFileSync3 } from "fs";
|
|
15477
15367
|
var LEDGER_PATH_PATTERN = /thoughts\/ledgers\/CONTINUITY_(.+)\.md$/;
|
|
15478
|
-
var HANDOFF_PATH_PATTERN = /thoughts\/shared\/handoffs\/(.+)\.md$/;
|
|
15479
15368
|
var PLAN_PATH_PATTERN = /thoughts\/shared\/plans\/(.+)\.md$/;
|
|
15480
15369
|
function parseLedger(content, filePath, sessionName) {
|
|
15481
15370
|
const goalMatch = content.match(/## Goal\n([^\n]+)/);
|
|
15482
|
-
const stateMatch = content.match(
|
|
15371
|
+
const stateMatch = content.match(/### In Progress\n- \[ \] ([^\n]+)/);
|
|
15483
15372
|
const decisionsMatch = content.match(/## Key Decisions\n([\s\S]*?)(?=\n## |$)/);
|
|
15373
|
+
const fileOpsSection = content.match(/## File Operations\n([\s\S]*?)(?=\n## |$)/);
|
|
15374
|
+
let filesRead = "";
|
|
15375
|
+
let filesModified = "";
|
|
15376
|
+
if (fileOpsSection) {
|
|
15377
|
+
const readMatch = fileOpsSection[1].match(/### Read\n([\s\S]*?)(?=\n### |$)/);
|
|
15378
|
+
const modifiedMatch = fileOpsSection[1].match(/### Modified\n([\s\S]*?)(?=\n### |$)/);
|
|
15379
|
+
if (readMatch) {
|
|
15380
|
+
const paths = readMatch[1].match(/`([^`]+)`/g);
|
|
15381
|
+
filesRead = paths ? paths.map((p) => p.replace(/`/g, "")).join(",") : "";
|
|
15382
|
+
}
|
|
15383
|
+
if (modifiedMatch) {
|
|
15384
|
+
const paths = modifiedMatch[1].match(/`([^`]+)`/g);
|
|
15385
|
+
filesModified = paths ? paths.map((p) => p.replace(/`/g, "")).join(",") : "";
|
|
15386
|
+
}
|
|
15387
|
+
}
|
|
15484
15388
|
return {
|
|
15485
15389
|
id: `ledger-${sessionName}`,
|
|
15486
15390
|
sessionName,
|
|
15487
15391
|
filePath,
|
|
15488
15392
|
goal: goalMatch?.[1] || "",
|
|
15489
15393
|
stateNow: stateMatch?.[1] || "",
|
|
15490
|
-
keyDecisions: decisionsMatch?.[1]?.trim() || ""
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
function parseHandoff(content, filePath, fileName) {
|
|
15494
|
-
const sessionMatch = content.match(/^session:\s*(.+)$/m);
|
|
15495
|
-
const sessionName = sessionMatch?.[1] || fileName;
|
|
15496
|
-
const taskMatch = content.match(/\*\*Working on:\*\*\s*([^\n]+)/);
|
|
15497
|
-
const taskSummary = taskMatch?.[1] || "";
|
|
15498
|
-
const learningsMatch = content.match(/## Learnings\n\n([\s\S]*?)(?=\n## |$)/);
|
|
15499
|
-
const learnings = learningsMatch?.[1]?.trim() || "";
|
|
15500
|
-
const workedMatch = content.match(/## What Worked\n\n([\s\S]*?)(?=\n## |$)/);
|
|
15501
|
-
const whatWorked = workedMatch?.[1]?.trim() || learnings;
|
|
15502
|
-
const failedMatch = content.match(/## What Failed\n\n([\s\S]*?)(?=\n## |$)/);
|
|
15503
|
-
const whatFailed = failedMatch?.[1]?.trim() || "";
|
|
15504
|
-
return {
|
|
15505
|
-
id: `handoff-${fileName}`,
|
|
15506
|
-
sessionName,
|
|
15507
|
-
filePath,
|
|
15508
|
-
taskSummary,
|
|
15509
|
-
whatWorked,
|
|
15510
|
-
whatFailed,
|
|
15511
|
-
learnings,
|
|
15512
|
-
outcome: "UNKNOWN"
|
|
15394
|
+
keyDecisions: decisionsMatch?.[1]?.trim() || "",
|
|
15395
|
+
filesRead,
|
|
15396
|
+
filesModified
|
|
15513
15397
|
};
|
|
15514
15398
|
}
|
|
15515
15399
|
function parsePlan(content, filePath, fileName) {
|
|
@@ -15545,15 +15429,6 @@ function createArtifactAutoIndexHook(_ctx) {
|
|
|
15545
15429
|
console.log(`[artifact-auto-index] Indexed ledger: ${filePath}`);
|
|
15546
15430
|
return;
|
|
15547
15431
|
}
|
|
15548
|
-
const handoffMatch = filePath.match(HANDOFF_PATH_PATTERN);
|
|
15549
|
-
if (handoffMatch) {
|
|
15550
|
-
const content = readFileSync3(filePath, "utf-8");
|
|
15551
|
-
const index = await getArtifactIndex();
|
|
15552
|
-
const record2 = parseHandoff(content, filePath, handoffMatch[1]);
|
|
15553
|
-
await index.indexHandoff(record2);
|
|
15554
|
-
console.log(`[artifact-auto-index] Indexed handoff: ${filePath}`);
|
|
15555
|
-
return;
|
|
15556
|
-
}
|
|
15557
15432
|
const planMatch = filePath.match(PLAN_PATH_PATTERN);
|
|
15558
15433
|
if (planMatch) {
|
|
15559
15434
|
const content = readFileSync3(filePath, "utf-8");
|
|
@@ -16062,6 +15937,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
|
|
|
16062
15937
|
const contextWindowMonitorHook = createContextWindowMonitorHook(ctx);
|
|
16063
15938
|
const commentCheckerHook = createCommentCheckerHook(ctx);
|
|
16064
15939
|
const artifactAutoIndexHook = createArtifactAutoIndexHook(ctx);
|
|
15940
|
+
const fileOpsTrackerHook = createFileOpsTrackerHook(ctx);
|
|
16065
15941
|
const backgroundTaskManager = new BackgroundTaskManager(ctx);
|
|
16066
15942
|
const backgroundTaskTools = createBackgroundTaskTools(backgroundTaskManager);
|
|
16067
15943
|
return {
|
|
@@ -16083,11 +15959,13 @@ var OpenCodeConfigPlugin = async (ctx) => {
|
|
|
16083
15959
|
};
|
|
16084
15960
|
const mergedAgents = mergeAgentConfigs(agents, userConfig);
|
|
16085
15961
|
config2.agent = {
|
|
16086
|
-
[PRIMARY_AGENT_NAME]: mergedAgents[PRIMARY_AGENT_NAME],
|
|
16087
|
-
...Object.fromEntries(Object.entries(mergedAgents).filter(([k]) => k !== PRIMARY_AGENT_NAME)),
|
|
16088
15962
|
...config2.agent,
|
|
16089
15963
|
build: { ...config2.agent?.build, mode: "subagent" },
|
|
16090
|
-
plan: { ...config2.agent?.plan, mode: "subagent" }
|
|
15964
|
+
plan: { ...config2.agent?.plan, mode: "subagent" },
|
|
15965
|
+
triage: { ...config2.agent?.triage, mode: "subagent" },
|
|
15966
|
+
docs: { ...config2.agent?.docs, mode: "subagent" },
|
|
15967
|
+
...Object.fromEntries(Object.entries(mergedAgents).filter(([k]) => k !== PRIMARY_AGENT_NAME)),
|
|
15968
|
+
[PRIMARY_AGENT_NAME]: mergedAgents[PRIMARY_AGENT_NAME]
|
|
16091
15969
|
};
|
|
16092
15970
|
config2.mcp = {
|
|
16093
15971
|
...config2.mcp,
|
|
@@ -16135,6 +16013,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
|
|
|
16135
16013
|
await commentCheckerHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
|
|
16136
16014
|
await contextInjectorHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
|
|
16137
16015
|
await artifactAutoIndexHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
|
|
16016
|
+
await fileOpsTrackerHook["tool.execute.after"]({ tool: input.tool, sessionID: input.sessionID, args: input.args }, output);
|
|
16138
16017
|
},
|
|
16139
16018
|
event: async ({ event }) => {
|
|
16140
16019
|
if (event.type === "session.deleted") {
|
|
@@ -16149,6 +16028,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
|
|
|
16149
16028
|
await tokenAwareTruncationHook.event({ event });
|
|
16150
16029
|
await contextWindowMonitorHook.event({ event });
|
|
16151
16030
|
backgroundTaskManager.handleEvent(event);
|
|
16031
|
+
await fileOpsTrackerHook.event({ event });
|
|
16152
16032
|
}
|
|
16153
16033
|
};
|
|
16154
16034
|
};
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
export interface HandoffRecord {
|
|
2
|
-
id: string;
|
|
3
|
-
sessionName?: string;
|
|
4
|
-
filePath: string;
|
|
5
|
-
taskSummary?: string;
|
|
6
|
-
whatWorked?: string;
|
|
7
|
-
whatFailed?: string;
|
|
8
|
-
learnings?: string;
|
|
9
|
-
outcome?: "SUCCEEDED" | "PARTIAL_PLUS" | "PARTIAL_MINUS" | "FAILED" | "UNKNOWN";
|
|
10
|
-
}
|
|
11
1
|
export interface PlanRecord {
|
|
12
2
|
id: string;
|
|
13
3
|
title?: string;
|
|
@@ -22,9 +12,11 @@ export interface LedgerRecord {
|
|
|
22
12
|
goal?: string;
|
|
23
13
|
stateNow?: string;
|
|
24
14
|
keyDecisions?: string;
|
|
15
|
+
filesRead?: string;
|
|
16
|
+
filesModified?: string;
|
|
25
17
|
}
|
|
26
18
|
export interface SearchResult {
|
|
27
|
-
type: "
|
|
19
|
+
type: "plan" | "ledger";
|
|
28
20
|
id: string;
|
|
29
21
|
filePath: string;
|
|
30
22
|
title?: string;
|
|
@@ -37,7 +29,6 @@ export declare class ArtifactIndex {
|
|
|
37
29
|
constructor(dbDir?: string);
|
|
38
30
|
initialize(): Promise<void>;
|
|
39
31
|
private getInlineSchema;
|
|
40
|
-
indexHandoff(record: HandoffRecord): Promise<void>;
|
|
41
32
|
indexPlan(record: PlanRecord): Promise<void>;
|
|
42
33
|
indexLedger(record: LedgerRecord): Promise<void>;
|
|
43
34
|
search(query: string, limit?: number): Promise<SearchResult[]>;
|
|
@@ -5,7 +5,6 @@ export declare const artifact_search: {
|
|
|
5
5
|
limit: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
6
6
|
type: import("zod").ZodOptional<import("zod").ZodEnum<{
|
|
7
7
|
all: "all";
|
|
8
|
-
handoff: "handoff";
|
|
9
8
|
plan: "plan";
|
|
10
9
|
ledger: "ledger";
|
|
11
10
|
}>>;
|
|
@@ -13,6 +12,6 @@ export declare const artifact_search: {
|
|
|
13
12
|
execute(args: {
|
|
14
13
|
query: string;
|
|
15
14
|
limit?: number | undefined;
|
|
16
|
-
type?: "all" | "
|
|
15
|
+
type?: "all" | "plan" | "ledger" | undefined;
|
|
17
16
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
18
17
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "micode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "OpenCode plugin with Brainstorm-Research-Plan-Implement workflow",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"url": "https://github.com/vtemian/micode/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@opencode-ai/plugin": "1.0.
|
|
47
|
+
"@opencode-ai/plugin": "^1.0.219"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@biomejs/biome": "^2.3.10",
|