@abarcenas/ai-workflow-template 1.25.0 → 1.28.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/.agents/instructions/agent-skill.instructions.md +18 -20
- package/.agents/instructions/agent.instructions.md +111 -570
- package/.agents/instructions/ai-prompt-engineering-safety.instructions.md +3 -3
- package/.agents/instructions/instructions.instructions.md +6 -7
- package/.agents/instructions/memory-bank.instructions.md +72 -18
- package/.agents/instructions/memory-schema.instructions.md +75 -0
- package/.agents/instructions/prompt.instructions.md +25 -30
- package/.agents/skills/conventional-commit/SKILL.md +2 -2
- package/.agents/skills/github-issues/SKILL.md +2 -2
- package/.agents/skills/memory-search/SKILL.md +69 -0
- package/.agents/skills/microsoft-docs/SKILL.md +1 -1
- package/.agents/skills/polygot-test-agent/SKILL.md +0 -1
- package/.agents/skills/prompt-builder/SKILL.md +15 -14
- package/.husky/post-merge +10 -0
- package/.husky/pre-commit +2 -0
- package/.opencode/agents/architect.agent.md +2 -1
- package/.opencode/agents/coder.agent.md +3 -1
- package/.opencode/agents/deployer.agent.md +1 -0
- package/.opencode/agents/designer.agent.md +2 -1
- package/.opencode/agents/e2e-tester.agent.md +1 -0
- package/.opencode/agents/implementer.agent.md +2 -1
- package/.opencode/agents/orchestrator/feature-pipeline.agent.md +4 -3
- package/.opencode/agents/orchestrator/orchestrator.agent.md +4 -3
- package/.opencode/agents/orchestrator/tdd-orchestrator.agent.md +8 -4
- package/.opencode/agents/researcher.agent.md +1 -0
- package/.opencode/agents/reviewer.agent.md +1 -0
- package/.opencode/agents/tracker.agent.md +2 -1
- package/.opencode/agents/unit-tester.agent.md +1 -0
- package/README.md +75 -4
- package/memory-bank/.vocabulary.json +52 -0
- package/opencode.mcp.example.json +7 -1
- package/package.json +18 -3
- package/scripts/mcp-memory-server.js +243 -0
- package/scripts/memory-cli.js +110 -0
- package/scripts/memory-index.js +512 -0
- package/scripts/normalize-memory.js +349 -0
- package/scripts/validate-memory-schema.js +190 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* mcp-memory-server.js — MCP server exposing memory-bank tools to OpenCode.
|
|
5
|
+
*
|
|
6
|
+
* Provides: memory_search, memory_rebuild, memory_get
|
|
7
|
+
* Uses stdio transport — OpenCode spawns this as a child process.
|
|
8
|
+
*
|
|
9
|
+
* Configuration (opencode.json):
|
|
10
|
+
* "memory-bank": {
|
|
11
|
+
* "type": "local",
|
|
12
|
+
* "command": ["node", "scripts/mcp-memory-server.js"],
|
|
13
|
+
* "enabled": true
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
18
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
|
+
import {
|
|
20
|
+
CallToolRequestSchema,
|
|
21
|
+
ListToolsRequestSchema,
|
|
22
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
23
|
+
import { search, rebuildIndex, incrementalUpdate, getFile, getStats } from './memory-index.js';
|
|
24
|
+
|
|
25
|
+
const VALID_FILES = [
|
|
26
|
+
'activeContext', 'systemPatterns', 'techContext',
|
|
27
|
+
'productContext', 'progress', 'projectbrief',
|
|
28
|
+
'tracker-log', 'architecture-context', 'orchestrator-log',
|
|
29
|
+
'spike-vector-db-memory', 'playwright-mcp-configuration',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const server = new Server(
|
|
33
|
+
{
|
|
34
|
+
name: 'memory-bank',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
capabilities: {
|
|
39
|
+
tools: {},
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// ── List tools ───────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
47
|
+
tools: [
|
|
48
|
+
{
|
|
49
|
+
name: 'memory_search',
|
|
50
|
+
description:
|
|
51
|
+
'Semantic search across the project memory bank (activeContext, systemPatterns, progress, decisions, tasks). ' +
|
|
52
|
+
'Returns ranked chunks with source file and similarity score. ' +
|
|
53
|
+
'Use this instead of reading all memory files to save tokens.',
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
query: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'Natural language query about project context, decisions, or status',
|
|
60
|
+
},
|
|
61
|
+
topK: {
|
|
62
|
+
type: 'number',
|
|
63
|
+
default: 5,
|
|
64
|
+
description: 'Number of results to return (1-20)',
|
|
65
|
+
},
|
|
66
|
+
sourceFile: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'Optional: filter to a specific memory file (e.g., "activeContext", "systemPatterns")',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ['query'],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'memory_rebuild',
|
|
76
|
+
description:
|
|
77
|
+
'Full rebuild of the memory vector index from memory-bank/*.md and docs/*.md files. ' +
|
|
78
|
+
'Re-indexes every file regardless of change. ' +
|
|
79
|
+
'Use memory_update instead for faster incremental sync (only changed files). ' +
|
|
80
|
+
'The index is disposable — rebuilding is safe.',
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'memory_update',
|
|
88
|
+
description:
|
|
89
|
+
'Incremental update — only re-indexes files whose content changed (SHA-256 content hash). ' +
|
|
90
|
+
'Much faster than memory_rebuild for daily use (~2 seconds vs minutes). ' +
|
|
91
|
+
'Run this after completing tasks, writing to memory-bank, or after git pull.',
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'memory_get',
|
|
99
|
+
description:
|
|
100
|
+
'Read the full content of a specific memory file by name. ' +
|
|
101
|
+
'Use when you need the complete file, not just search snippets.',
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
properties: {
|
|
105
|
+
file: {
|
|
106
|
+
type: 'string',
|
|
107
|
+
description: 'File ID without extension. One of: ' + VALID_FILES.join(', '),
|
|
108
|
+
enum: VALID_FILES,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
required: ['file'],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'memory_stats',
|
|
116
|
+
description: 'Get statistics about the memory index (vector count, file count, database size).',
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
// ── Handle tool calls ────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
128
|
+
const { name, arguments: args } = request.params;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
switch (name) {
|
|
132
|
+
case 'memory_search': {
|
|
133
|
+
const results = await search(args.query, {
|
|
134
|
+
topK: args.topK || 5,
|
|
135
|
+
sourceFile: args.sourceFile || undefined,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (results.length === 0) {
|
|
139
|
+
return {
|
|
140
|
+
content: [{
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: 'No results found. The index may be empty. Try running memory_rebuild first.',
|
|
143
|
+
}],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const formatted = results
|
|
148
|
+
.map((r, i) =>
|
|
149
|
+
`### Result ${i + 1} (score: ${r.score})\n` +
|
|
150
|
+
`**File:** \`${r.source_file}\`\n\n${r.content}`
|
|
151
|
+
)
|
|
152
|
+
.join('\n\n---\n\n');
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
content: [{ type: 'text', text: formatted }],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case 'memory_rebuild': {
|
|
160
|
+
const result = await rebuildIndex();
|
|
161
|
+
return {
|
|
162
|
+
content: [{
|
|
163
|
+
type: 'text',
|
|
164
|
+
text: `Index rebuilt successfully.\n` +
|
|
165
|
+
`- Chunks indexed: ${result.indexed}\n` +
|
|
166
|
+
`- Chunks skipped (unchanged): ${result.skipped}\n` +
|
|
167
|
+
`- Total vectors: ${result.stats.totalVectors}\n` +
|
|
168
|
+
`- Total files: ${result.stats.totalFiles}\n` +
|
|
169
|
+
`- DB size: ${result.stats.dbSize}`,
|
|
170
|
+
}],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case 'memory_update': {
|
|
175
|
+
const result = await incrementalUpdate();
|
|
176
|
+
return {
|
|
177
|
+
content: [{
|
|
178
|
+
type: 'text',
|
|
179
|
+
text: `Index updated incrementally.\n` +
|
|
180
|
+
`- Chunks re-indexed: ${result.indexed}\n` +
|
|
181
|
+
`- Chunks cached (unchanged): ${result.skipped}\n` +
|
|
182
|
+
(result.changed.length > 0
|
|
183
|
+
? `- Changed files: ${result.changed.join(', ')}\n`
|
|
184
|
+
: '- No files changed.\n') +
|
|
185
|
+
`- Total vectors: ${(await getStats()).totalVectors}`,
|
|
186
|
+
}],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
case 'memory_get': {
|
|
191
|
+
const content = getFile(args.file);
|
|
192
|
+
if (!content) {
|
|
193
|
+
return {
|
|
194
|
+
content: [{
|
|
195
|
+
type: 'text',
|
|
196
|
+
text: `File not found: ${args.file}.md. Available files: ${VALID_FILES.join(', ')}`,
|
|
197
|
+
}],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
content: [{ type: 'text', text: content }],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
case 'memory_stats': {
|
|
206
|
+
const stats = getStats();
|
|
207
|
+
return {
|
|
208
|
+
content: [{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: `**Memory Index Stats**\n` +
|
|
211
|
+
`- Total vectors: ${stats.totalVectors}\n` +
|
|
212
|
+
`- Total files indexed: ${stats.totalFiles}\n` +
|
|
213
|
+
`- Database size: ${stats.dbSize}`,
|
|
214
|
+
}],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
default:
|
|
219
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
220
|
+
}
|
|
221
|
+
} catch (err) {
|
|
222
|
+
return {
|
|
223
|
+
content: [{
|
|
224
|
+
type: 'text',
|
|
225
|
+
text: `Error: ${err.message}`,
|
|
226
|
+
}],
|
|
227
|
+
isError: true,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// ── Start server ─────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
async function main() {
|
|
235
|
+
const transport = new StdioServerTransport();
|
|
236
|
+
await server.connect(transport);
|
|
237
|
+
console.error('[memory-bank-mcp] Server started via stdio transport');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
main().catch((err) => {
|
|
241
|
+
console.error('[memory-bank-mcp] Fatal error:', err);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* memory-cli.js — CLI for manual memory index operations.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node scripts/memory-cli.js search "<query>" [--topK 5] [--file activeContext]
|
|
8
|
+
* node scripts/memory-cli.js rebuild
|
|
9
|
+
* node scripts/memory-cli.js update (incremental)
|
|
10
|
+
* node scripts/memory-cli.js get <fileId>
|
|
11
|
+
* node scripts/memory-cli.js stats
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { search, rebuildIndex, incrementalUpdate, getFile, getStats, closeDB } from './memory-index.js';
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const command = args[0];
|
|
18
|
+
|
|
19
|
+
function parseFlags(argv) {
|
|
20
|
+
const flags = {};
|
|
21
|
+
const positional = [];
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
if (argv[i] === '--topK' && i + 1 < argv.length) {
|
|
24
|
+
flags.topK = parseInt(argv[++i], 10);
|
|
25
|
+
} else if (argv[i] === '--file' && i + 1 < argv.length) {
|
|
26
|
+
flags.sourceFile = argv[++i];
|
|
27
|
+
} else {
|
|
28
|
+
positional.push(argv[i]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { flags, positional };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function main() {
|
|
35
|
+
try {
|
|
36
|
+
switch (command) {
|
|
37
|
+
case 'search': {
|
|
38
|
+
const { flags, positional } = parseFlags(args.slice(1));
|
|
39
|
+
const query = positional.join(' ');
|
|
40
|
+
if (!query) {
|
|
41
|
+
console.error('Usage: node scripts/memory-cli.js search "<query>" [--topK 5] [--file name]');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const results = await search(query, {
|
|
46
|
+
topK: flags.topK || 5,
|
|
47
|
+
sourceFile: flags.sourceFile,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (results.length === 0) {
|
|
51
|
+
console.log(JSON.stringify({ results: [], hint: 'No results. Try running "rebuild" first.' }));
|
|
52
|
+
} else {
|
|
53
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
case 'rebuild': {
|
|
59
|
+
const result = await rebuildIndex();
|
|
60
|
+
console.log(JSON.stringify({ status: 'ok', ...result }, null, 2));
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case 'update': {
|
|
65
|
+
const result = await incrementalUpdate();
|
|
66
|
+
console.log(JSON.stringify({ status: 'ok', ...result }, null, 2));
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case 'get': {
|
|
71
|
+
const fileId = args[1];
|
|
72
|
+
if (!fileId) {
|
|
73
|
+
console.error('Usage: node scripts/memory-cli.js get <fileId>');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const content = getFile(fileId);
|
|
77
|
+
if (content) {
|
|
78
|
+
console.log(content);
|
|
79
|
+
} else {
|
|
80
|
+
console.error(`File not found: ${fileId}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case 'stats': {
|
|
87
|
+
console.log(JSON.stringify(getStats(), null, 2));
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
console.error(`Usage: node scripts/memory-cli.js <search|rebuild|update|get|stats> [args...]`);
|
|
93
|
+
console.error('');
|
|
94
|
+
console.error('Commands:');
|
|
95
|
+
console.error(' search "<query>" [--topK N] [--file name] Semantic search');
|
|
96
|
+
console.error(' rebuild Full index rebuild');
|
|
97
|
+
console.error(' update Incremental update');
|
|
98
|
+
console.error(' get <fileId> Read a memory file');
|
|
99
|
+
console.error(' stats Index statistics');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(`Error: ${err.message}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
} finally {
|
|
106
|
+
closeDB();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|