@abarcenas/ai-workflow-template 1.41.0 → 1.44.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.
Files changed (30) hide show
  1. package/.agents/instructions/knowledge-retrieval.instructions.md +69 -0
  2. package/.agents/instructions/knowledgebase.instructions.md +43 -22
  3. package/.agents/instructions/learned-knowledge.instructions.md +180 -0
  4. package/.agents/instructions/memory-schema.instructions.md +22 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.opencode/agents/architect.agent.md +1 -0
  7. package/.opencode/agents/coder.agent.md +1 -1
  8. package/.opencode/agents/deployer.agent.md +3 -2
  9. package/.opencode/agents/designer.agent.md +4 -3
  10. package/.opencode/agents/e2e-tester.agent.md +5 -4
  11. package/.opencode/agents/implementer.agent.md +1 -0
  12. package/.opencode/agents/orchestrator/feature-pipeline.agent.md +6 -2
  13. package/.opencode/agents/orchestrator/orchestrator.agent.md +5 -1
  14. package/.opencode/agents/orchestrator/tdd-orchestrator.agent.md +7 -1
  15. package/.opencode/agents/researcher.agent.md +9 -1
  16. package/.opencode/agents/reviewer.agent.md +2 -0
  17. package/.opencode/agents/tracker.agent.md +8 -6
  18. package/.opencode/agents/unit-tester.agent.md +6 -5
  19. package/README.md +124 -304
  20. package/memory-bank/.vocabulary.json +125 -31
  21. package/package.json +1 -1
  22. package/scripts/mcp-knowledgebase-server.js +85 -19
  23. package/scripts/mcp-knowledgebase-server.test.js +745 -0
  24. package/scripts/setup/constants.js +2 -1
  25. package/scripts/sync.js +1 -0
  26. package/scripts/sync.test.js +4 -3
  27. package/scripts/validate-memory-schema.js +28 -3
  28. package/scripts/validate-memory-schema.test.js +328 -0
  29. package/scripts/vocab-sync.js +216 -0
  30. package/scripts/vocab-sync.test.js +814 -0
@@ -9,50 +9,144 @@
9
9
  "task": "Individual task tracking (tasks/TASK*.md)",
10
10
  "decision": "ADR-style architecture/technical decisions"
11
11
  },
12
-
13
12
  "tags": {
14
13
  "agent_roles": [
15
- "designer", "architect", "researcher", "coder", "implementer",
16
- "tester", "reviewer", "tracker", "deployer", "orchestrator"
14
+ "designer",
15
+ "architect",
16
+ "researcher",
17
+ "coder",
18
+ "implementer",
19
+ "tester",
20
+ "reviewer",
21
+ "tracker",
22
+ "deployer",
23
+ "orchestrator"
17
24
  ],
18
25
  "workflow": [
19
- "bootstrap", "setup", "configuration", "migration", "refactor",
20
- "tdd", "feature-pipeline", "onboarding", "normalization",
21
- "research", "spike", "implementation", "implementation-planning",
26
+ "bootstrap",
27
+ "setup",
28
+ "configuration",
29
+ "migration",
30
+ "refactor",
31
+ "tdd",
32
+ "feature-pipeline",
33
+ "onboarding",
34
+ "normalization",
35
+ "research",
36
+ "spike",
37
+ "implementation",
38
+ "implementation-planning",
22
39
  "documentation"
23
40
  ],
24
41
  "memory_ops": [
25
- "decision", "discovery", "status-update", "task-plan", "retrospective",
26
- "bug-fix", "verification"
42
+ "decision",
43
+ "discovery",
44
+ "status-update",
45
+ "task-plan",
46
+ "retrospective",
47
+ "bug-fix",
48
+ "verification"
27
49
  ],
28
50
  "topic": [
29
- "knowledgebase", "pgvector", "mcp", "embeddings", "dotenv",
30
- "chunk-parser", "agent-exercise"
51
+ "agent-exercise",
52
+ "agents",
53
+ "chunk-parser",
54
+ "commit",
55
+ "credential",
56
+ "dotenv",
57
+ "embeddings",
58
+ "env-remediation",
59
+ "feature-vocab-sync",
60
+ "fix-kb-registerproject",
61
+ "float32array",
62
+ "git",
63
+ "graceful-failure",
64
+ "knowledge-vector-gaps",
65
+ "knowledgebase",
66
+ "learned-knowledge",
67
+ "mcp",
68
+ "package-structure",
69
+ "pgvector",
70
+ "pipeline-follow-up",
71
+ "pre-commit",
72
+ "readme",
73
+ "security",
74
+ "test-hardening",
75
+ "vocabulary"
31
76
  ]
32
77
  },
