opencode-branch-memory-manager 0.1.7 → 0.1.9
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.
|
@@ -5,3 +5,27 @@ export { ContextInjector } from "./injector.js";
|
|
|
5
5
|
export { BranchMonitor } from "./monitor.js";
|
|
6
6
|
export { ConfigManager } from "./config.js";
|
|
7
7
|
export type { BranchContext, PluginConfig, Message, Todo } from "./types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Show a toast notification in the OpenCode UI
|
|
11
|
+
* Falls back to console.log if toast API is unavailable
|
|
12
|
+
*/
|
|
13
|
+
export function showToast(
|
|
14
|
+
client: any,
|
|
15
|
+
message: string,
|
|
16
|
+
variant: "info" | "success" | "warning" | "error" = "info",
|
|
17
|
+
title?: string,
|
|
18
|
+
duration?: number
|
|
19
|
+
): void {
|
|
20
|
+
try {
|
|
21
|
+
client?.global?.tui?.showToast?.({
|
|
22
|
+
title: title || "Branch Memory",
|
|
23
|
+
message,
|
|
24
|
+
variant,
|
|
25
|
+
duration: duration || (variant === "error" ? undefined : 3000)
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
// Fallback to console if toast unavailable
|
|
29
|
+
console.log(`[${variant.toUpperCase()}] ${title || "Branch Memory"}: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -2176,19 +2176,34 @@ class ConfigManager {
|
|
|
2176
2176
|
return result;
|
|
2177
2177
|
}
|
|
2178
2178
|
}
|
|
2179
|
+
|
|
2180
|
+
// .opencode/branch-memory/index.ts
|
|
2181
|
+
function showToast(client, message, variant = "info", title, duration) {
|
|
2182
|
+
try {
|
|
2183
|
+
client?.global?.tui?.showToast?.({
|
|
2184
|
+
title: title || "Branch Memory",
|
|
2185
|
+
message,
|
|
2186
|
+
variant,
|
|
2187
|
+
duration: duration || (variant === "error" ? undefined : 3000)
|
|
2188
|
+
});
|
|
2189
|
+
} catch (error) {
|
|
2190
|
+
console.log(`[${variant.toUpperCase()}] ${title || "Branch Memory"}: ${message}`);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2179
2194
|
// .opencode/plugin/branch-memory-plugin.ts
|
|
2180
2195
|
var BranchMemoryPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
2181
|
-
console.log("\uD83E\uDDE0 Branch Memory Plugin initializing...");
|
|
2182
2196
|
const configManager = new ConfigManager(directory);
|
|
2183
2197
|
const isGitRepo = await GitOperations.isGitRepo();
|
|
2184
2198
|
if (!isGitRepo) {
|
|
2185
|
-
|
|
2199
|
+
showToast(client, "Not in a git repository. Branch memory features disabled.", "warning");
|
|
2186
2200
|
return {};
|
|
2187
2201
|
}
|
|
2188
2202
|
const config = await configManager.load();
|
|
2189
2203
|
const storage = new ContextStorage(configManager.getStorageDir(), config);
|
|
2190
2204
|
const collector = new ContextCollector(config, client);
|
|
2191
2205
|
let lastAutoSave = 0;
|
|
2206
|
+
let saveCount = 0;
|
|
2192
2207
|
const autoSave = async (reason) => {
|
|
2193
2208
|
const currentConfig = await configManager.load();
|
|
2194
2209
|
if (currentConfig.autoSave.enabled) {
|
|
@@ -2200,46 +2215,40 @@ var BranchMemoryPlugin = async ({ project, client, $, directory, worktree }) =>
|
|
|
2200
2215
|
const context = await collector.collectContext(currentConfig.context.defaultInclude.includes("messages"), currentConfig.context.defaultInclude.includes("todos"), currentConfig.context.defaultInclude.includes("files"), reason);
|
|
2201
2216
|
await storage.saveContext(currentBranch, context);
|
|
2202
2217
|
lastAutoSave = now;
|
|
2203
|
-
|
|
2218
|
+
saveCount++;
|
|
2219
|
+
if (saveCount === 1 || saveCount % 10 === 0) {
|
|
2220
|
+
showToast(client, `Context saved for ${currentBranch}`, "success", undefined, 2000);
|
|
2221
|
+
}
|
|
2204
2222
|
}
|
|
2205
2223
|
} catch (error) {
|
|
2206
|
-
|
|
2224
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2225
|
+
showToast(client, `Failed to save context: ${errorMessage}`, "error");
|
|
2207
2226
|
}
|
|
2208
2227
|
}
|
|
2209
2228
|
}
|
|
2210
2229
|
};
|
|
2211
2230
|
const branchMonitor = new BranchMonitor(async (oldBranch, newBranch) => {
|
|
2212
|
-
console.log(`\uD83D\uDD04 Branch changed: ${oldBranch || "(none)"} → ${newBranch}`);
|
|
2213
2231
|
const currentConfig = await configManager.load();
|
|
2214
2232
|
if (oldBranch && currentConfig.autoSave.onBranchChange) {
|
|
2215
2233
|
const context = await collector.collectContext(currentConfig.context.defaultInclude.includes("messages"), currentConfig.context.defaultInclude.includes("todos"), currentConfig.context.defaultInclude.includes("files"), "branch change");
|
|
2216
2234
|
await storage.saveContext(oldBranch, context);
|
|
2217
|
-
console.log(`\uD83D\uDCBE Saved context for old branch '${oldBranch}'`);
|
|
2218
2235
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
} else {
|
|
2225
|
-
console.log(`ℹ️ No saved context for branch '${newBranch}'`);
|
|
2226
|
-
}
|
|
2227
|
-
} else if (currentConfig.contextLoading === "ask") {
|
|
2228
|
-
console.log(`ℹ️ Context available for branch '${newBranch}'`);
|
|
2229
|
-
console.log(` Use @branch-memory_load to restore it`);
|
|
2236
|
+
const branchContext = await storage.loadContext(newBranch);
|
|
2237
|
+
if (branchContext) {
|
|
2238
|
+
showToast(client, `Switched to ${newBranch}. Context available - use @branch-memory_load to restore.`, "info", "Branch Changed", 5000);
|
|
2239
|
+
} else {
|
|
2240
|
+
showToast(client, `Switched to ${newBranch}. No saved context for this branch.`, "info", "Branch Changed", 3000);
|
|
2230
2241
|
}
|
|
2231
2242
|
}, config);
|
|
2232
2243
|
await branchMonitor.start();
|
|
2233
2244
|
return {
|
|
2234
2245
|
"session.created": async (input, output) => {
|
|
2235
|
-
console.log("\uD83D\uDE80 Session created - checking for saved context...");
|
|
2236
2246
|
const currentConfig = await configManager.load();
|
|
2237
2247
|
const branch = await GitOperations.getCurrentBranch();
|
|
2238
2248
|
if (branch && currentConfig.contextLoading === "auto") {
|
|
2239
2249
|
const branchContext = await storage.loadContext(branch);
|
|
2240
2250
|
if (branchContext) {
|
|
2241
|
-
|
|
2242
|
-
console.log(" Use @branch-memory_load to restore it");
|
|
2251
|
+
showToast(client, `Context available for ${branch}. Use @branch-memory_load to restore.`, "info", undefined, 4000);
|
|
2243
2252
|
}
|
|
2244
2253
|
}
|
|
2245
2254
|
},
|
|
@@ -2256,12 +2265,11 @@ var BranchMemoryPlugin = async ({ project, client, $, directory, worktree }) =>
|
|
|
2256
2265
|
}
|
|
2257
2266
|
},
|
|
2258
2267
|
unload: () => {
|
|
2259
|
-
console.log("\uD83E\uDDE0 Branch Memory Plugin shutting down...");
|
|
2260
2268
|
branchMonitor.stop();
|
|
2261
2269
|
autoSave("plugin unload").catch((error) => {
|
|
2262
|
-
|
|
2270
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2271
|
+
showToast(client, `Final save failed: ${errorMessage}`, "error");
|
|
2263
2272
|
});
|
|
2264
|
-
console.log("✅ Plugin stopped");
|
|
2265
2273
|
}
|
|
2266
2274
|
};
|
|
2267
2275
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Plugin } from '@opencode-ai/plugin'
|
|
2
|
-
import { ContextStorage, GitOperations, ContextCollector, ConfigManager, BranchMonitor } from '../branch-memory/index.js'
|
|
2
|
+
import { ContextStorage, GitOperations, ContextCollector, ConfigManager, BranchMonitor, showToast } from '../branch-memory/index.js'
|
|
3
3
|
|
|
4
4
|
export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
|
|
5
|
-
|
|
5
|
+
// Silent initialization - no need to notify user
|
|
6
6
|
|
|
7
7
|
// Load configuration
|
|
8
8
|
const configManager = new ConfigManager(directory)
|
|
@@ -10,7 +10,11 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
10
10
|
// Check if we're in a git repository
|
|
11
11
|
const isGitRepo = await GitOperations.isGitRepo()
|
|
12
12
|
if (!isGitRepo) {
|
|
13
|
-
|
|
13
|
+
showToast(
|
|
14
|
+
client,
|
|
15
|
+
'Not in a git repository. Branch memory features disabled.',
|
|
16
|
+
'warning'
|
|
17
|
+
)
|
|
14
18
|
return {}
|
|
15
19
|
}
|
|
16
20
|
|
|
@@ -18,8 +22,9 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
18
22
|
const storage = new ContextStorage(configManager.getStorageDir(), config)
|
|
19
23
|
const collector = new ContextCollector(config, client)
|
|
20
24
|
|
|
21
|
-
// Track last auto-save time
|
|
25
|
+
// Track last auto-save time and count
|
|
22
26
|
let lastAutoSave = 0
|
|
27
|
+
let saveCount = 0
|
|
23
28
|
|
|
24
29
|
// Auto-save function with throttling
|
|
25
30
|
const autoSave = async (reason: string) => {
|
|
@@ -41,10 +46,16 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
41
46
|
|
|
42
47
|
await storage.saveContext(currentBranch, context)
|
|
43
48
|
lastAutoSave = now
|
|
44
|
-
|
|
49
|
+
saveCount++
|
|
50
|
+
|
|
51
|
+
// Only show toast on first save or every 10th save to reduce noise
|
|
52
|
+
if (saveCount === 1 || saveCount % 10 === 0) {
|
|
53
|
+
showToast(client, `Context saved for ${currentBranch}`, 'success', undefined, 2000)
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
} catch (error) {
|
|
47
|
-
|
|
57
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
58
|
+
showToast(client, `Failed to save context: ${errorMessage}`, 'error')
|
|
48
59
|
}
|
|
49
60
|
}
|
|
50
61
|
}
|
|
@@ -53,8 +64,6 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
53
64
|
// Initialize branch monitor with callback
|
|
54
65
|
const branchMonitor = new BranchMonitor(
|
|
55
66
|
async (oldBranch, newBranch) => {
|
|
56
|
-
console.log(`🔄 Branch changed: ${oldBranch || '(none)'} → ${newBranch}`)
|
|
57
|
-
|
|
58
67
|
const currentConfig = await configManager.load()
|
|
59
68
|
|
|
60
69
|
// Auto-save old branch context
|
|
@@ -66,21 +75,26 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
66
75
|
'branch change'
|
|
67
76
|
)
|
|
68
77
|
await storage.saveContext(oldBranch, context)
|
|
69
|
-
console.log(`💾 Saved context for old branch '${oldBranch}'`)
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
// Check for context and show consolidated message
|
|
81
|
+
const branchContext = await storage.loadContext(newBranch)
|
|
82
|
+
if (branchContext) {
|
|
83
|
+
showToast(
|
|
84
|
+
client,
|
|
85
|
+
`Switched to ${newBranch}. Context available - use @branch-memory_load to restore.`,
|
|
86
|
+
'info',
|
|
87
|
+
'Branch Changed',
|
|
88
|
+
5000
|
|
89
|
+
)
|
|
90
|
+
} else {
|
|
91
|
+
showToast(
|
|
92
|
+
client,
|
|
93
|
+
`Switched to ${newBranch}. No saved context for this branch.`,
|
|
94
|
+
'info',
|
|
95
|
+
'Branch Changed',
|
|
96
|
+
3000
|
|
97
|
+
)
|
|
84
98
|
}
|
|
85
99
|
},
|
|
86
100
|
config
|
|
@@ -92,15 +106,20 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
92
106
|
return {
|
|
93
107
|
// Hook: Auto-load context when session is created
|
|
94
108
|
'session.created': async (input: any, output: any) => {
|
|
95
|
-
|
|
109
|
+
// Only show toast if context exists for current branch
|
|
96
110
|
const currentConfig = await configManager.load()
|
|
97
111
|
const branch = await GitOperations.getCurrentBranch()
|
|
98
112
|
|
|
99
113
|
if (branch && currentConfig.contextLoading === 'auto') {
|
|
100
114
|
const branchContext = await storage.loadContext(branch)
|
|
101
115
|
if (branchContext) {
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
showToast(
|
|
117
|
+
client,
|
|
118
|
+
`Context available for ${branch}. Use @branch-memory_load to restore.`,
|
|
119
|
+
'info',
|
|
120
|
+
undefined,
|
|
121
|
+
4000
|
|
122
|
+
)
|
|
104
123
|
}
|
|
105
124
|
}
|
|
106
125
|
},
|
|
@@ -124,17 +143,17 @@ export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory
|
|
|
124
143
|
|
|
125
144
|
// Hook: Cleanup on plugin unload
|
|
126
145
|
unload: () => {
|
|
127
|
-
|
|
146
|
+
// Silent cleanup - no need to notify user
|
|
128
147
|
|
|
129
148
|
// Stop branch monitoring
|
|
130
149
|
branchMonitor.stop()
|
|
131
150
|
|
|
132
151
|
// Save one last time before shutdown
|
|
133
152
|
autoSave('plugin unload').catch((error) => {
|
|
134
|
-
|
|
153
|
+
// Only show error if final save fails
|
|
154
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
155
|
+
showToast(client, `Final save failed: ${errorMessage}`, 'error')
|
|
135
156
|
})
|
|
136
|
-
|
|
137
|
-
console.log('✅ Plugin stopped')
|
|
138
157
|
},
|
|
139
158
|
}
|
|
140
159
|
}
|
package/dist/index.js
CHANGED
|
@@ -62,15 +62,11 @@ class ContextStorage {
|
|
|
62
62
|
try {
|
|
63
63
|
const content = await fs.readFile(filePath, "utf8");
|
|
64
64
|
const data = JSON.parse(content);
|
|
65
|
-
if (data.metadata?.version !== "1.0.0") {
|
|
66
|
-
console.warn(`Context version mismatch for branch '${branch}': ${data.metadata?.version}`);
|
|
67
|
-
}
|
|
65
|
+
if (data.metadata?.version !== "1.0.0") {}
|
|
68
66
|
return data;
|
|
69
67
|
} catch (error) {
|
|
70
|
-
console.error(`Failed to load context for branch '${branch}':`, error);
|
|
71
68
|
const backup = await this.restoreFromBackup(branch);
|
|
72
69
|
if (backup) {
|
|
73
|
-
console.info(`Restored from backup for branch '${branch}'`);
|
|
74
70
|
return backup;
|
|
75
71
|
}
|
|
76
72
|
return null;
|
|
@@ -84,9 +80,7 @@ class ContextStorage {
|
|
|
84
80
|
const backupFile = this.getBackupFile(branch, Date.now());
|
|
85
81
|
await fs.copyFile(filePath, backupFile);
|
|
86
82
|
await this.cleanOldBackups(branch);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.warn("Failed to create backup:", error);
|
|
89
|
-
}
|
|
83
|
+
} catch (error) {}
|
|
90
84
|
}
|
|
91
85
|
async cleanOldBackups(branch) {
|
|
92
86
|
try {
|
|
@@ -99,9 +93,7 @@ class ContextStorage {
|
|
|
99
93
|
for (const backup of backups) {
|
|
100
94
|
await fs.unlink(path.join(this.storageDir, backup.name)).catch(() => {});
|
|
101
95
|
}
|
|
102
|
-
} catch (error) {
|
|
103
|
-
console.warn("Failed to clean old backups:", error);
|
|
104
|
-
}
|
|
96
|
+
} catch (error) {}
|
|
105
97
|
}
|
|
106
98
|
async restoreFromBackup(branch) {
|
|
107
99
|
try {
|
|
@@ -119,9 +111,7 @@ class ContextStorage {
|
|
|
119
111
|
continue;
|
|
120
112
|
}
|
|
121
113
|
}
|
|
122
|
-
} catch (error) {
|
|
123
|
-
console.error("Failed to restore from backup:", error);
|
|
124
|
-
}
|
|
114
|
+
} catch (error) {}
|
|
125
115
|
return null;
|
|
126
116
|
}
|
|
127
117
|
async listBranches() {
|
|
@@ -143,7 +133,6 @@ class ContextStorage {
|
|
|
143
133
|
}
|
|
144
134
|
return branches;
|
|
145
135
|
} catch (error) {
|
|
146
|
-
console.error("Failed to list branches:", error);
|
|
147
136
|
return [];
|
|
148
137
|
}
|
|
149
138
|
}
|
|
@@ -176,7 +165,6 @@ class ContextStorage {
|
|
|
176
165
|
fileCount: data.metadata?.fileCount || 0
|
|
177
166
|
};
|
|
178
167
|
} catch (error) {
|
|
179
|
-
console.error("Failed to get metadata:", error);
|
|
180
168
|
return {
|
|
181
169
|
size: "Error",
|
|
182
170
|
modified: "Error",
|
|
@@ -198,9 +186,7 @@ class ContextStorage {
|
|
|
198
186
|
for (const backup of backups) {
|
|
199
187
|
await fs.unlink(path.join(this.storageDir, backup)).catch(() => {});
|
|
200
188
|
}
|
|
201
|
-
} catch (error) {
|
|
202
|
-
console.error("Failed to delete backups:", error);
|
|
203
|
-
}
|
|
189
|
+
} catch (error) {}
|
|
204
190
|
}
|
|
205
191
|
}
|
|
206
192
|
// branch-memory/git.ts
|
|
@@ -379,20 +365,9 @@ class ContextInjector {
|
|
|
379
365
|
}
|
|
380
366
|
async injectContext(branchContext) {
|
|
381
367
|
const summary = this.formatContextSummary(branchContext);
|
|
382
|
-
console.log(`
|
|
383
|
-
\uD83D\uDCE5 Context injected for branch:`, branchContext.branch);
|
|
384
|
-
console.log("─".repeat(50));
|
|
385
|
-
console.log(summary);
|
|
386
|
-
console.log("─".repeat(50));
|
|
387
368
|
}
|
|
388
369
|
async askUserAboutContextLoading(branchContext) {
|
|
389
370
|
const summary = this.formatContextSummary(branchContext);
|
|
390
|
-
console.log(`
|
|
391
|
-
\uD83D\uDCE5 Context available for branch:`, branchContext.branch);
|
|
392
|
-
console.log("─".repeat(50));
|
|
393
|
-
console.log(summary);
|
|
394
|
-
console.log("─".repeat(50));
|
|
395
|
-
console.log("Load this context? (y/n)");
|
|
396
371
|
return true;
|
|
397
372
|
}
|
|
398
373
|
formatContextSummary(context) {
|
|
@@ -475,7 +450,6 @@ class ConfigManager {
|
|
|
475
450
|
const userConfig = JSON.parse(content);
|
|
476
451
|
return this.deepMerge(DEFAULT_CONFIG, userConfig);
|
|
477
452
|
} catch (error) {
|
|
478
|
-
console.warn("Failed to load config, using defaults:", error instanceof Error ? error.message : error);
|
|
479
453
|
return { ...DEFAULT_CONFIG };
|
|
480
454
|
}
|
|
481
455
|
}
|
|
@@ -513,6 +487,21 @@ class ConfigManager {
|
|
|
513
487
|
return result;
|
|
514
488
|
}
|
|
515
489
|
}
|
|
490
|
+
|
|
491
|
+
// branch-memory/index.ts
|
|
492
|
+
function showToast(client, message, variant = "info", title, duration) {
|
|
493
|
+
try {
|
|
494
|
+
client?.global?.tui?.showToast?.({
|
|
495
|
+
title: title || "Branch Memory",
|
|
496
|
+
message,
|
|
497
|
+
variant,
|
|
498
|
+
duration: duration || (variant === "error" ? undefined : 3000)
|
|
499
|
+
});
|
|
500
|
+
} catch (error) {
|
|
501
|
+
console.log(`[${variant.toUpperCase()}] ${title || "Branch Memory"}: ${message}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
516
505
|
// ../node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
517
506
|
var exports_external = {};
|
|
518
507
|
__export(exports_external, {
|
|
@@ -12848,20 +12837,23 @@ var save = tool({
|
|
|
12848
12837
|
const config2 = await ConfigManager.load();
|
|
12849
12838
|
const currentBranch = await GitOperations.getCurrentBranch();
|
|
12850
12839
|
if (!currentBranch) {
|
|
12840
|
+
showToast(context.client, "Not on a git branch, context not saved", "warning");
|
|
12851
12841
|
return "⚠️ Not on a git branch, context not saved";
|
|
12852
12842
|
}
|
|
12853
12843
|
const collector = new ContextCollector(config2);
|
|
12854
12844
|
const branchContext = await collector.collectContext(args.includeMessages ?? config2.context.defaultInclude.includes("messages"), args.includeTodos ?? config2.context.defaultInclude.includes("todos"), args.includeFiles ?? config2.context.defaultInclude.includes("files"), args.description || "");
|
|
12855
12845
|
const storage = new ContextStorage(ConfigManager.getStorageDir(process.cwd()));
|
|
12856
12846
|
await storage.saveContext(currentBranch, branchContext);
|
|
12847
|
+
showToast(context.client, `Saved ${branchContext.metadata.messageCount} messages, ${branchContext.metadata.todoCount} todos, ${branchContext.metadata.fileCount} files`, "success");
|
|
12857
12848
|
return `✅ Saved context for branch '${currentBranch}'
|
|
12858
12849
|
├─ Messages: ${branchContext.metadata.messageCount}
|
|
12859
12850
|
├─ Todos: ${branchContext.metadata.todoCount}
|
|
12860
12851
|
├─ Files: ${branchContext.metadata.fileCount}
|
|
12861
12852
|
└─ Size: ${(branchContext.metadata.size / 1024).toFixed(1)}KB`;
|
|
12862
12853
|
} catch (error45) {
|
|
12863
|
-
|
|
12864
|
-
|
|
12854
|
+
const errorMessage = error45 instanceof Error ? error45.message : "Unknown error";
|
|
12855
|
+
showToast(context.client, `Failed to save context: ${errorMessage}`, "error");
|
|
12856
|
+
return `❌ Failed to save context: ${errorMessage}`;
|
|
12865
12857
|
}
|
|
12866
12858
|
}
|
|
12867
12859
|
});
|
|
@@ -12876,23 +12868,27 @@ var load = tool({
|
|
|
12876
12868
|
const git = GitOperations;
|
|
12877
12869
|
const targetBranch = args.branch || await git.getCurrentBranch();
|
|
12878
12870
|
if (!targetBranch) {
|
|
12871
|
+
showToast(context.client, "Not on a git branch", "warning");
|
|
12879
12872
|
return "⚠️ Not on a git branch";
|
|
12880
12873
|
}
|
|
12881
12874
|
const storage = new ContextStorage(ConfigManager.getStorageDir(process.cwd()));
|
|
12882
12875
|
const branchContext = await storage.loadContext(targetBranch);
|
|
12883
12876
|
if (!branchContext) {
|
|
12877
|
+
showToast(context.client, `No saved context found for branch '${targetBranch}'`, "warning");
|
|
12884
12878
|
return `⚠️ No context found for branch '${targetBranch}'`;
|
|
12885
12879
|
}
|
|
12886
12880
|
const injector = new ContextInjector(context);
|
|
12887
12881
|
await injector.injectContext(branchContext);
|
|
12882
|
+
showToast(context.client, `Loaded context from ${branchContext.savedAt.substring(0, 10)}`, "success");
|
|
12888
12883
|
return `✅ Loaded context for branch '${targetBranch}'
|
|
12889
12884
|
├─ Saved: ${branchContext.savedAt.substring(0, 10)}...
|
|
12890
12885
|
├─ Messages: ${branchContext.metadata.messageCount}
|
|
12891
12886
|
├─ Todos: ${branchContext.metadata.todoCount}
|
|
12892
12887
|
└─ Files: ${branchContext.metadata.fileCount}`;
|
|
12893
12888
|
} catch (error45) {
|
|
12894
|
-
|
|
12895
|
-
|
|
12889
|
+
const errorMessage = error45 instanceof Error ? error45.message : "Unknown error";
|
|
12890
|
+
showToast(context.client, `Failed to load context: ${errorMessage}`, "error");
|
|
12891
|
+
return `❌ Failed to load context: ${errorMessage}`;
|
|
12896
12892
|
}
|
|
12897
12893
|
}
|
|
12898
12894
|
});
|
|
@@ -12960,8 +12956,9 @@ No saved contexts found
|
|
|
12960
12956
|
`;
|
|
12961
12957
|
return output;
|
|
12962
12958
|
} catch (error45) {
|
|
12963
|
-
|
|
12964
|
-
|
|
12959
|
+
const errorMessage = error45 instanceof Error ? error45.message : "Unknown error";
|
|
12960
|
+
showToast(context.client, `Failed to get status: ${errorMessage}`, "error");
|
|
12961
|
+
return `❌ Failed to get status: ${errorMessage}`;
|
|
12965
12962
|
}
|
|
12966
12963
|
}
|
|
12967
12964
|
});
|
|
@@ -12975,10 +12972,12 @@ var deleteContext = tool({
|
|
|
12975
12972
|
ConfigManager.setProjectPath(process.cwd());
|
|
12976
12973
|
const storage = new ContextStorage(ConfigManager.getStorageDir(process.cwd()));
|
|
12977
12974
|
await storage.deleteContext(args.branch);
|
|
12975
|
+
showToast(context.client, `Deleted context for branch '${args.branch}'`, "success");
|
|
12978
12976
|
return `✅ Deleted context for branch '${args.branch}'`;
|
|
12979
12977
|
} catch (error45) {
|
|
12980
|
-
|
|
12981
|
-
|
|
12978
|
+
const errorMessage = error45 instanceof Error ? error45.message : "Unknown error";
|
|
12979
|
+
showToast(context.client, `Failed to delete context: ${errorMessage}`, "error");
|
|
12980
|
+
return `❌ Failed to delete context: ${errorMessage}`;
|
|
12982
12981
|
}
|
|
12983
12982
|
}
|
|
12984
12983
|
});
|
|
@@ -13026,24 +13025,25 @@ Total: ${branches.length} branch(es)
|
|
|
13026
13025
|
`;
|
|
13027
13026
|
return output;
|
|
13028
13027
|
} catch (error45) {
|
|
13029
|
-
|
|
13030
|
-
|
|
13028
|
+
const errorMessage = error45 instanceof Error ? error45.message : "Unknown error";
|
|
13029
|
+
showToast(context.client, `Failed to list contexts: ${errorMessage}`, "error");
|
|
13030
|
+
return `❌ Failed to list contexts: ${errorMessage}`;
|
|
13031
13031
|
}
|
|
13032
13032
|
}
|
|
13033
13033
|
});
|
|
13034
13034
|
|
|
13035
13035
|
// index.ts
|
|
13036
13036
|
var BranchMemoryPlugin = async (ctx) => {
|
|
13037
|
-
console.log("\uD83E\uDDE0 Branch Memory Plugin initializing...");
|
|
13038
13037
|
ConfigManager.setProjectPath(ctx.directory);
|
|
13039
13038
|
const isGitRepo = await GitOperations.isGitRepo();
|
|
13040
13039
|
if (!isGitRepo) {
|
|
13041
|
-
|
|
13040
|
+
showToast(ctx.client, "Not in a git repository. Branch memory features disabled.", "warning");
|
|
13042
13041
|
return {};
|
|
13043
13042
|
}
|
|
13044
13043
|
const storage = new ContextStorage(ConfigManager.getStorageDir(ctx.directory));
|
|
13045
13044
|
const collector = new ContextCollector(await ConfigManager.load());
|
|
13046
13045
|
let lastAutoSave = 0;
|
|
13046
|
+
let saveCount = 0;
|
|
13047
13047
|
const AUTO_SAVE_THROTTLE = 5000;
|
|
13048
13048
|
const autoSave = async (reason) => {
|
|
13049
13049
|
const config2 = await ConfigManager.load();
|
|
@@ -13056,10 +13056,14 @@ var BranchMemoryPlugin = async (ctx) => {
|
|
|
13056
13056
|
const context = await collector.collectContext(config2.context.defaultInclude.includes("messages"), config2.context.defaultInclude.includes("todos"), config2.context.defaultInclude.includes("files"), reason);
|
|
13057
13057
|
await storage.saveContext(currentBranch2, context);
|
|
13058
13058
|
lastAutoSave = now;
|
|
13059
|
-
|
|
13059
|
+
saveCount++;
|
|
13060
|
+
if (saveCount === 1 || saveCount % 10 === 0) {
|
|
13061
|
+
showToast(ctx.client, `Context saved for ${currentBranch2}`, "success", undefined, 2000);
|
|
13062
|
+
}
|
|
13060
13063
|
}
|
|
13061
13064
|
} catch (error45) {
|
|
13062
|
-
|
|
13065
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
13066
|
+
showToast(ctx.client, `Failed to save context: ${errorMessage}`, "error");
|
|
13063
13067
|
}
|
|
13064
13068
|
}
|
|
13065
13069
|
}
|
|
@@ -13072,26 +13076,19 @@ var BranchMemoryPlugin = async (ctx) => {
|
|
|
13072
13076
|
const oldBranchName = currentBranch;
|
|
13073
13077
|
currentBranch = newBranch;
|
|
13074
13078
|
const config2 = await ConfigManager.load();
|
|
13075
|
-
console.log(`\uD83D\uDD04 Branch changed: ${oldBranchName || "(none)"} → ${newBranch}`);
|
|
13076
13079
|
if (oldBranchName && config2.autoSave.onBranchChange) {
|
|
13077
13080
|
await storage.saveContext(oldBranchName, await collector.collectContext(config2.context.defaultInclude.includes("messages"), config2.context.defaultInclude.includes("todos"), config2.context.defaultInclude.includes("files"), "branch change"));
|
|
13078
|
-
console.log(`\uD83D\uDCBE Saved context for old branch '${oldBranchName}'`);
|
|
13079
13081
|
}
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
} else {
|
|
13086
|
-
console.log(`ℹ️ No saved context for branch '${newBranch}'`);
|
|
13087
|
-
}
|
|
13088
|
-
} else if (config2.contextLoading === "ask") {
|
|
13089
|
-
console.log(`ℹ️ Context available for branch '${newBranch}'`);
|
|
13090
|
-
console.log(` Use @branch-memory_save to restore it`);
|
|
13082
|
+
const branchContext = await storage.loadContext(newBranch);
|
|
13083
|
+
if (branchContext) {
|
|
13084
|
+
showToast(ctx.client, `Switched to ${newBranch}. Context available - use @branch-memory_load to restore.`, "info", "Branch Changed", 5000);
|
|
13085
|
+
} else {
|
|
13086
|
+
showToast(ctx.client, `Switched to ${newBranch}. No saved context for this branch.`, "info", "Branch Changed", 3000);
|
|
13091
13087
|
}
|
|
13092
13088
|
}
|
|
13093
13089
|
} catch (error45) {
|
|
13094
|
-
|
|
13090
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
13091
|
+
showToast(ctx.client, `Error monitoring branch: ${errorMessage}`, "error");
|
|
13095
13092
|
}
|
|
13096
13093
|
};
|
|
13097
13094
|
const branchMonitorInterval = setInterval(monitorBranch, 2000);
|
|
@@ -13104,14 +13101,12 @@ var BranchMemoryPlugin = async (ctx) => {
|
|
|
13104
13101
|
deleteContext
|
|
13105
13102
|
},
|
|
13106
13103
|
"session.created": async (input, output) => {
|
|
13107
|
-
console.log("\uD83D\uDE80 Session created - checking for saved context...");
|
|
13108
13104
|
const config2 = await ConfigManager.load();
|
|
13109
13105
|
const branch = await GitOperations.getCurrentBranch();
|
|
13110
13106
|
if (branch && config2.contextLoading === "auto") {
|
|
13111
13107
|
const branchContext = await storage.loadContext(branch);
|
|
13112
13108
|
if (branchContext) {
|
|
13113
|
-
|
|
13114
|
-
console.log(` Use @branch-memory_save to restore it`);
|
|
13109
|
+
showToast(ctx.client, `Context available for ${branch}. Use @branch-memory_load to restore.`, "info", undefined, 4000);
|
|
13115
13110
|
}
|
|
13116
13111
|
}
|
|
13117
13112
|
},
|
|
@@ -13128,12 +13123,11 @@ var BranchMemoryPlugin = async (ctx) => {
|
|
|
13128
13123
|
}
|
|
13129
13124
|
},
|
|
13130
13125
|
unload: () => {
|
|
13131
|
-
console.log("\uD83E\uDDE0 Branch Memory Plugin shutting down...");
|
|
13132
13126
|
clearInterval(branchMonitorInterval);
|
|
13133
13127
|
autoSave("plugin unload").catch((error45) => {
|
|
13134
|
-
|
|
13128
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
13129
|
+
showToast(ctx.client, `Final save failed: ${errorMessage}`, "error");
|
|
13135
13130
|
});
|
|
13136
|
-
console.log("✅ Plugin stopped");
|
|
13137
13131
|
}
|
|
13138
13132
|
};
|
|
13139
13133
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-branch-memory-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Automatically manages branch-specific context for OpenCode with auto-save, auto-load, and git workflow integration",
|
|
5
5
|
"author": "Davidcreador",
|
|
6
6
|
"license": "MIT",
|