opencode-self-improvement 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +34 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-self-improvement",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Durable memory for opencode: remember facts, decisions, and commands across sessions, plus a one-command per-repo setup (/repo-init).",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"exports": {
|
package/src/index.ts
CHANGED
|
@@ -204,6 +204,36 @@ const impl = {
|
|
|
204
204
|
hasEngagement: hasEngagement(getCurrent().sid),
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
+
// Identity-event race backstop: a resumed session's FIRST turn can write
|
|
208
|
+
// before the subscription has delivered its execution.started, and the
|
|
209
|
+
// module-scope fallback is the last setup instance — possibly another
|
|
210
|
+
// workspace. The session context API is synchronous and its messages
|
|
211
|
+
// carry location.directory, so bind once from them for unmapped sids.
|
|
212
|
+
// One-shot: never overwrites an event-bound entry.
|
|
213
|
+
const bindingSids = new Set<string>();
|
|
214
|
+
async function bindSessionFromMessages(sid: string | undefined): Promise<void> {
|
|
215
|
+
if (!sid || sid === "?" || sessionDir(sid) || bindingSids.has(sid)) return;
|
|
216
|
+
bindingSids.add(sid);
|
|
217
|
+
try {
|
|
218
|
+
const msgs = await ctx.session?.context?.({ sessionID: sid });
|
|
219
|
+
if (Array.isArray(msgs)) {
|
|
220
|
+
for (const m of msgs) {
|
|
221
|
+
if (!m || typeof m !== "object") continue;
|
|
222
|
+
const r = m as Record<string, any>;
|
|
223
|
+
const dir = r.location?.directory ?? r.info?.location?.directory;
|
|
224
|
+
if (typeof dir === "string" && dir) {
|
|
225
|
+
rememberSessionDir(sid, dir);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
// no context access or host error: the event stream still binds later
|
|
232
|
+
} finally {
|
|
233
|
+
bindingSids.delete(sid);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
207
237
|
// ---- tools --------------------------------------------------------------
|
|
208
238
|
await ctx.tool?.transform((editor) => {
|
|
209
239
|
const direct = { codemode: false } as const;
|
|
@@ -223,6 +253,7 @@ const impl = {
|
|
|
223
253
|
status: args.status,
|
|
224
254
|
repo: args.global ? "global" : undefined,
|
|
225
255
|
};
|
|
256
|
+
await bindSessionFromMessages(getCurrent().sid);
|
|
226
257
|
const outcome = await runWriteGate(candidate, gateContext());
|
|
227
258
|
if (outcome.action === "promoted") return { content: `Remembered: ${args.type} in ${args.scope}` };
|
|
228
259
|
if (outcome.action === "collapsed") return { content: `Duplicate ignored: ${args.type}/${args.scope}` };
|
|
@@ -240,6 +271,7 @@ const impl = {
|
|
|
240
271
|
"Initialize repository memory and knowledge graph. Ensures the codebase-memory graph for this repo is indexed (one-shot `cli` spawn; no session MCP needed) and re-derives build/lint/test/typecheck command memories through the write gate, then stores an init stamp keyed on git HEAD. Idempotent: a no-op when the stamp matches HEAD, unless force=true (refresh after a refactor).",
|
|
241
272
|
options: direct,
|
|
242
273
|
async execute(args): Promise<{ content: string }> {
|
|
274
|
+
await bindSessionFromMessages(getCurrent().sid);
|
|
243
275
|
const summary = await runInit({ liveRoot: gateContext().liveRoot, force: args.force, mode: args.mode });
|
|
244
276
|
return { content: formatInit(summary) };
|
|
245
277
|
},
|
|
@@ -286,6 +318,7 @@ const impl = {
|
|
|
286
318
|
"Update an existing memory (gated like remember; the prior entry is superseded, not erased). `query` selects which row to update: its exact id (ts), a clock fragment (e.g. HH:MM:SS.mmm), or a unique substring of its content. Omitting `query` targets the most recent active row of scope+type. An unmatched or ambiguous `query` fails closed and writes nothing.",
|
|
287
319
|
options: direct,
|
|
288
320
|
async execute(args): Promise<{ content: string }> {
|
|
321
|
+
await bindSessionFromMessages(getCurrent().sid);
|
|
289
322
|
const prior = (await loadAll()).filter(
|
|
290
323
|
(m) => m.scope === args.scope && m.type === args.type && isActive(m) && isVisibleHere(m),
|
|
291
324
|
);
|
|
@@ -684,6 +717,7 @@ const impl = {
|
|
|
684
717
|
if (typeof sid === "string") {
|
|
685
718
|
markTurn(sid);
|
|
686
719
|
await checkNewSession(sid);
|
|
720
|
+
await bindSessionFromMessages(sid);
|
|
687
721
|
const version = await storeVersion();
|
|
688
722
|
const cached = epoch.get(sid);
|
|
689
723
|
if (!cached || cached.version !== version) {
|