opencodekit 0.15.21 → 0.16.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 (144) hide show
  1. package/dist/index.js +5 -7
  2. package/dist/template/.opencode/AGENTS.md +85 -23
  3. package/dist/template/.opencode/agent/build.md +88 -7
  4. package/dist/template/.opencode/agent/explore.md +1 -1
  5. package/dist/template/.opencode/agent/general.md +54 -4
  6. package/dist/template/.opencode/agent/looker.md +1 -1
  7. package/dist/template/.opencode/agent/painter.md +1 -1
  8. package/dist/template/.opencode/agent/plan.md +52 -0
  9. package/dist/template/.opencode/agent/review.md +1 -1
  10. package/dist/template/.opencode/agent/scout.md +3 -3
  11. package/dist/template/.opencode/agent/vision.md +1 -1
  12. package/dist/template/.opencode/command/create.md +231 -91
  13. package/dist/template/.opencode/command/design.md +40 -7
  14. package/dist/template/.opencode/command/handoff.md +22 -0
  15. package/dist/template/.opencode/command/init.md +49 -78
  16. package/dist/template/.opencode/command/plan.md +36 -16
  17. package/dist/template/.opencode/command/pr.md +15 -0
  18. package/dist/template/.opencode/command/research.md +3 -0
  19. package/dist/template/.opencode/command/resume.md +8 -18
  20. package/dist/template/.opencode/command/review-codebase.md +30 -0
  21. package/dist/template/.opencode/command/ship.md +199 -0
  22. package/dist/template/.opencode/command/start.md +316 -28
  23. package/dist/template/.opencode/command/status.md +24 -1
  24. package/dist/template/.opencode/command/ui-review.md +36 -7
  25. package/dist/template/.opencode/command/verify.md +307 -0
  26. package/dist/template/.opencode/memory/_templates/prd.md +29 -0
  27. package/dist/template/.opencode/memory/_templates/proposal.md +38 -0
  28. package/dist/template/.opencode/memory/_templates/spec.md +66 -0
  29. package/dist/template/.opencode/memory/_templates/tasks.md +198 -0
  30. package/dist/template/.opencode/memory/_templates/tech-stack.md +50 -0
  31. package/dist/template/.opencode/memory/project/tech-stack.md +53 -0
  32. package/dist/template/.opencode/memory/research/ccpm-analysis.md +334 -0
  33. package/dist/template/.opencode/memory/research/openspec-analysis.md +226 -0
  34. package/dist/template/.opencode/memory.db +0 -0
  35. package/dist/template/.opencode/memory.db-shm +0 -0
  36. package/dist/template/.opencode/memory.db-wal +0 -0
  37. package/dist/template/.opencode/opencode.json +18 -4
  38. package/dist/template/.opencode/package.json +1 -0
  39. package/dist/template/.opencode/plans/1770006237537-mighty-otter.md +418 -0
  40. package/dist/template/.opencode/plans/1770006913647-glowing-forest.md +170 -0
  41. package/dist/template/.opencode/plans/1770013678126-witty-planet.md +278 -0
  42. package/dist/template/.opencode/plugin/lib/memory-db.ts +828 -0
  43. package/dist/template/.opencode/plugin/memory.ts +38 -1
  44. package/dist/template/.opencode/skill/index-knowledge/SKILL.md +76 -31
  45. package/dist/template/.opencode/skill/memory-system/SKILL.md +110 -55
  46. package/dist/template/.opencode/skill/tool-priority/SKILL.md +2 -2
  47. package/dist/template/.opencode/tool/memory-get.ts +143 -0
  48. package/dist/template/.opencode/tool/memory-maintain.ts +167 -0
  49. package/dist/template/.opencode/tool/memory-migrate.ts +319 -0
  50. package/dist/template/.opencode/tool/memory-read.ts +17 -46
  51. package/dist/template/.opencode/tool/memory-search.ts +131 -28
  52. package/dist/template/.opencode/tool/memory-timeline.ts +105 -0
  53. package/dist/template/.opencode/tool/memory-update.ts +21 -26
  54. package/dist/template/.opencode/tool/observation.ts +112 -100
  55. package/dist/template/.opencode/tsconfig.json +19 -19
  56. package/package.json +1 -1
  57. package/dist/template/.opencode/command/accessibility-check.md +0 -331
  58. package/dist/template/.opencode/command/agent-browser.md +0 -21
  59. package/dist/template/.opencode/command/analyze-mockup.md +0 -423
  60. package/dist/template/.opencode/command/analyze-project.md +0 -295
  61. package/dist/template/.opencode/command/brainstorm.md +0 -373
  62. package/dist/template/.opencode/command/cloudflare.md +0 -70
  63. package/dist/template/.opencode/command/commit.md +0 -245
  64. package/dist/template/.opencode/command/complete-next-task.md +0 -77
  65. package/dist/template/.opencode/command/design-audit.md +0 -480
  66. package/dist/template/.opencode/command/edit-image.md +0 -242
  67. package/dist/template/.opencode/command/finish.md +0 -255
  68. package/dist/template/.opencode/command/fix-ci.md +0 -109
  69. package/dist/template/.opencode/command/fix-types.md +0 -104
  70. package/dist/template/.opencode/command/fix-ui.md +0 -117
  71. package/dist/template/.opencode/command/fix.md +0 -168
  72. package/dist/template/.opencode/command/frontend-design.md +0 -21
  73. package/dist/template/.opencode/command/generate-diagram.md +0 -349
  74. package/dist/template/.opencode/command/generate-icon.md +0 -283
  75. package/dist/template/.opencode/command/generate-image.md +0 -246
  76. package/dist/template/.opencode/command/generate-pattern.md +0 -247
  77. package/dist/template/.opencode/command/generate-storyboard.md +0 -250
  78. package/dist/template/.opencode/command/implement.md +0 -609
  79. package/dist/template/.opencode/command/import-plan.md +0 -406
  80. package/dist/template/.opencode/command/index-knowledge.md +0 -25
  81. package/dist/template/.opencode/command/integration-test.md +0 -424
  82. package/dist/template/.opencode/command/issue.md +0 -102
  83. package/dist/template/.opencode/command/new-feature.md +0 -651
  84. package/dist/template/.opencode/command/opensrc.md +0 -58
  85. package/dist/template/.opencode/command/quick-build.md +0 -238
  86. package/dist/template/.opencode/command/ralph.md +0 -41
  87. package/dist/template/.opencode/command/research-and-implement.md +0 -148
  88. package/dist/template/.opencode/command/research-ui.md +0 -466
  89. package/dist/template/.opencode/command/restore-image.md +0 -424
  90. package/dist/template/.opencode/command/revert-feature.md +0 -386
  91. package/dist/template/.opencode/command/skill-create.md +0 -517
  92. package/dist/template/.opencode/command/skill-optimize.md +0 -556
  93. package/dist/template/.opencode/command/summarize.md +0 -412
  94. package/dist/template/.opencode/command/triage.md +0 -398
  95. package/dist/template/.opencode/memory/_templates/README.md +0 -35
  96. package/dist/template/.opencode/memory/_templates/observation.md +0 -39
  97. package/dist/template/.opencode/memory/_templates/project/architecture.md +0 -60
  98. package/dist/template/.opencode/memory/_templates/project/commands.md +0 -72
  99. package/dist/template/.opencode/memory/_templates/project/conventions.md +0 -68
  100. package/dist/template/.opencode/memory/_templates/project/gotchas.md +0 -41
  101. package/dist/template/.opencode/memory/_templates/prompt-engineering.md +0 -333
  102. package/dist/template/.opencode/memory/observations/2026-01-22-decision-agents-md-prompt-engineering-improvement.md +0 -29
  103. package/dist/template/.opencode/memory/observations/2026-01-25-decision-agent-roles-build-orchestrates-general-e.md +0 -14
  104. package/dist/template/.opencode/memory/observations/2026-01-25-decision-simplified-swarm-helper-tool-to-fix-type.md +0 -20
  105. package/dist/template/.opencode/memory/observations/2026-01-25-decision-use-beads-as-swarm-board-source-of-truth.md +0 -14
  106. package/dist/template/.opencode/memory/observations/2026-01-25-learning-user-wants-real-swarm-coordination-guida.md +0 -15
  107. package/dist/template/.opencode/memory/observations/2026-01-28-decision-created-deep-research-skill-for-thorough.md +0 -29
  108. package/dist/template/.opencode/memory/observations/2026-01-28-decision-gh-grep-mcp-wrapper-vs-native-grep-searc.md +0 -21
  109. package/dist/template/.opencode/memory/observations/2026-01-28-decision-oracle-tool-optimal-usage-patterns.md +0 -32
  110. package/dist/template/.opencode/memory/observations/2026-01-28-learning-ampcode-deep-mode-research-integration-w.md +0 -42
  111. package/dist/template/.opencode/memory/observations/2026-01-28-pattern-research-delegation-pattern-explore-for-.md +0 -32
  112. package/dist/template/.opencode/memory/observations/2026-01-29-decision-copilot-auth-plugin-rate-limit-handling.md +0 -27
  113. package/dist/template/.opencode/memory/observations/2026-01-29-decision-spec-driven-approach-for-opencodekit.md +0 -21
  114. package/dist/template/.opencode/memory/observations/2026-01-29-learning-karpathy-llm-coding-insights-dec-2025.md +0 -44
  115. package/dist/template/.opencode/memory/observations/2026-01-30-decision-github-copilot-claude-routing-keep-disab.md +0 -32
  116. package/dist/template/.opencode/memory/observations/2026-01-30-discovery-context-management-research-critical-gap.md +0 -14
  117. package/dist/template/.opencode/memory/observations/2026-01-30-discovery-kimi-k2-5-agent-swarm-architecture-patte.md +0 -45
  118. package/dist/template/.opencode/memory/observations/2026-01-30-pattern-swarm-tools-architecture.md +0 -28
  119. package/dist/template/.opencode/memory/observations/2026-01-31-decision-copilot-auth-plugin-updated-with-baseurl.md +0 -63
  120. package/dist/template/.opencode/memory/observations/2026-01-31-decision-created-dedicated-worker-agent-for-swarm.md +0 -20
  121. package/dist/template/.opencode/memory/observations/2026-01-31-decision-rollback-to-v1-1-47-for-copilot-claude-r.md +0 -21
  122. package/dist/template/.opencode/memory/observations/2026-01-31-decision-simplified-swarm-to-task-tool-pattern.md +0 -44
  123. package/dist/template/.opencode/memory/observations/2026-01-31-decision-swarm-architecture-task-tool-over-tmux.md +0 -33
  124. package/dist/template/.opencode/memory/observations/2026-01-31-decision-worker-skills-defined-for-swarm-delegati.md +0 -30
  125. package/dist/template/.opencode/memory/observations/2026-01-31-learning-gpt-reasoning-config-for-github-copilot.md +0 -51
  126. package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-copilot-auth-comparison-finding.md +0 -61
  127. package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-copilot-reasoning-architecture-.md +0 -66
  128. package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-custom-tools-api.md +0 -48
  129. package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-v1-1-48-skills-as-slash-command.md +0 -21
  130. package/dist/template/.opencode/memory/observations/2026-01-31-learning-swarm-system-simplified-removed-mailbox-.md +0 -30
  131. package/dist/template/.opencode/memory/observations/2026-01-31-learning-v1-1-48-native-copilot-reasoning-via-pr-.md +0 -45
  132. package/dist/template/.opencode/memory/observations/2026-01-31-warning-cannot-add-custom-config-to-opencode-jso.md +0 -18
  133. package/dist/template/.opencode/memory/observations/2026-01-31-warning-copilot-claude-v1-endpoint-returns-404-c.md +0 -48
  134. package/dist/template/.opencode/memory/observations/2026-01-31-warning-opencode-v1-1-48-claude-thinking-block-s.md +0 -51
  135. package/dist/template/.opencode/memory/project/architecture.md +0 -60
  136. package/dist/template/.opencode/memory/project/commands.md +0 -72
  137. package/dist/template/.opencode/memory/project/conventions.md +0 -68
  138. package/dist/template/.opencode/memory/project/gotchas.md +0 -41
  139. package/dist/template/.opencode/skill/notebooklm/SKILL.md +0 -272
  140. package/dist/template/.opencode/skill/notebooklm/references/setup.md +0 -353
  141. package/dist/template/.opencode/tool/notebooklm.ts +0 -488
  142. package/dist/template/.opencode/tool/oracle.ts +0 -240
  143. /package/dist/template/.opencode/memory/{user.example.md → _templates/user.md} +0 -0
  144. /package/dist/template/.opencode/memory/{user.md → project/user.md} +0 -0
