opencode-branch-memory-manager 0.1.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/.opencode/branch-memory/.gitkeep +0 -0
- package/.opencode/branch-memory/collector.ts +90 -0
- package/.opencode/branch-memory/config.ts +127 -0
- package/.opencode/branch-memory/git.ts +169 -0
- package/.opencode/branch-memory/index.ts +7 -0
- package/.opencode/branch-memory/injector.ts +108 -0
- package/.opencode/branch-memory/monitor.ts +230 -0
- package/.opencode/branch-memory/storage.ts +322 -0
- package/.opencode/branch-memory/types.ts +68 -0
- package/.opencode/dist/branch-memory.js +13040 -0
- package/.opencode/package-lock.json +82 -0
- package/.opencode/plugin/branch-memory-plugin.ts +149 -0
- package/.opencode/tool/branch-memory.ts +254 -0
- package/LICENSE +21 -0
- package/README.md +500 -0
- package/package.json +61 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-branch-memory-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "opencode-branch-memory-plugin",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"chokidar": "^4.0.0",
|
|
12
|
+
"zod": "^3.22.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@opencode-ai/plugin": "latest"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"node_modules/@opencode-ai/plugin": {
|
|
19
|
+
"version": "1.0.220",
|
|
20
|
+
"resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.0.220.tgz",
|
|
21
|
+
"integrity": "sha512-PlhuyWCjVqAgvM+4HO/syri+JXnypGp0owLkKjF7H285IU8V1geNY2RRSv17hAGp2C0gwwmZNyA63VEUbiJVCQ==",
|
|
22
|
+
"dev": true,
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@opencode-ai/sdk": "1.0.220",
|
|
25
|
+
"zod": "4.1.8"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"node_modules/@opencode-ai/plugin/node_modules/zod": {
|
|
29
|
+
"version": "4.1.8",
|
|
30
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
|
|
31
|
+
"integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
|
|
32
|
+
"dev": true,
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"funding": {
|
|
35
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"node_modules/@opencode-ai/sdk": {
|
|
39
|
+
"version": "1.0.220",
|
|
40
|
+
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.0.220.tgz",
|
|
41
|
+
"integrity": "sha512-OllJJ+SjTSzbccGAat2uj5Vka56B2bxm+9SkFpFV0BXquaJA21KRdt1TgJOQnnzo6A/Zlwbx+ecmJ+Xai01JZA==",
|
|
42
|
+
"dev": true
|
|
43
|
+
},
|
|
44
|
+
"node_modules/chokidar": {
|
|
45
|
+
"version": "4.0.3",
|
|
46
|
+
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
|
47
|
+
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"readdirp": "^4.0.1"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">= 14.16.0"
|
|
54
|
+
},
|
|
55
|
+
"funding": {
|
|
56
|
+
"url": "https://paulmillr.com/funding/"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"node_modules/readdirp": {
|
|
60
|
+
"version": "4.1.2",
|
|
61
|
+
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
|
62
|
+
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">= 14.18.0"
|
|
66
|
+
},
|
|
67
|
+
"funding": {
|
|
68
|
+
"type": "individual",
|
|
69
|
+
"url": "https://paulmillr.com/funding/"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"node_modules/zod": {
|
|
73
|
+
"version": "3.25.76",
|
|
74
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
|
75
|
+
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
|
76
|
+
"license": "MIT",
|
|
77
|
+
"funding": {
|
|
78
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { Plugin } from '@opencode-ai/plugin'
|
|
2
|
+
import { ContextStorage, GitOperations, ContextCollector, ConfigManager } from '../branch-memory/index.js'
|
|
3
|
+
|
|
4
|
+
export const BranchMemoryPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
|
|
5
|
+
console.log('š§ Branch Memory Plugin initializing...')
|
|
6
|
+
|
|
7
|
+
// Load configuration
|
|
8
|
+
ConfigManager.setProjectPath(directory)
|
|
9
|
+
|
|
10
|
+
// Check if we're in a git repository
|
|
11
|
+
const isGitRepo = await GitOperations.isGitRepo()
|
|
12
|
+
if (!isGitRepo) {
|
|
13
|
+
console.log('ā ļø Not in a git repository, branch memory disabled')
|
|
14
|
+
return {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const storage = new ContextStorage(ConfigManager.getStorageDir(directory))
|
|
18
|
+
const collector = new ContextCollector(await ConfigManager.load())
|
|
19
|
+
|
|
20
|
+
// Track last auto-save time to avoid too frequent saves
|
|
21
|
+
let lastAutoSave = 0
|
|
22
|
+
const AUTO_SAVE_THROTTLE = 5000 // 5 seconds
|
|
23
|
+
|
|
24
|
+
// Auto-save function with throttling
|
|
25
|
+
const autoSave = async (reason: string) => {
|
|
26
|
+
const config = await ConfigManager.load()
|
|
27
|
+
if (config.autoSave.enabled) {
|
|
28
|
+
const now = Date.now()
|
|
29
|
+
|
|
30
|
+
if (now - lastAutoSave > AUTO_SAVE_THROTTLE) {
|
|
31
|
+
try {
|
|
32
|
+
const currentBranch = await GitOperations.getCurrentBranch()
|
|
33
|
+
|
|
34
|
+
if (currentBranch) {
|
|
35
|
+
const context = await collector.collectContext(
|
|
36
|
+
config.context.defaultInclude.includes('messages'),
|
|
37
|
+
config.context.defaultInclude.includes('todos'),
|
|
38
|
+
config.context.defaultInclude.includes('files'),
|
|
39
|
+
reason
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
await storage.saveContext(currentBranch, context)
|
|
43
|
+
lastAutoSave = now
|
|
44
|
+
console.log(`š¾ Auto-saved context for branch '${currentBranch}' (${reason})`)
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Auto-save failed:', error)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Track current branch for monitoring
|
|
54
|
+
let currentBranch: string | null = null
|
|
55
|
+
|
|
56
|
+
// Monitor git branch changes
|
|
57
|
+
const monitorBranch = async () => {
|
|
58
|
+
try {
|
|
59
|
+
const newBranch = await GitOperations.getCurrentBranch()
|
|
60
|
+
|
|
61
|
+
if (newBranch && newBranch !== currentBranch) {
|
|
62
|
+
const oldBranchName = currentBranch
|
|
63
|
+
currentBranch = newBranch
|
|
64
|
+
const config = await ConfigManager.load()
|
|
65
|
+
|
|
66
|
+
console.log(`š Branch changed: ${oldBranchName || '(none)'} ā ${newBranch}`)
|
|
67
|
+
|
|
68
|
+
// Auto-save old branch context
|
|
69
|
+
if (oldBranchName && config.autoSave.onBranchChange) {
|
|
70
|
+
await storage.saveContext(oldBranchName, await collector.collectContext(
|
|
71
|
+
config.context.defaultInclude.includes('messages'),
|
|
72
|
+
config.context.defaultInclude.includes('todos'),
|
|
73
|
+
config.context.defaultInclude.includes('files'),
|
|
74
|
+
'branch change'
|
|
75
|
+
))
|
|
76
|
+
console.log(`š¾ Saved context for old branch '${oldBranchName}'`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Auto-load new branch context
|
|
80
|
+
if (config.contextLoading === 'auto') {
|
|
81
|
+
const branchContext = await storage.loadContext(newBranch)
|
|
82
|
+
if (branchContext) {
|
|
83
|
+
console.log(`š„ Found context for branch '${newBranch}'`)
|
|
84
|
+
console.log(' Use @branch-memory_load to restore it')
|
|
85
|
+
} else {
|
|
86
|
+
console.log(`ā¹ļø No saved context for branch '${newBranch}'`)
|
|
87
|
+
}
|
|
88
|
+
} else if (config.contextLoading === 'ask') {
|
|
89
|
+
console.log(`ā¹ļø Context available for branch '${newBranch}'`)
|
|
90
|
+
console.log(` Use @branch-memory_load to restore it`)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error monitoring branch:', error)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Start branch monitoring interval
|
|
99
|
+
const branchMonitorInterval = setInterval(monitorBranch, 2000)
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
// Hook: Auto-load context when session is created
|
|
103
|
+
'session.created': async (input: any, output: any) => {
|
|
104
|
+
console.log('š Session created - checking for saved context...')
|
|
105
|
+
const config = await ConfigManager.load()
|
|
106
|
+
const branch = await GitOperations.getCurrentBranch()
|
|
107
|
+
|
|
108
|
+
if (branch && config.contextLoading === 'auto') {
|
|
109
|
+
const branchContext = await storage.loadContext(branch)
|
|
110
|
+
if (branchContext) {
|
|
111
|
+
console.log(`š„ Found context for branch '${branch}'`)
|
|
112
|
+
console.log(' Use @branch-memory_load to restore it')
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Hook: Auto-save before tool execution
|
|
118
|
+
'tool.execute.before': async (input: any, output: any) => {
|
|
119
|
+
await autoSave('tool execution')
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Hook: Auto-save when session is updated (periodic checkpoints)
|
|
123
|
+
'session.updated': async (input: any, output: any) => {
|
|
124
|
+
const config = await ConfigManager.load()
|
|
125
|
+
if (config.autoSave.enabled) {
|
|
126
|
+
const now = Date.now()
|
|
127
|
+
// Only auto-save periodically (every 60 seconds)
|
|
128
|
+
if (now - lastAutoSave > 60000) {
|
|
129
|
+
await autoSave('session update')
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// Hook: Cleanup on plugin unload
|
|
135
|
+
unload: () => {
|
|
136
|
+
console.log('š§ Branch Memory Plugin shutting down...')
|
|
137
|
+
|
|
138
|
+
// Clear the branch monitor interval
|
|
139
|
+
clearInterval(branchMonitorInterval)
|
|
140
|
+
|
|
141
|
+
// Save one last time before shutdown
|
|
142
|
+
autoSave('plugin unload').catch((error) => {
|
|
143
|
+
console.error('Final save failed:', error)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
console.log('ā
Plugin stopped')
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { tool, type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
|
+
import {
|
|
3
|
+
ContextStorage,
|
|
4
|
+
GitOperations,
|
|
5
|
+
ContextCollector,
|
|
6
|
+
ConfigManager,
|
|
7
|
+
ContextInjector,
|
|
8
|
+
} from "../branch-memory/index.js";
|
|
9
|
+
import type { ToolContext } from "@opencode-ai/plugin";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Save current session context for current git branch with optional filters
|
|
13
|
+
*/
|
|
14
|
+
export const save: ToolDefinition = tool({
|
|
15
|
+
description:
|
|
16
|
+
"Save current session context for current git branch with optional filters",
|
|
17
|
+
args: {
|
|
18
|
+
includeMessages: tool.schema
|
|
19
|
+
.boolean()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Include conversation messages"),
|
|
22
|
+
includeTodos: tool.schema
|
|
23
|
+
.boolean()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Include todo items"),
|
|
26
|
+
includeFiles: tool.schema
|
|
27
|
+
.boolean()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Include file references"),
|
|
30
|
+
description: tool.schema
|
|
31
|
+
.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Description of what you are saving"),
|
|
34
|
+
},
|
|
35
|
+
async execute(args, context: ToolContext) {
|
|
36
|
+
try {
|
|
37
|
+
ConfigManager.setProjectPath(process.cwd());
|
|
38
|
+
const config = await ConfigManager.load();
|
|
39
|
+
|
|
40
|
+
const currentBranch = await GitOperations.getCurrentBranch();
|
|
41
|
+
|
|
42
|
+
if (!currentBranch) {
|
|
43
|
+
return "ā ļø Not on a git branch, context not saved";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const collector = new ContextCollector(config);
|
|
47
|
+
const branchContext = await collector.collectContext(
|
|
48
|
+
args.includeMessages ??
|
|
49
|
+
config.context.defaultInclude.includes("messages"),
|
|
50
|
+
args.includeTodos ?? config.context.defaultInclude.includes("todos"),
|
|
51
|
+
args.includeFiles ?? config.context.defaultInclude.includes("files"),
|
|
52
|
+
args.description || "",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const storage = new ContextStorage(
|
|
56
|
+
ConfigManager.getStorageDir(process.cwd()),
|
|
57
|
+
);
|
|
58
|
+
await storage.saveContext(currentBranch, branchContext);
|
|
59
|
+
|
|
60
|
+
return `ā
Saved context for branch '${currentBranch}'
|
|
61
|
+
āā Messages: ${branchContext.metadata.messageCount}
|
|
62
|
+
āā Todos: ${branchContext.metadata.todoCount}
|
|
63
|
+
āā Files: ${branchContext.metadata.fileCount}
|
|
64
|
+
āā Size: ${(branchContext.metadata.size / 1024).toFixed(1)}KB`;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error("Error saving context:", error);
|
|
67
|
+
return `ā Failed to save context: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Load branch-specific context into current session
|
|
74
|
+
*/
|
|
75
|
+
export const load: ToolDefinition = tool({
|
|
76
|
+
description: "Load branch-specific context into current session",
|
|
77
|
+
args: {
|
|
78
|
+
branch: tool.schema
|
|
79
|
+
.string()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Branch name (default: current branch)"),
|
|
82
|
+
},
|
|
83
|
+
async execute(args, context: ToolContext) {
|
|
84
|
+
try {
|
|
85
|
+
ConfigManager.setProjectPath(process.cwd());
|
|
86
|
+
|
|
87
|
+
const git = GitOperations;
|
|
88
|
+
const targetBranch = args.branch || (await git.getCurrentBranch());
|
|
89
|
+
|
|
90
|
+
if (!targetBranch) {
|
|
91
|
+
return "ā ļø Not on a git branch";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const storage = new ContextStorage(
|
|
95
|
+
ConfigManager.getStorageDir(process.cwd()),
|
|
96
|
+
);
|
|
97
|
+
const branchContext = await storage.loadContext(targetBranch);
|
|
98
|
+
|
|
99
|
+
if (!branchContext) {
|
|
100
|
+
return `ā ļø No context found for branch '${targetBranch}'`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const injector = new ContextInjector(context);
|
|
104
|
+
await injector.injectContext(branchContext);
|
|
105
|
+
|
|
106
|
+
return `ā
Loaded context for branch '${targetBranch}'
|
|
107
|
+
āā Saved: ${branchContext.savedAt.substring(0, 10)}...
|
|
108
|
+
āā Messages: ${branchContext.metadata.messageCount}
|
|
109
|
+
āā Todos: ${branchContext.metadata.todoCount}
|
|
110
|
+
āā Files: ${branchContext.metadata.fileCount}`;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error("Error loading context:", error);
|
|
113
|
+
return `ā Failed to load context: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Show branch memory status and available contexts
|
|
120
|
+
*/
|
|
121
|
+
export const status: ToolDefinition = tool({
|
|
122
|
+
description: "Show branch memory status and available contexts",
|
|
123
|
+
args: {},
|
|
124
|
+
async execute(args, context: ToolContext) {
|
|
125
|
+
try {
|
|
126
|
+
ConfigManager.setProjectPath(process.cwd());
|
|
127
|
+
|
|
128
|
+
const git = GitOperations;
|
|
129
|
+
const currentBranch = await git.getCurrentBranch();
|
|
130
|
+
const storage = new ContextStorage(
|
|
131
|
+
ConfigManager.getStorageDir(process.cwd()),
|
|
132
|
+
);
|
|
133
|
+
const branches = await storage.listBranches();
|
|
134
|
+
|
|
135
|
+
let output = "\nš Branch Memory Status";
|
|
136
|
+
output += "\nāāāāāāāāāāāāāāāāāāāāā\n";
|
|
137
|
+
|
|
138
|
+
if (currentBranch) {
|
|
139
|
+
output += `Current branch: ${currentBranch}\n\n`;
|
|
140
|
+
|
|
141
|
+
const branchContext = await storage.loadContext(currentBranch);
|
|
142
|
+
if (branchContext) {
|
|
143
|
+
output += `Current context:\n`;
|
|
144
|
+
output += ` š Messages: ${branchContext.metadata.messageCount}\n`;
|
|
145
|
+
output += ` ā
Todos: ${branchContext.metadata.todoCount}\n`;
|
|
146
|
+
output += ` š Files: ${branchContext.metadata.fileCount}\n`;
|
|
147
|
+
output += ` š¾ Size: ${(branchContext.metadata.size / 1024).toFixed(1)}KB\n`;
|
|
148
|
+
output += ` ā° Saved: ${branchContext.savedAt}\n`;
|
|
149
|
+
if (branchContext.data.description) {
|
|
150
|
+
output += ` š Description: ${branchContext.data.description}\n`;
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
output += `Current branch has no saved context\n`;
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
output += `Not in a git repository\n`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (branches.length > 0) {
|
|
160
|
+
output += "\nAvailable contexts:\n";
|
|
161
|
+
for (const branch of branches) {
|
|
162
|
+
const meta = await storage.getMetadata(branch);
|
|
163
|
+
const marker = branch === currentBranch ? "ā " : " ";
|
|
164
|
+
output += `${marker}${branch} (${meta.size}, ${meta.modified.substring(0, 10)}...)\n`;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
output += "\nNo saved contexts found\n";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
output += "āāāāāāāāāāāāāāāāāāāāā\n";
|
|
171
|
+
|
|
172
|
+
return output;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error("Error getting status:", error);
|
|
175
|
+
return `ā Failed to get status: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Delete saved context for a branch
|
|
182
|
+
*/
|
|
183
|
+
export const deleteContext: ToolDefinition = tool({
|
|
184
|
+
description: "Delete saved context for a branch",
|
|
185
|
+
args: {
|
|
186
|
+
branch: tool.schema.string().describe("Branch name to delete context for"),
|
|
187
|
+
},
|
|
188
|
+
async execute(args, context: ToolContext) {
|
|
189
|
+
try {
|
|
190
|
+
ConfigManager.setProjectPath(process.cwd());
|
|
191
|
+
|
|
192
|
+
const storage = new ContextStorage(
|
|
193
|
+
ConfigManager.getStorageDir(process.cwd()),
|
|
194
|
+
);
|
|
195
|
+
await storage.deleteContext(args.branch);
|
|
196
|
+
|
|
197
|
+
return `ā
Deleted context for branch '${args.branch}'`;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error("Error deleting context:", error);
|
|
200
|
+
return `ā Failed to delete context: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* List all branches with saved contexts
|
|
207
|
+
*/
|
|
208
|
+
export const list: ToolDefinition = tool({
|
|
209
|
+
description: "List all branches with saved contexts",
|
|
210
|
+
args: {
|
|
211
|
+
verbose: tool.schema
|
|
212
|
+
.boolean()
|
|
213
|
+
.optional()
|
|
214
|
+
.describe("Show detailed information"),
|
|
215
|
+
},
|
|
216
|
+
async execute(args, context: ToolContext) {
|
|
217
|
+
try {
|
|
218
|
+
ConfigManager.setProjectPath(process.cwd());
|
|
219
|
+
|
|
220
|
+
const storage = new ContextStorage(
|
|
221
|
+
ConfigManager.getStorageDir(process.cwd()),
|
|
222
|
+
);
|
|
223
|
+
const branches = await storage.listBranches();
|
|
224
|
+
|
|
225
|
+
if (branches.length === 0) {
|
|
226
|
+
return "No saved contexts found";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let output = "\nš Branches with saved contexts\n";
|
|
230
|
+
output += "āāāāāāāāāāāāāāāāāāāāā\n";
|
|
231
|
+
|
|
232
|
+
for (const branch of branches) {
|
|
233
|
+
const meta = await storage.getMetadata(branch);
|
|
234
|
+
output += `\n${branch}\n`;
|
|
235
|
+
output += ` š¾ Size: ${meta.size}\n`;
|
|
236
|
+
output += ` ā° Modified: ${meta.modified}\n`;
|
|
237
|
+
|
|
238
|
+
if (args.verbose) {
|
|
239
|
+
output += ` š Messages: ${meta.messageCount}\n`;
|
|
240
|
+
output += ` ā
Todos: ${meta.todoCount}\n`;
|
|
241
|
+
output += ` š Files: ${meta.fileCount}\n`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
output += "\nāāāāāāāāāāāāāāāāāāāāā\n";
|
|
246
|
+
output += `\nTotal: ${branches.length} branch(es)\n`;
|
|
247
|
+
|
|
248
|
+
return output;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error("Error listing contexts:", error);
|
|
251
|
+
return `ā Failed to list contexts: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
});
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 OpenCode Branch Memory Manager Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the
|
|
10
|
+
Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|