33
-
34
78
  "entity_patterns": {
35
- "vitest": ["vitest", "vitest\\.config", "@vitest\\/coverage"],
36
- "playwright": ["playwright", "@playwright", "browser.*test"],
37
- "graphify": ["graphify", "knowledge graph", "graph\\.json"],
38
- "memory-bank": ["memory-bank", "memory bank", "activeContext", "systemPatterns"],
39
- "husky": ["husky", "pre-commit", "git hook"],
40
- "tdd-orchestrator": ["tdd orchestrator", "RED.*GREEN.*REFACTOR"],
41
- "mcp-server": ["mcp", "modelcontextprotocol", "stdio", "mcp server"],
42
- "sqlite-vec": ["sqlite-vec", "sqlite.vec", "better-sqlite3"],
43
- "transformers.js": ["@xenova\\/transformers", "transformers\\.js", "all-MiniLM"],
44
- "opencode": ["opencode", "OpenCode", "open code"],
45
- "npm": ["npm", "npm package", "npm install"]
79
+ "vitest": [
80
+ "vitest",
81
+ "vitest\\.config",
82
+ "@vitest\\/coverage"
83
+ ],
84
+ "playwright": [
85
+ "playwright",
86
+ "@playwright",
87
+ "browser.*test"
88
+ ],
89
+ "graphify": [
90
+ "graphify",
91
+ "knowledge graph",
92
+ "graph\\.json"
93
+ ],
94
+ "memory-bank": [
95
+ "memory-bank",
96
+ "memory bank",
97
+ "activeContext",
98
+ "systemPatterns"
99
+ ],
100
+ "husky": [
101
+ "husky",
102
+ "pre-commit",
103
+ "git hook"
104
+ ],
105
+ "tdd-orchestrator": [
106
+ "tdd orchestrator",
107
+ "RED.*GREEN.*REFACTOR"
108
+ ],
109
+ "mcp-server": [
110
+ "mcp",
111
+ "modelcontextprotocol",
112
+ "stdio",
113
+ "mcp server"
114
+ ],
115
+ "sqlite-vec": [
116
+ "sqlite-vec",
117
+ "sqlite.vec",
118
+ "better-sqlite3"
119
+ ],
120
+ "transformers.js": [
121
+ "@xenova\\/transformers",
122
+ "transformers\\.js",
123
+ "all-MiniLM"
124
+ ],
125
+ "opencode": [
126
+ "opencode",
127
+ "OpenCode",
128
+ "open code"
129
+ ],
130
+ "npm": [
131
+ "npm",
132
+ "npm package",
133
+ "npm install"
134
+ ]
46
135
  },
47
-
48
136
  "file_category_map": {
49
- "projectbrief": "brief",
50
- "productContext": "product",
51
- "systemPatterns": "architecture",
52
- "techContext": "tech",
53
- "activeContext": "context",
54
- "progress": "progress"
137
+ "projectbrief": "brief",
138
+ "productContext": "product",
139
+ "systemPatterns": "architecture",
140
+ "techContext": "tech",
141
+ "activeContext": "context",
142
+ "progress": "progress"
55
143
  },
56
-
57
- "doc_types": ["spike", "architecture", "configuration", "orchestrator-log", "tracker-log", "decision"]
144
+ "doc_types": [
145
+ "spike",
146
+ "architecture",
147
+ "configuration",
148
+ "orchestrator-log",
149
+ "tracker-log",
150
+ "decision"
151
+ ]
58
152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abarcenas/ai-workflow-template",
3
- "version": "1.41.0",
3
+ "version": "1.44.0",
4
4
  "description": "A comprehensive template repository for AI-assisted development workflows using GitHub Copilot. This template provides a structured approach to leveraging AI assistance through pre-configured skills, prompts, agents, and coding standards.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -30,11 +30,13 @@ import {