@@ -1,8 +1,15 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { tool } from "@opencode-ai/plugin";
4
+ import {
5
+ type ObservationType,
6
+ type SearchIndexResult,
7
+ checkFTS5Available,
8
+ searchObservationsFTS,
9
+ } from "../plugin/lib/memory-db";
4
10
 
5
- interface SearchResult {
11
+ // Fallback file-based search for non-SQLite content
12
+ interface FileSearchResult {
6
13
  file: string;
7
14
  matches: { line: number; content: string }[];
8
15
  }
@@ -10,7 +17,7 @@ interface SearchResult {
10
17
  async function searchDirectory(
11
18
  dir: string,
12
19
  pattern: RegExp,
13
- results: SearchResult[],
20
+ results: FileSearchResult[],
14
21
  baseDir: string,
15
22
  ): Promise<void> {
16
23
  try {
@@ -30,14 +37,15 @@ async function searchDirectory(
30
37
  const lines = content.split("\n");
31
38
  const matches: { line: number; content: string }[] = [];
32
39
 
33
- lines.forEach((line, index) => {
40
+ for (let index = 0; index < lines.length; index++) {
41
+ const line = lines[index];
34
42
  if (pattern.test(line)) {
35
43
  matches.push({
36
44
  line: index + 1,
37
45
  content: line.trim().substring(0, 150),
38
46
  });
39
47
  }
40
- });
48
+ }
41
49
 
42
50
  if (matches.length > 0) {
43
51
  const relativePath = path.relative(baseDir, fullPath);
@@ -50,11 +58,11 @@ async function searchDirectory(
50
58
  }
51
59
  }
52
60
 
53
- async function keywordSearch(
61
+ async function fallbackKeywordSearch(
54
62
  query: string,
55
63
  type: string | undefined,
56
- _limit: number,
57
- ): Promise<SearchResult[]> {
64
+ limit: number,
65
+ ): Promise<FileSearchResult[]> {
58
66
  const memoryDir = path.join(process.cwd(), ".opencode/memory");
59
67
  const beadsDir = path.join(process.cwd(), ".beads/artifacts");
60
68
  const globalMemoryDir = path.join(
@@ -72,17 +80,16 @@ async function keywordSearch(
72
80
  pattern = new RegExp(escaped, "i");
73
81
  }
74
82
 
75
- const results: SearchResult[] = [];
83
+ const results: FileSearchResult[] = [];
76
84
 
77
85
  // Handle type filtering
78
86
  if (type === "beads") {
79
87
  await searchDirectory(beadsDir, pattern, results, beadsDir);
80
- } else if (type && type !== "all") {
88
+ } else if (type && type !== "all" && type !== "observations") {
81
89
  const typeMap: Record<string, string> = {
82
90
  handoffs: "handoffs",
83
91
  research: "research",
84
92
  templates: "_templates",
85
- observations: "observations",
86
93
  };
87
94
  const subdir = typeMap[type];
88
95
  if (subdir) {
@@ -96,19 +103,73 @@ async function keywordSearch(
96
103
  await searchDirectory(globalMemoryDir, pattern, results, globalMemoryDir);
97
104
  }
98
105
 
99
- return results;
106
+ return results.slice(0, limit);
100
107
  }
101
108
 
102
- function formatKeywordResults(
109
+ const TYPE_ICONS: Record<string, string> = {
110
+ decision: "🎯",
111
+ bugfix: "🐛",
112
+ feature: "✨",
113
+ pattern: "🔄",
114
+ discovery: "💡",
115
+ learning: "📚",
116
+ warning: "⚠️",
117
+ };
118
+
119
+ function formatCompactIndex(
120
+ results: SearchIndexResult[],
103
121
  query: string,
104
- results: SearchResult[],
122
+ ): string {
123
+ if (results.length === 0) {
124
+ return `No observations found for "${query}".\n\nTip: Use memory-search without query to see recent observations.`;
125
+ }
126
+
127
+ let output = `# Search Results: "${query}"\n\n`;
128
+ output += `Found **${results.length}** observation(s). Use \`memory-get\` for full details.\n\n`;
129
+ output += "| ID | Type | Title | Date |\n";
130
+ output += "|---|---|---|---|\n";
131
+
132
+ for (const result of results) {
133
+ const icon = TYPE_ICONS[result.type] || "📝";
134
+ const date = result.created_at.split("T")[0];
135
+ const title =
136
+ result.title.length > 50
137
+ ? `${result.title.substring(0, 47)}...`
138
+ : result.title;
139
+ output += `| #${result.id} | ${icon} ${result.type} | ${title} | ${date} |\n`;
140
+ }
141
+
142
+ output += "\n## Snippets\n\n";
143
+ for (const result of results) {
144
+ const icon = TYPE_ICONS[result.type] || "📝";
145
+ output += `**#${result.id}** ${icon} ${result.title}\n`;
146
+ if (result.snippet) {
147
+ output += `> ${result.snippet}...\n`;
148
+ }
149
+ output += "\n";
150
+ }
151
+
152
+ output += "\n---\n";
153
+ output += `💡 **Next steps:**\n`;
154
+ output += `- \`memory-get({ ids: "${results
155
+ .map((r) => r.id)
156
+ .slice(0, 3)
157
+ .join(",")}" })\` - Get full details\n`;
158
+ output += `- \`memory-timeline({ anchor_id: ${results[0].id} })\` - See chronological context\n`;
159
+
160
+ return output;
161
+ }
162
+
163
+ function formatFallbackResults(
164
+ query: string,
165
+ results: FileSearchResult[],
105
166
  limit: number,
106
167
  ): string {
107
168
  if (results.length === 0) {
108
- return `No matches found for "${query}".`;
169
+ return `No matches found for "${query}" in non-observation files.`;
109
170
  }
110
171
 
111
- let output = `# Search: "${query}"\n\n`;
172
+ let output = `# File Search: "${query}"\n\n`;
112
173
  output += `Found ${results.length} file(s) with matches.\n\n`;
113
174
 
114
175
  for (const result of results) {
@@ -127,30 +188,72 @@ function formatKeywordResults(
127
188
  }
128
189
 
129
190
  export default tool({
130
- description: `Search across all memory files using keywords.
191
+ description: `Search observations using FTS5 full-text search.
131
192
 
132
193
  Purpose:
133
- - Find past decisions, research, or handoffs
134
- - Searches: project memory, beads artifacts, and global memory
135
- - Returns file paths with matched lines and context
194
+ - Fast, ranked search across all observations in SQLite
195
+ - Returns compact index (~50-100 tokens per result) for progressive disclosure
196
+ - Use memory-get for full details after identifying relevant observations
197
+
198
+ Search modes:
199
+ - "observations" (default): Search SQLite with FTS5 ranking
200
+ - "handoffs", "research", "templates": Search specific markdown directories
201
+ - "beads": Search .beads/artifacts
202
+ - "all": Search everything (SQLite + markdown files)
136
203
 
137
204
  Example:
138
- memory-search({ query: "authentication", type: "all", limit: 10 })`,
205
+ memory-search({ query: "authentication" })
206
+ memory-search({ query: "auth", type: "decision", limit: 5 })`,
139
207
  args: {
140
- query: tool.schema
141
- .string()
142
- .describe("Search query: keywords or regex pattern"),
208
+ query: tool.schema.string().describe("Search query: keywords or phrase"),
143
209
  type: tool.schema
144
210
  .string()
145
211
  .optional()
146
212
  .describe(
147
- "Filter by type: 'all' (default), 'handoffs', 'research', 'templates', 'observations', 'beads'",
213
+ "Filter by observation type (decision, bugfix, feature, pattern, discovery, learning, warning) or search scope (observations, handoffs, research, templates, beads, all)",
148
214
  ),
149
- limit: tool.schema.number().optional().describe("Max results (default: 5)"),
215
+ limit: tool.schema
216
+ .number()
217
+ .optional()
218
+ .describe("Max results (default: 10)"),
150
219
  },
151
220
  execute: async (args: { query: string; type?: string; limit?: number }) => {
152
- const limit = args.limit || 5;
153
- const results = await keywordSearch(args.query, args.type, limit);
154
- return formatKeywordResults(args.query, results, limit);
221
+ const limit = args.limit || 10;
222
+
223
+ // Determine if we should use SQLite FTS5 or fallback
224
+ const observationTypes = [
225
+ "decision",
226
+ "bugfix",
227
+ "feature",
228
+ "pattern",
229
+ "discovery",
230
+ "learning",
231
+ "warning",
232
+ ];
233
+ const isObservationType =
234
+ args.type && observationTypes.includes(args.type.toLowerCase());
235
+ const isObservationsScope =
236
+ !args.type || args.type === "observations" || isObservationType;
237
+
238
+ // Try SQLite FTS5 for observations
239
+ if (isObservationsScope && checkFTS5Available()) {
240
+ try {
241
+ const obsType = isObservationType
242
+ ? (args.type?.toLowerCase() as ObservationType)
243
+ : undefined;
244
+ const results = searchObservationsFTS(args.query, {
245
+ type: obsType,
246
+ limit,
247
+ });
248
+ return formatCompactIndex(results, args.query);
249
+ } catch {
250
+ // FTS5 failed, fall through to file search
251
+ // Silently fall back to file-based search
252
+ }
253
+ }
254
+
255
+ // Fallback to file-based search for non-observation types or FTS5 failure
256
+ const results = await fallbackKeywordSearch(args.query, args.type, limit);
257
+ return formatFallbackResults(args.query, results, limit);
155
258
  },
156
259
  });
@@ -0,0 +1,105 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import {
3
+ type SearchIndexResult,
4
+ getTimelineAroundObservation,
5
+ } from "../plugin/lib/memory-db";
6
+
7
+ const TYPE_ICONS: Record<string, string> = {
8
+ decision: "🎯",
9
+ bugfix: "🐛",
10
+ feature: "✨",
11
+ pattern: "🔄",
12
+ discovery: "💡",
13
+ learning: "📚",
14
+ warning: "⚠️",
15
+ };
16
+
17
+ function formatTimelineResult(
18
+ anchor: {
19
+ id: number;
20
+ type: string;
21
+ title: string;
22
+ created_at: string;
23
+ } | null,
24
+ before: SearchIndexResult[],
25
+ after: SearchIndexResult[],
26
+ ): string {
27
+ if (!anchor) {
28
+ return "Anchor observation not found.";
29
+ }
30
+
31
+ let output = "# Timeline Context\n\n";
32
+
33
+ // Before (older observations)
34
+ if (before.length > 0) {
35
+ output += "## Earlier Observations\n\n";
36
+ for (const obs of before) {
37
+ const icon = TYPE_ICONS[obs.type] || "📝";
38
+ const date = obs.created_at.split("T")[0];
39
+ output += `- **#${obs.id}** ${icon} ${obs.title} _(${date})_\n`;
40
+ if (obs.snippet) {
41
+ output += ` ${obs.snippet.substring(0, 80)}...\n`;
42
+ }
43
+ }
44
+ output += "\n";
45
+ }
46
+
47
+ // Anchor
48
+ const anchorIcon = TYPE_ICONS[anchor.type] || "📝";
49
+ const anchorDate = anchor.created_at.split("T")[0];
50
+ output += `## ▶ Current: #${anchor.id}\n\n`;
51
+ output += `${anchorIcon} **${anchor.title}** _(${anchorDate})_\n\n`;
52
+
53
+ // After (newer observations)
54
+ if (after.length > 0) {
55
+ output += "## Later Observations\n\n";
56
+ for (const obs of after) {
57
+ const icon = TYPE_ICONS[obs.type] || "📝";
58
+ const date = obs.created_at.split("T")[0];
59
+ output += `- **#${obs.id}** ${icon} ${obs.title} _(${date})_\n`;
60
+ if (obs.snippet) {
61
+ output += ` ${obs.snippet.substring(0, 80)}...\n`;
62
+ }
63
+ }
64
+ }
65
+
66
+ return output;
67
+ }
68
+
69
+ export default tool({
70
+ description: `Get chronological context around an observation.
71
+
72
+ Purpose:
73
+ - Progressive disclosure: see what was happening before/after a specific observation
74
+ - Understand decision context over time
75
+ - Navigate memory timeline
76
+
77
+ Example:
78
+ memory-timeline({ anchor_id: 42, depth_before: 5, depth_after: 5 })`,
79
+ args: {
80
+ anchor_id: tool.schema
81
+ .number()
82
+ .describe("ID of the observation to get context around"),
83
+ depth_before: tool.schema
84
+ .number()
85
+ .optional()
86
+ .describe("Number of earlier observations to include (default: 5)"),
87
+ depth_after: tool.schema
88
+ .number()
89
+ .optional()
90
+ .describe("Number of later observations to include (default: 5)"),
91
+ },
92
+ execute: async (args: {
93
+ anchor_id: number;
94
+ depth_before?: number;
95
+ depth_after?: number;
96
+ }) => {
97
+ const { anchor, before, after } = getTimelineAroundObservation(
98
+ args.anchor_id,
99
+ args.depth_before ?? 5,
100
+ args.depth_after ?? 5,
101
+ );
102
+
103
+ return formatTimelineResult(anchor, before, after);
104
+ },
105
+ });
@@ -1,12 +1,11 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
1
  import { tool } from "@opencode-ai/plugin";
2
+ import { upsertMemoryFile } from "../plugin/lib/memory-db.js";
4
3
 
5
4
  export default tool({
6
5
  description: `Update memory files with new learnings, progress, or context.
7
6
 
8
7
  Purpose:
9
- - Write or append to project memory in .opencode/memory/
8
+ - Write or append to project memory in SQLite
10
9
  - Supports subdirectories (e.g., 'research/2024-01-topic')
11
10
  - Two modes: 'replace' (overwrite) or 'append' (add to end)
12
11
 
@@ -17,7 +16,7 @@ export default tool({
17
16
  file: tool.schema
18
17
  .string()
19
18
  .describe(
20
- "Memory file to update: handoffs/YYYY-MM-DD-phase, research/YYYY-MM-DD-topic. Use _templates/ for reference only.",
19
+ "Memory file to update: handoffs/YYYY-MM-DD-phase, research/YYYY-MM-DD-topic",
21
20
  ),
22
21
  content: tool.schema
23
22
  .string()
@@ -31,33 +30,29 @@ export default tool({
31
30
  ),
32
31
  },
33
32
  execute: async (args: { file: string; content: string; mode?: string }) => {
34
- // Always write to project memory (.opencode/memory/)
35
- const memoryDir = path.join(process.cwd(), ".opencode/memory");
33
+ // Normalize file path: strip existing .md extension
34
+ const normalizedFile = args.file.replace(/\.md$/i, "");
35
+ const mode = (args.mode || "replace") as "replace" | "append";
36
36
 
37
- // Normalize file path: strip existing .md extension, handle subdirectories
38
- const normalizedFile = args.file.replace(/\.md$/i, ""); // Remove .md if present
39
- const filePath = path.join(memoryDir, `${normalizedFile}.md`);
40
- const mode = args.mode || "replace";
37
+ const timestamp = new Date().toISOString();
38
+ let finalContent: string;
41
39
 
42
- try {
43
- // Ensure parent directory exists (handles subdirectories)
44
- const fileDir = path.dirname(filePath);
45
- await fs.mkdir(fileDir, { recursive: true });
46
-
47
- if (mode === "append") {
48
- const timestamp = new Date().toISOString();
49
- const appendContent = `\n\n---\n**Updated:** ${timestamp}\n\n${args.content}`;
50
- await fs.appendFile(filePath, appendContent, "utf-8");
51
- return `Successfully appended to ${normalizedFile}.md\n[Written to: ${filePath}]`;
52
- }
53
- // Replace mode - update timestamp
54
- const timestamp = new Date().toISOString();
55
- const updatedContent = args.content.replace(
40
+ if (mode === "append") {
41
+ finalContent = `\n\n---\n**Updated:** ${timestamp}\n\n${args.content}`;
42
+ } else {
43
+ // Replace mode - update timestamp placeholder if present
44
+ finalContent = args.content.replace(
56
45
  /\*\*Last Updated:\*\* \[Timestamp\]/,
57
46
  `**Last Updated:** ${timestamp}`,
58
47
  );
59
- await fs.writeFile(filePath, updatedContent, "utf-8");
60
- return `Successfully updated ${normalizedFile}.md\n[Written to: ${filePath}]`;
48
+ }
49
+
50
+ try {
51
+ // Store in SQLite (single source of truth)
52
+ upsertMemoryFile(normalizedFile, finalContent, mode);
53
+
54
+ const action = mode === "append" ? "Appended to" : "Updated";
55
+ return `✓ ${action} ${normalizedFile}`;
61
56
  } catch (error) {
62
57
  if (error instanceof Error) {
63
58
  return `Error updating memory: ${error.message}`;