opencodekit 0.17.13 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -6
- package/dist/template/.opencode/dcp.jsonc +81 -81
- package/dist/template/.opencode/memory/memory.db +0 -0
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +199 -23
- package/dist/template/.opencode/opencode.json.tui-migration.bak +1380 -0
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/lib/capture.ts +177 -0
- package/dist/template/.opencode/plugin/lib/context.ts +194 -0
- package/dist/template/.opencode/plugin/lib/curator.ts +234 -0
- package/dist/template/.opencode/plugin/lib/db/maintenance.ts +312 -0
- package/dist/template/.opencode/plugin/lib/db/observations.ts +299 -0
- package/dist/template/.opencode/plugin/lib/db/pipeline.ts +520 -0
- package/dist/template/.opencode/plugin/lib/db/schema.ts +356 -0
- package/dist/template/.opencode/plugin/lib/db/types.ts +211 -0
- package/dist/template/.opencode/plugin/lib/distill.ts +376 -0
- package/dist/template/.opencode/plugin/lib/inject.ts +126 -0
- package/dist/template/.opencode/plugin/lib/memory-admin-tools.ts +188 -0
- package/dist/template/.opencode/plugin/lib/memory-db.ts +54 -936
- package/dist/template/.opencode/plugin/lib/memory-helpers.ts +202 -0
- package/dist/template/.opencode/plugin/lib/memory-hooks.ts +240 -0
- package/dist/template/.opencode/plugin/lib/memory-tools.ts +341 -0
- package/dist/template/.opencode/plugin/memory.ts +56 -60
- package/dist/template/.opencode/plugin/sessions.ts +372 -93
- package/dist/template/.opencode/tui.json +15 -0
- package/package.json +1 -1
- package/dist/template/.opencode/tool/action-queue.ts +0 -313
- package/dist/template/.opencode/tool/memory-admin.ts +0 -445
- package/dist/template/.opencode/tool/memory-get.ts +0 -143
- package/dist/template/.opencode/tool/memory-read.ts +0 -45
- package/dist/template/.opencode/tool/memory-search.ts +0 -264
- package/dist/template/.opencode/tool/memory-timeline.ts +0 -105
- package/dist/template/.opencode/tool/memory-update.ts +0 -63
- package/dist/template/.opencode/tool/observation.ts +0 -357
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
|
-
import {
|
|
3
|
-
type ConfidenceLevel,
|
|
4
|
-
type ObservationInput,
|
|
5
|
-
type ObservationType,
|
|
6
|
-
storeObservation,
|
|
7
|
-
} from "../plugin/lib/memory-db.js";
|
|
8
|
-
|
|
9
|
-
const TYPE_ICONS: Record<ObservationType, string> = {
|
|
10
|
-
decision: "🎯",
|
|
11
|
-
bugfix: "🐛",
|
|
12
|
-
feature: "✨",
|
|
13
|
-
pattern: "🔄",
|
|
14
|
-
discovery: "💡",
|
|
15
|
-
learning: "📚",
|
|
16
|
-
warning: "⚠️",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const CONFIDENCE_ICONS: Record<ConfidenceLevel, string> = {
|
|
20
|
-
high: "🟢",
|
|
21
|
-
medium: "🟡",
|
|
22
|
-
low: "🔴",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Patterns to detect file references in observation content
|
|
26
|
-
const FILE_PATTERNS = [
|
|
27
|
-
// file:line format (e.g., src/auth.ts:42)
|
|
28
|
-
/(?:^|\s)([a-zA-Z0-9_\-./]+\.[a-zA-Z]{2,4}):(\d+)/g,
|
|
29
|
-
// backtick file paths (e.g., `src/auth.ts`)
|
|
30
|
-
/`([a-zA-Z0-9_\-./]+\.[a-zA-Z]{2,4})`/g,
|
|
31
|
-
// common source paths
|
|
32
|
-
/(?:^|\s)(src\/[a-zA-Z0-9_\-./]+\.[a-zA-Z]{2,4})/g,
|
|
33
|
-
/(?:^|\s)(\.opencode\/[a-zA-Z0-9_\-./]+\.[a-zA-Z]{2,4})/g,
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
interface FileReference {
|
|
37
|
-
file: string;
|
|
38
|
-
line?: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Extract file references from observation content
|
|
42
|
-
function extractFileReferences(content: string): FileReference[] {
|
|
43
|
-
const refs: FileReference[] = [];
|
|
44
|
-
const seen = new Set<string>();
|
|
45
|
-
|
|
46
|
-
for (const pattern of FILE_PATTERNS) {
|
|
47
|
-
// Reset regex state
|
|
48
|
-
pattern.lastIndex = 0;
|
|
49
|
-
let match = pattern.exec(content);
|
|
50
|
-
|
|
51
|
-
while (match !== null) {
|
|
52
|
-
const file = match[1];
|
|
53
|
-
const line = match[2] ? Number.parseInt(match[2], 10) : undefined;
|
|
54
|
-
const key = `${file}:${line || ""}`;
|
|
55
|
-
|
|
56
|
-
if (!seen.has(key) && !file.includes("node_modules")) {
|
|
57
|
-
seen.add(key);
|
|
58
|
-
refs.push({ file, line });
|
|
59
|
-
}
|
|
60
|
-
match = pattern.exec(content);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return refs;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export default tool({
|
|
68
|
-
description: `Create a structured observation for future reference.
|
|
69
|
-
|
|
70
|
-
Purpose:
|
|
71
|
-
- Capture decisions, bugs, features, patterns, discoveries, learnings, or warnings
|
|
72
|
-
- Auto-detects file references from content (file:line, \`path\`, src/, .opencode/)
|
|
73
|
-
- Stores in SQLite with FTS5 index for fast search
|
|
74
|
-
- Supports enhanced schema: facts, subtitle, files_read/files_modified
|
|
75
|
-
|
|
76
|
-
Confidence guidance:
|
|
77
|
-
- high: verified by tests, logs, or direct inspection (default)
|
|
78
|
-
- medium: likely, but not fully verified
|
|
79
|
-
- low: uncertain or speculative
|
|
80
|
-
|
|
81
|
-
Type-specific examples:
|
|
82
|
-
decision
|
|
83
|
-
observation({
|
|
84
|
-
type: "decision",
|
|
85
|
-
title: "Use JWT for auth",
|
|
86
|
-
narrative: "Chose JWT for stateless auth across services.",
|
|
87
|
-
facts: "stateless, scalable",
|
|
88
|
-
concepts: "authentication, jwt",
|
|
89
|
-
confidence: "high"
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
bugfix
|
|
93
|
-
observation({
|
|
94
|
-
type: "bugfix",
|
|
95
|
-
title: "Fix null pointer on login",
|
|
96
|
-
narrative: "Guarded optional user in src/auth.ts:42 to prevent crash.",
|
|
97
|
-
files_modified: "src/auth.ts",
|
|
98
|
-
concepts: "auth, null-check",
|
|
99
|
-
confidence: "high"
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
feature
|
|
103
|
-
observation({
|
|
104
|
-
type: "feature",
|
|
105
|
-
title: "Add CLI --dry-run",
|
|
106
|
-
narrative: "Introduce dry-run mode to show planned changes without writing.",
|
|
107
|
-
files_modified: "src/commands/init.ts",
|
|
108
|
-
concepts: "cli, ux",
|
|
109
|
-
confidence: "medium"
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
pattern
|
|
113
|
-
observation({
|
|
114
|
-
type: "pattern",
|
|
115
|
-
title: "Use zod for input validation",
|
|
116
|
-
narrative: "All command inputs validated with zod schemas before execute.",
|
|
117
|
-
concepts: "validation, zod",
|
|
118
|
-
confidence: "high"
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
discovery
|
|
122
|
-
observation({
|
|
123
|
-
type: "discovery",
|
|
124
|
-
title: "Build copies .opencode/ to dist/template/",
|
|
125
|
-
narrative: "Found rsync step in build.ts that bundles .opencode/.",
|
|
126
|
-
files_read: "build.ts",
|
|
127
|
-
confidence: "high"
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
learning
|
|
131
|
-
observation({
|
|
132
|
-
type: "learning",
|
|
133
|
-
title: "Bun test respects --watch",
|
|
134
|
-
narrative: "Observed bun test --watch keeps runner active during edits.",
|
|
135
|
-
confidence: "medium"
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
warning
|
|
139
|
-
observation({
|
|
140
|
-
type: "warning",
|
|
141
|
-
title: "Do not edit dist/ directly",
|
|
142
|
-
narrative: "dist/ is built output and overwritten on build.",
|
|
143
|
-
concepts: "build, generated",
|
|
144
|
-
confidence: "high"
|
|
145
|
-
})`,
|
|
146
|
-
args: {
|
|
147
|
-
type: tool.schema
|
|
148
|
-
.string()
|
|
149
|
-
.describe(
|
|
150
|
-
"Observation type: decision, bugfix, feature, pattern, discovery, learning, warning",
|
|
151
|
-
),
|
|
152
|
-
title: tool.schema.string().describe("Brief title for the observation"),
|
|
153
|
-
subtitle: tool.schema
|
|
154
|
-
.string()
|
|
155
|
-
.optional()
|
|
156
|
-
.describe("Optional subtitle or tagline"),
|
|
157
|
-
facts: tool.schema
|
|
158
|
-
.string()
|
|
159
|
-
.optional()
|
|
160
|
-
.describe("Comma-separated key facts (e.g., 'stateless, scalable')"),
|
|
161
|
-
narrative: tool.schema
|
|
162
|
-
.string()
|
|
163
|
-
.optional()
|
|
164
|
-
.describe("Detailed observation content with context"),
|
|
165
|
-
content: tool.schema
|
|
166
|
-
.string()
|
|
167
|
-
.optional()
|
|
168
|
-
.describe(
|
|
169
|
-
"DEPRECATED: Use 'narrative' instead. Alias for backward compat.",
|
|
170
|
-
),
|
|
171
|
-
concepts: tool.schema
|
|
172
|
-
.string()
|
|
173
|
-
.optional()
|
|
174
|
-
.describe(
|
|
175
|
-
"Comma-separated concept tags (e.g., 'authentication, oauth, security')",
|
|
176
|
-
),
|
|
177
|
-
files_read: tool.schema
|
|
178
|
-
.string()
|
|
179
|
-
.optional()
|
|
180
|
-
.describe("Comma-separated files that were read (e.g., 'src/auth.ts')"),
|
|
181
|
-
files_modified: tool.schema
|
|
182
|
-
.string()
|
|
183
|
-
.optional()
|
|
184
|
-
.describe("Comma-separated files that were modified"),
|
|
185
|
-
files: tool.schema
|
|
186
|
-
.string()
|
|
187
|
-
.optional()
|
|
188
|
-
.describe(
|
|
189
|
-
"DEPRECATED: Use 'files_modified' instead. Alias for backward compat.",
|
|
190
|
-
),
|
|
191
|
-
bead_id: tool.schema
|
|
192
|
-
.string()
|
|
193
|
-
.optional()
|
|
194
|
-
.describe("Related bead ID for traceability"),
|
|
195
|
-
confidence: tool.schema
|
|
196
|
-
.string()
|
|
197
|
-
.optional()
|
|
198
|
-
.describe(
|
|
199
|
-
"Confidence level: high (verified), medium (likely), low (uncertain). Defaults to high.",
|
|
200
|
-
),
|
|
201
|
-
supersedes: tool.schema
|
|
202
|
-
.string()
|
|
203
|
-
.optional()
|
|
204
|
-
.describe(
|
|
205
|
-
"ID or filename of observation this supersedes (for contradiction handling)",
|
|
206
|
-
),
|
|
207
|
-
},
|
|
208
|
-
execute: async (args: {
|
|
209
|
-
type: string;
|
|
210
|
-
title: string;
|
|
211
|
-
subtitle?: string;
|
|
212
|
-
facts?: string;
|
|
213
|
-
narrative?: string;
|
|
214
|
-
content?: string;
|
|
215
|
-
concepts?: string;
|
|
216
|
-
files_read?: string;
|
|
217
|
-
files_modified?: string;
|
|
218
|
-
files?: string;
|
|
219
|
-
bead_id?: string;
|
|
220
|
-
confidence?: string;
|
|
221
|
-
supersedes?: string;
|
|
222
|
-
}) => {
|
|
223
|
-
// Validate type
|
|
224
|
-
const validTypes: ObservationType[] = [
|
|
225
|
-
"decision",
|
|
226
|
-
"bugfix",
|
|
227
|
-
"feature",
|
|
228
|
-
"pattern",
|
|
229
|
-
"discovery",
|
|
230
|
-
"learning",
|
|
231
|
-
"warning",
|
|
232
|
-
];
|
|
233
|
-
const obsType = args.type.toLowerCase() as ObservationType;
|
|
234
|
-
if (!validTypes.includes(obsType)) {
|
|
235
|
-
return `Error: Invalid observation type '${args.type}'.\nValid types: ${validTypes.join(", ")}`;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Validate confidence level
|
|
239
|
-
const validConfidence: ConfidenceLevel[] = ["high", "medium", "low"];
|
|
240
|
-
const confidence = (args.confidence?.toLowerCase() ||
|
|
241
|
-
"high") as ConfidenceLevel;
|
|
242
|
-
if (!validConfidence.includes(confidence)) {
|
|
243
|
-
return `Error: Invalid confidence level '${args.confidence}'.\nValid levels: ${validConfidence.join(", ")}`;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Handle deprecated fields (backward compat)
|
|
247
|
-
const narrative = args.narrative || args.content || "";
|
|
248
|
-
const filesModifiedRaw = args.files_modified || args.files || "";
|
|
249
|
-
|
|
250
|
-
// Parse arrays from comma-separated strings
|
|
251
|
-
const facts = args.facts
|
|
252
|
-
? args.facts
|
|
253
|
-
.split(",")
|
|
254
|
-
.map((f) => f.trim())
|
|
255
|
-
.filter(Boolean)
|
|
256
|
-
: [];
|
|
257
|
-
const concepts = args.concepts
|
|
258
|
-
? args.concepts
|
|
259
|
-
.split(",")
|
|
260
|
-
.map((c) => c.trim())
|
|
261
|
-
.filter(Boolean)
|
|
262
|
-
: [];
|
|
263
|
-
let filesRead = args.files_read
|
|
264
|
-
? args.files_read
|
|
265
|
-
.split(",")
|
|
266
|
-
.map((f) => f.trim())
|
|
267
|
-
.filter(Boolean)
|
|
268
|
-
: [];
|
|
269
|
-
const filesModified = filesModifiedRaw
|
|
270
|
-
? filesModifiedRaw
|
|
271
|
-
.split(",")
|
|
272
|
-
.map((f) => f.trim())
|
|
273
|
-
.filter(Boolean)
|
|
274
|
-
: [];
|
|
275
|
-
|
|
276
|
-
// Auto-detect file references from narrative
|
|
277
|
-
const detectedRefs = extractFileReferences(narrative);
|
|
278
|
-
const detectedFiles = detectedRefs.map((r) => r.file);
|
|
279
|
-
|
|
280
|
-
// Merge detected files with explicitly provided files
|
|
281
|
-
filesRead = [...new Set([...filesRead, ...detectedFiles])];
|
|
282
|
-
|
|
283
|
-
// Parse supersedes (could be numeric ID or filename)
|
|
284
|
-
let supersedesId: number | undefined;
|
|
285
|
-
if (args.supersedes) {
|
|
286
|
-
const parsed = Number.parseInt(args.supersedes, 10);
|
|
287
|
-
if (!Number.isNaN(parsed)) {
|
|
288
|
-
supersedesId = parsed;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Prepare observation input
|
|
293
|
-
const input: ObservationInput = {
|
|
294
|
-
type: obsType,
|
|
295
|
-
title: args.title,
|
|
296
|
-
subtitle: args.subtitle,
|
|
297
|
-
facts: facts.length > 0 ? facts : undefined,
|
|
298
|
-
narrative: narrative || undefined,
|
|
299
|
-
concepts: concepts.length > 0 ? concepts : undefined,
|
|
300
|
-
files_read: filesRead.length > 0 ? filesRead : undefined,
|
|
301
|
-
files_modified: filesModified.length > 0 ? filesModified : undefined,
|
|
302
|
-
confidence,
|
|
303
|
-
bead_id: args.bead_id,
|
|
304
|
-
supersedes: supersedesId,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
try {
|
|
308
|
-
// Store in SQLite (single source of truth)
|
|
309
|
-
const sqliteId = storeObservation(input);
|
|
310
|
-
|
|
311
|
-
// Update bead notes if bead_id provided
|
|
312
|
-
let beadUpdate = "";
|
|
313
|
-
if (args.bead_id) {
|
|
314
|
-
try {
|
|
315
|
-
const { execSync } = await import("node:child_process");
|
|
316
|
-
const noteContent = `${TYPE_ICONS[obsType]} ${obsType}: ${args.title}`;
|
|
317
|
-
execSync(
|
|
318
|
-
`br edit ${args.bead_id} --note "${noteContent.replace(/"/g, '\\"')}"`,
|
|
319
|
-
{
|
|
320
|
-
cwd: process.cwd(),
|
|
321
|
-
encoding: "utf-8",
|
|
322
|
-
timeout: 5000,
|
|
323
|
-
},
|
|
324
|
-
);
|
|
325
|
-
beadUpdate = `\nBead updated: ${args.bead_id}`;
|
|
326
|
-
} catch {
|
|
327
|
-
beadUpdate = `\nWarning: Could not update bead ${args.bead_id}`;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Build output
|
|
332
|
-
const icon = TYPE_ICONS[obsType];
|
|
333
|
-
const confIcon = CONFIDENCE_ICONS[confidence];
|
|
334
|
-
|
|
335
|
-
let output = `✓ Observation #${sqliteId} saved\n\n`;
|
|
336
|
-
output += `**Type**: ${icon} ${obsType}\n`;
|
|
337
|
-
output += `**Title**: ${args.title}\n`;
|
|
338
|
-
output += `**Confidence**: ${confIcon} ${confidence}\n`;
|
|
339
|
-
|
|
340
|
-
if (concepts.length > 0) {
|
|
341
|
-
output += `**Concepts**: ${concepts.join(", ")}\n`;
|
|
342
|
-
}
|
|
343
|
-
if (facts.length > 0) {
|
|
344
|
-
output += `**Facts**: ${facts.length} extracted\n`;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
output += beadUpdate;
|
|
348
|
-
|
|
349
|
-
return output;
|
|
350
|
-
} catch (error) {
|
|
351
|
-
if (error instanceof Error) {
|
|
352
|
-
return `Error saving observation: ${error.message}`;
|
|
353
|
-
}
|
|
354
|
-
return "Unknown error saving observation";
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
});
|