30
30
  chunkLearnedKnowledge,
31
31
  getStats,
32
32
  listProjects,
33
+ registerProject,
33
34
  getPool,
34
35
  closePool,
35
36
  } from './knowledgebase-index.js';
36
37
  import { readFileSync } from 'node:fs';
37
38
  import { resolve } from 'node:path';
39
+ import { fileURLToPath } from 'node:url';
38
40
 
39
41
  const server = new Server(
40
42
  {
@@ -72,7 +74,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
72
74
  },
73
75
  threshold: {
74
76
  type: 'number',
75
- default: 0.6,
77
+ default: 0.1,
76
78
  description: 'Minimum similarity threshold (0.0–1.0). Higher values return fewer but more relevant results.',
77
79
  },
78
80
  limit: {
@@ -130,18 +132,65 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
130
132
  ],
131
133
  }));
132
134
 
135
+ // ── Helpers ──────────────────────────────────────────────────────────────
136
+
137
+ /**
138
+ * Redact connection-string credentials from an error message.
139
+ *
140
+ * Every postgres:// or postgresql:// URL token in the message has its
141
+ * userinfo credentials replaced with *** and its query string (or fragment)
142
+ * replaced with ?*** / #***, while the host and path are preserved verbatim
143
+ * (e.g. postgres://user:secret@host:5432/kb?password=hunter2 →
144
+ * postgres://***@host:5432/kb?***). Messages without a URL token are returned
145
+ * unchanged so generic errors (e.g. "Unknown tool: ...") stay readable — the
146
+ * engine helper returns '***' for non-URLs, which would hide every error
147
+ * message.
148
+ *
149
+ * Unlike the engine helper (knowledgebase-index.js:62) — which fails closed
150
+ * by returning '***' when `new URL()` throws — this helper FAILS CLOSED on
151
+ * unparseable tokens by using a pure regex instead of URL parsing: libpq
152
+ * unix-socket authorities such as postgres://user:secret@/var/run/postgresql
153
+ * (or postgresql://user:secret@/tmp?host=/tmp) make `new URL()` throw, so a
154
+ * parse-based helper would return the message unchanged and leak the
155
+ * credentials. The regex strips the userinfo and the query/fragment from
156
+ * EVERY token and preserves the host/path even when the URL cannot be parsed.
157
+ *
158
+ * @param {string} message
159
+ * @returns {string}
160
+ */
161
+ function redactConnectionString(message) {
162
+ if (typeof message !== 'string' || message.length === 0) return message;
163
+ return message.replace(
164
+ /(postgres(?:ql)?:\/\/)([^/\s]+)@([^?#\s]*)([?#][^\s]*)?/gi,
165
+ (match, scheme, userinfo, hostAndPath, queryOrFragment) =>
166
+ `${scheme}***@${hostAndPath}${queryOrFragment ? queryOrFragment[0] + '***' : ''}`
167
+ );
168
+ }
169
+
133
170
  // ── Handle tool calls ────────────────────────────────────────────────
134
171
 
135
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
172
+ /**
173
+ * Handle an MCP tool call.
174
+ *
175
+ * Exported for testability — the stdio bootstrap (main) is guarded to
176
+ * direct-run only, so tests can import this handler without opening a
177
+ * stdio transport or a database connection.
178
+ * @param {{ params: { name: string, arguments: Record<string, any> } }} request
179
+ * @returns {Promise<{ content: Array<{ type: string, text: string }>, isError?: boolean }>}
180
+ */
181
+ export async function handleToolCall(request) {
136
182
  const { name, arguments: args } = request.params;
137
183
 
138
184
  try {
139
185
  switch (name) {
140
186
  case 'knowledgebase_search': {
187
+ // projectId is an optional filter — a whitespace-only value is dropped
188
+ // to match-all (graceful degradation, mirroring the INDEX handler's
189
+ // trim/validate parity) and the trimmed value is passed to the engine.
141
190
  const results = await search(args.query, {
142
- project_id: args.projectId || undefined,
191
+ project_id: args.projectId?.trim() || undefined,
143
192
  threshold: args.threshold ?? 0.1,
144
- limit: args.limit || 5,
193
+ limit: args.limit ?? 5,
145
194
  });
146
195
 
147
196
  if (results.length === 0) {
@@ -180,7 +229,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
180
229
 
181
230
  case 'knowledgebase_index': {
182
231
  const projectId = args.projectId;
183
- if (!projectId) {
232
+ if (!projectId?.trim()) {
184
233
  return {
185
234
  content: [{
186
235
  type: 'text',
@@ -189,6 +238,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
189
238
  isError: true,
190
239
  };
191
240
  }
241
+ const pid = projectId.trim();
192
242
 
193
243
  let content = args.content;
194
244
  if (!content) {
@@ -213,25 +263,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
213
263
  }
214
264
  }
215
265
 
216
- const chunks = chunkLearnedKnowledge(content, projectId);
266
+ const chunks = chunkLearnedKnowledge(content, pid);
217
267
  if (chunks.length === 0) {
218
268
  return {
219
269
  content: [{
220
270
  type: 'text',
221
- text: `No valid knowledge chunks found for project "${projectId}". ` +
271
+ text: `No valid knowledge chunks found for project "${pid}". ` +
222
272
  'Ensure the content contains "## Session:" headers with ' +
223
273
  '"**New knowledge:**" sections.',
224
274
  }],
225
275
  };
226
276
  }
227
277
 
278
+ // Register/update the project in the projects table (idempotent upsert)
279
+ await registerProject(pid, pid);
280
+
228
281
  const result = await upsertChunks(chunks);
229
282
  return {
230
283
  content: [{
231
284
  type: 'text',
232
285
  text:
233
286
  `Indexed ${result.inserted} chunks, updated ${result.updated}, ` +
234
- `skipped ${result.skipped} for project "${projectId}".`,
287
+ `skipped ${result.skipped} for project "${pid}".`,
235
288
  }],
236
289
  };
237
290
  }
@@ -307,12 +360,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
307
360
  return {
308
361
  content: [{
309
362
  type: 'text',
310
- text: `Error: ${err.message}`,
363
+ text: `Error: ${redactConnectionString(err.message)}`,
311
364
  }],
312
365
  isError: true,
313
366
  };
314
367
  }
315
- });
368
+ }
369
+
370
+ server.setRequestHandler(CallToolRequestSchema, handleToolCall);
316
371
 
317
372
  // ── Start server ─────────────────────────────────────────────────────
318
373
 
@@ -322,13 +377,24 @@ async function main() {
322
377
  console.error('[knowledgebase-mcp] Server started via stdio transport');
323
378
  }
324
379
 
325
- process.on('SIGTERM', () => {
326
- console.error('[knowledgebase-mcp] SIGTERM received, closing pool');
327
- closePool().catch(() => {});
328
- process.exit(0);
329
- });
380
+ /**
381
+ * Only start the stdio server when this file is executed directly
382
+ * (node scripts/mcp-knowledgebase-server.js). When the module is imported
383
+ * — e.g. by tests exercising the exported handleToolCall — no stdio
384
+ * transport is opened and the process can exit cleanly.
385
+ */
386
+ const isDirectRun =
387
+ process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
330
388
 
331
- main().catch((err) => {
332
- console.error('[knowledgebase-mcp] Fatal error:', err);
333
- process.exit(1);
334
- });
389
+ if (isDirectRun) {
390
+ process.on('SIGTERM', () => {
391
+ console.error('[knowledgebase-mcp] SIGTERM received, closing pool');
392
+ closePool().catch(() => {});
393
+ process.exit(0);
394
+ });
395
+
396
+ main().catch((err) => {
397
+ console.error('[knowledgebase-mcp] Fatal error:', err);
398
+ process.exit(1);
399
+ });
400
+ }