@vedtechsolutions/engram-mcp 1.0.1 → 1.0.3
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/README.md +2 -2
- package/bin/import-pack.js +212 -0
- package/dist/{chunk-AC6AZCP4.js → chunk-QU7DOPA4.js} +32 -2
- package/dist/hook.js +15 -4
- package/dist/index.js +1 -1
- package/package.json +4 -2
- package/packs/claude-code-patterns.json +106 -0
- package/packs/python-patterns.json +106 -0
- package/packs/typescript-patterns.json +106 -0
package/README.md
CHANGED
|
@@ -17,10 +17,10 @@ Engram gives Claude Code **persistent, cross-session memory** that learns from e
|
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
# Install
|
|
20
|
-
npm install -g engram-mcp
|
|
20
|
+
npm install -g @vedtechsolutions/engram-mcp
|
|
21
21
|
|
|
22
22
|
# Configure Claude Code
|
|
23
|
-
npx engram-setup
|
|
23
|
+
npx @vedtechsolutions/engram-mcp setup
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
That's it. Start a new Claude Code session and Engram activates automatically.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Engram Knowledge Pack Importer
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node bin/import-pack.js <pack-file-or-name>
|
|
8
|
+
* npx @vedtechsolutions/engram-mcp import-pack typescript-patterns
|
|
9
|
+
*
|
|
10
|
+
* Reads a JSON knowledge pack and imports memories into the Engram database.
|
|
11
|
+
* Skips duplicates via content similarity check.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
15
|
+
import { resolve, join, dirname } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const PACKS_DIR = join(__dirname, '..', 'packs');
|
|
20
|
+
|
|
21
|
+
function usage() {
|
|
22
|
+
console.log(`
|
|
23
|
+
Engram Knowledge Pack Importer
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
engram-import-pack <pack>
|
|
27
|
+
|
|
28
|
+
Where <pack> is one of:
|
|
29
|
+
- A built-in pack name: claude-code-patterns, typescript-patterns, python-patterns
|
|
30
|
+
- A path to a .json pack file
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
engram-import-pack typescript-patterns
|
|
34
|
+
engram-import-pack ./my-custom-pack.json
|
|
35
|
+
`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function main() {
|
|
40
|
+
const arg = process.argv[2];
|
|
41
|
+
if (!arg || arg === '--help' || arg === '-h') usage();
|
|
42
|
+
|
|
43
|
+
// Resolve pack file path
|
|
44
|
+
let packPath;
|
|
45
|
+
if (existsSync(arg)) {
|
|
46
|
+
packPath = resolve(arg);
|
|
47
|
+
} else if (existsSync(join(PACKS_DIR, `${arg}.json`))) {
|
|
48
|
+
packPath = join(PACKS_DIR, `${arg}.json`);
|
|
49
|
+
} else {
|
|
50
|
+
console.error(` Error: Pack not found: ${arg}`);
|
|
51
|
+
console.error(` Available packs: ${listPacks().join(', ')}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Load pack
|
|
56
|
+
let pack;
|
|
57
|
+
try {
|
|
58
|
+
pack = JSON.parse(readFileSync(packPath, 'utf-8'));
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.error(` Error: Invalid JSON in ${packPath}: ${e.message}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!pack.memories || !Array.isArray(pack.memories)) {
|
|
65
|
+
console.error(' Error: Pack must have a "memories" array');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`\n Importing: ${pack.name ?? packPath}`);
|
|
70
|
+
console.log(` Memories: ${pack.memories.length}`);
|
|
71
|
+
console.log();
|
|
72
|
+
|
|
73
|
+
// Dynamic import of Engram modules (they need the DB initialized)
|
|
74
|
+
const dbPath = resolve(
|
|
75
|
+
process.env.ENGRAM_DB_PATH
|
|
76
|
+
?? join(process.env.HOME ?? '', '.engram', 'engram.db')
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!existsSync(dirname(dbPath))) {
|
|
80
|
+
const { mkdirSync } = await import('node:fs');
|
|
81
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Initialize database
|
|
85
|
+
const { initializeDatabase } = await import('../dist/chunk-AC6AZCP4.js');
|
|
86
|
+
// Fallback: try to import from the compiled output
|
|
87
|
+
let createMemory, searchMemories, generateEmbedding, embeddingToBuffer;
|
|
88
|
+
try {
|
|
89
|
+
const mod = await import('../dist/chunk-AC6AZCP4.js');
|
|
90
|
+
createMemory = mod.createMemory;
|
|
91
|
+
searchMemories = mod.searchMemories;
|
|
92
|
+
generateEmbedding = mod.generateEmbedding;
|
|
93
|
+
embeddingToBuffer = mod.embeddingToBuffer;
|
|
94
|
+
// Init DB
|
|
95
|
+
mod.initializeDatabase({ db_path: dbPath });
|
|
96
|
+
} catch {
|
|
97
|
+
console.error(' Error: Could not load Engram modules.');
|
|
98
|
+
console.error(' Make sure you are running from the engram-mcp package directory.');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let imported = 0;
|
|
103
|
+
let skipped = 0;
|
|
104
|
+
|
|
105
|
+
for (const entry of pack.memories) {
|
|
106
|
+
const { type, content, domains, severity, tags } = entry;
|
|
107
|
+
|
|
108
|
+
if (!type || !content) {
|
|
109
|
+
console.log(` SKIP (missing type or content): ${content?.substring(0, 60) ?? '(empty)'}`);
|
|
110
|
+
skipped++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check for duplicates via keyword search
|
|
115
|
+
try {
|
|
116
|
+
const existing = searchMemories(content.substring(0, 100), 3);
|
|
117
|
+
const isDuplicate = existing.some(m =>
|
|
118
|
+
m.content === content ||
|
|
119
|
+
(m.content.length > 50 && content.includes(m.content.substring(0, 50)))
|
|
120
|
+
);
|
|
121
|
+
if (isDuplicate) {
|
|
122
|
+
console.log(` SKIP (duplicate): ${content.substring(0, 60)}...`);
|
|
123
|
+
skipped++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
} catch { /* search failed, proceed with import */ }
|
|
127
|
+
|
|
128
|
+
// Build type_data based on memory type
|
|
129
|
+
let type_data;
|
|
130
|
+
if (type === 'antipattern') {
|
|
131
|
+
type_data = {
|
|
132
|
+
kind: 'antipattern',
|
|
133
|
+
pattern_description: content,
|
|
134
|
+
correct_approach: null,
|
|
135
|
+
severity: severity ?? 'medium',
|
|
136
|
+
occurrences: 0,
|
|
137
|
+
last_seen: null,
|
|
138
|
+
auto_detected: false,
|
|
139
|
+
};
|
|
140
|
+
} else if (type === 'semantic') {
|
|
141
|
+
type_data = {
|
|
142
|
+
kind: 'semantic',
|
|
143
|
+
knowledge_type: 'convention',
|
|
144
|
+
source: 'knowledge_pack',
|
|
145
|
+
source_episodes: [],
|
|
146
|
+
applicable_versions: null,
|
|
147
|
+
deprecated_in: null,
|
|
148
|
+
};
|
|
149
|
+
} else if (type === 'procedural') {
|
|
150
|
+
type_data = {
|
|
151
|
+
kind: 'procedural',
|
|
152
|
+
steps: [],
|
|
153
|
+
preconditions: [],
|
|
154
|
+
postconditions: [],
|
|
155
|
+
success_count: 0,
|
|
156
|
+
failure_count: 0,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
createMemory({
|
|
162
|
+
type,
|
|
163
|
+
content,
|
|
164
|
+
summary: null,
|
|
165
|
+
encoding_strength: 0.7,
|
|
166
|
+
reinforcement: 1.5,
|
|
167
|
+
confidence: 0.8,
|
|
168
|
+
domains: domains ?? [],
|
|
169
|
+
version: null,
|
|
170
|
+
tags: [...(tags ?? []), 'knowledge-pack', pack.name?.toLowerCase().replace(/\s+/g, '-') ?? 'custom'],
|
|
171
|
+
storage_tier: 'long_term',
|
|
172
|
+
pinned: false,
|
|
173
|
+
type_data,
|
|
174
|
+
encoding_context: {
|
|
175
|
+
framework: domains?.[0] ?? null,
|
|
176
|
+
version: null,
|
|
177
|
+
project: null,
|
|
178
|
+
project_path: null,
|
|
179
|
+
task_type: null,
|
|
180
|
+
files: [],
|
|
181
|
+
error_context: null,
|
|
182
|
+
session_id: `pack-import-${Date.now()}`,
|
|
183
|
+
significance_score: 0.7,
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
console.log(` OK (${type}): ${content.substring(0, 60)}...`);
|
|
187
|
+
imported++;
|
|
188
|
+
} catch (e) {
|
|
189
|
+
console.log(` ERR: ${content.substring(0, 40)}... — ${e.message}`);
|
|
190
|
+
skipped++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`\n Done. Imported: ${imported}, Skipped: ${skipped}`);
|
|
195
|
+
console.log(` Database: ${dbPath}\n`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function listPacks() {
|
|
199
|
+
try {
|
|
200
|
+
const { readdirSync } = await import('node:fs');
|
|
201
|
+
return readdirSync(PACKS_DIR)
|
|
202
|
+
.filter(f => f.endsWith('.json'))
|
|
203
|
+
.map(f => f.replace('.json', ''));
|
|
204
|
+
} catch {
|
|
205
|
+
return ['claude-code-patterns', 'typescript-patterns', 'python-patterns'];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
main().catch(e => {
|
|
210
|
+
console.error(` Fatal: ${e.message}`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
});
|
|
@@ -1710,7 +1710,11 @@ var CONTEXTUAL = {
|
|
|
1710
1710
|
/** Penalty multiplier for project mismatch on episodic memories (0.3 = 70% reduction) */
|
|
1711
1711
|
PROJECT_MISMATCH_PENALTY: 0.3,
|
|
1712
1712
|
/** Boost for failure experiences with lessons (surfaces "I tried this and it didn't work") */
|
|
1713
|
-
FAILURE_EXPERIENCE_BOOST: 0.2
|
|
1713
|
+
FAILURE_EXPERIENCE_BOOST: 0.2,
|
|
1714
|
+
/** Boost when memory files share the same module directory as current file */
|
|
1715
|
+
MODULE_PROXIMITY_BOOST: 0.2,
|
|
1716
|
+
/** Penalty multiplier when memory files are all from a different module (0.3 = 70% reduction) */
|
|
1717
|
+
MODULE_MISMATCH_PENALTY: 0.3
|
|
1714
1718
|
};
|
|
1715
1719
|
var REWARD = {
|
|
1716
1720
|
/** Seed activation boost for positively-reinforced memories */
|
|
@@ -8758,6 +8762,19 @@ function extractCodeContext(code, filePath) {
|
|
|
8758
8762
|
identifiers: uniqueTerms
|
|
8759
8763
|
};
|
|
8760
8764
|
}
|
|
8765
|
+
function extractModuleFromPath(filePath) {
|
|
8766
|
+
const parts = filePath.split(/[/\\]/).filter(Boolean);
|
|
8767
|
+
const markers = ["addons", "custom-addons", "extra-addons", "packages", "apps"];
|
|
8768
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
8769
|
+
if (markers.includes(parts[i]) && i + 1 < parts.length) {
|
|
8770
|
+
return parts[i + 1];
|
|
8771
|
+
}
|
|
8772
|
+
}
|
|
8773
|
+
if (parts.length >= 3) {
|
|
8774
|
+
return parts[parts.length - 3];
|
|
8775
|
+
}
|
|
8776
|
+
return null;
|
|
8777
|
+
}
|
|
8761
8778
|
function detectLanguage(code, filePath) {
|
|
8762
8779
|
if (filePath) {
|
|
8763
8780
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
@@ -9033,6 +9050,18 @@ function contextualBoost(memory, context) {
|
|
|
9033
9050
|
if (context.current_files.length > 0 && enc.files.length > 0) {
|
|
9034
9051
|
const overlap = context.current_files.filter((f) => enc.files.includes(f)).length;
|
|
9035
9052
|
boost += Math.min(overlap * CONTEXTUAL.FILE_BOOST_PER_MATCH, CONTEXTUAL.FILE_BOOST_CAP);
|
|
9053
|
+
if (overlap === 0) {
|
|
9054
|
+
const currentModules = context.current_files.map(extractModuleFromPath).filter(Boolean);
|
|
9055
|
+
const memModules = enc.files.map(extractModuleFromPath).filter(Boolean);
|
|
9056
|
+
if (currentModules.length > 0 && memModules.length > 0) {
|
|
9057
|
+
const moduleOverlap = currentModules.some((m) => memModules.includes(m));
|
|
9058
|
+
if (moduleOverlap) {
|
|
9059
|
+
boost += CONTEXTUAL.MODULE_PROXIMITY_BOOST;
|
|
9060
|
+
} else {
|
|
9061
|
+
return (1 + boost) * CONTEXTUAL.MODULE_MISMATCH_PENALTY;
|
|
9062
|
+
}
|
|
9063
|
+
}
|
|
9064
|
+
}
|
|
9036
9065
|
}
|
|
9037
9066
|
if (context.current_error && enc.error_context) {
|
|
9038
9067
|
const errorKeywords = context.current_error.toLowerCase().split(/\s+/).slice(0, 10);
|
|
@@ -12541,6 +12570,7 @@ export {
|
|
|
12541
12570
|
computeActivationProfile,
|
|
12542
12571
|
contextualRecall,
|
|
12543
12572
|
codeContextRecall,
|
|
12573
|
+
extractModuleFromPath,
|
|
12544
12574
|
findSimilarDecisions,
|
|
12545
12575
|
formatDecisionInjection,
|
|
12546
12576
|
findSimilarChains,
|
|
@@ -12597,4 +12627,4 @@ export {
|
|
|
12597
12627
|
composeProjectUnderstanding,
|
|
12598
12628
|
formatMentalModelInjection
|
|
12599
12629
|
};
|
|
12600
|
-
//# sourceMappingURL=chunk-
|
|
12630
|
+
//# sourceMappingURL=chunk-QU7DOPA4.js.map
|
package/dist/hook.js
CHANGED
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
estimateTokens,
|
|
80
80
|
extractErrorFingerprint,
|
|
81
81
|
extractKeywords,
|
|
82
|
+
extractModuleFromPath,
|
|
82
83
|
findDuplicate,
|
|
83
84
|
findErrorByFingerprint,
|
|
84
85
|
findResolutionForError,
|
|
@@ -172,7 +173,7 @@ import {
|
|
|
172
173
|
updateReasoningChain,
|
|
173
174
|
updateSelfModelFromSession,
|
|
174
175
|
updateTask
|
|
175
|
-
} from "./chunk-
|
|
176
|
+
} from "./chunk-QU7DOPA4.js";
|
|
176
177
|
|
|
177
178
|
// src/hook.ts
|
|
178
179
|
import { readFileSync, writeFileSync, existsSync, renameSync, statSync, readdirSync, unlinkSync, appendFileSync, openSync, readSync, closeSync } from "fs";
|
|
@@ -3527,9 +3528,7 @@ function handlePreWrite(toolInput, argFallback) {
|
|
|
3527
3528
|
if (filePath) {
|
|
3528
3529
|
const fileCount = (watcherState.prewrite_file_counts[filePath] ?? 0) + 1;
|
|
3529
3530
|
watcherState.prewrite_file_counts[filePath] = fileCount;
|
|
3530
|
-
|
|
3531
|
-
skipCodeRecall = fileCount > 1;
|
|
3532
|
-
}
|
|
3531
|
+
skipCodeRecall = fileCount % CODE_CONTEXT_RECALL.PREWRITE_COOLDOWN_PER_FILE !== 1;
|
|
3533
3532
|
saveWatcherState(watcherState);
|
|
3534
3533
|
}
|
|
3535
3534
|
if (!skipCodeRecall && content.length >= CODE_CONTEXT_RECALL.MIN_CONTENT_LENGTH) {
|
|
@@ -3540,18 +3539,30 @@ function handlePreWrite(toolInput, argFallback) {
|
|
|
3540
3539
|
project: watcherState.active_project
|
|
3541
3540
|
}, config.retrieval);
|
|
3542
3541
|
const activeDomain = watcherState.active_domain;
|
|
3542
|
+
const currentModule = filePath ? extractModuleFromPath(filePath) : null;
|
|
3543
|
+
const isModuleMismatch = (mem) => {
|
|
3544
|
+
if (!currentModule) return false;
|
|
3545
|
+
const memFiles = mem.memory.encoding_context?.files;
|
|
3546
|
+
if (!memFiles || memFiles.length === 0) return false;
|
|
3547
|
+
const memModules = memFiles.map(extractModuleFromPath).filter(Boolean);
|
|
3548
|
+
if (memModules.length === 0) return false;
|
|
3549
|
+
return !memModules.includes(currentModule);
|
|
3550
|
+
};
|
|
3543
3551
|
for (const p of codeResult.patterns) {
|
|
3544
3552
|
if (p.activation < CODE_CONTEXT_RECALL.MIN_PREWRITE_ACTIVATION) continue;
|
|
3545
3553
|
if (p.memory.type === "episodic") continue;
|
|
3546
3554
|
if (activeDomain && p.memory.encoding_context?.framework && p.memory.encoding_context.framework !== activeDomain) continue;
|
|
3555
|
+
if (isModuleMismatch(p)) continue;
|
|
3547
3556
|
contextLines.push(`[ENGRAM PATTERN] ${truncate(p.memory.content, 200)}`);
|
|
3548
3557
|
}
|
|
3549
3558
|
for (const c of codeResult.conventions) {
|
|
3550
3559
|
if (c.activation < CODE_CONTEXT_RECALL.MIN_PREWRITE_ACTIVATION) continue;
|
|
3551
3560
|
if (activeDomain && c.memory.encoding_context?.framework && c.memory.encoding_context.framework !== activeDomain) continue;
|
|
3561
|
+
if (isModuleMismatch(c)) continue;
|
|
3552
3562
|
contextLines.push(`[ENGRAM CONVENTION] ${truncate(c.memory.content, 200)}`);
|
|
3553
3563
|
}
|
|
3554
3564
|
for (const p of codeResult.procedural) {
|
|
3565
|
+
if (isModuleMismatch(p)) continue;
|
|
3555
3566
|
contextLines.push(`[ENGRAM HOW-TO] ${truncate(p.memory.content, 200)}`);
|
|
3556
3567
|
}
|
|
3557
3568
|
} catch (e) {
|
package/dist/index.js
CHANGED
|
@@ -154,7 +154,7 @@ import {
|
|
|
154
154
|
vaccinate,
|
|
155
155
|
vacuumDatabase,
|
|
156
156
|
validateMultiPerspective
|
|
157
|
-
} from "./chunk-
|
|
157
|
+
} from "./chunk-QU7DOPA4.js";
|
|
158
158
|
|
|
159
159
|
// src/index.ts
|
|
160
160
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vedtechsolutions/engram-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Cognitive memory system for AI — persistent, cross-session learning via MCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"engram-mcp": "dist/index.js",
|
|
10
10
|
"engram-hook": "dist/hook.js",
|
|
11
|
-
"engram-setup": "bin/setup.js"
|
|
11
|
+
"engram-setup": "bin/setup.js",
|
|
12
|
+
"engram-import-pack": "bin/import-pack.js"
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"setup": "node bin/setup.js",
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
"files": [
|
|
48
49
|
"dist/",
|
|
49
50
|
"bin/",
|
|
51
|
+
"packs/",
|
|
50
52
|
"LICENSE",
|
|
51
53
|
"README.md"
|
|
52
54
|
]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Claude Code Best Practices",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Common patterns and antipatterns for effective Claude Code usage",
|
|
5
|
+
"memories": [
|
|
6
|
+
{
|
|
7
|
+
"type": "antipattern",
|
|
8
|
+
"content": "Don't use sed/awk for file edits in Claude Code. Use the Edit tool — it shows diffs, preserves formatting, and the user can review changes before applying.",
|
|
9
|
+
"domains": ["claude-code"],
|
|
10
|
+
"severity": "high",
|
|
11
|
+
"tags": ["tool-usage"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "antipattern",
|
|
15
|
+
"content": "Don't use find/ls to search for files. Use the Glob tool — it's faster, handles permissions correctly, and returns structured results.",
|
|
16
|
+
"domains": ["claude-code"],
|
|
17
|
+
"severity": "high",
|
|
18
|
+
"tags": ["tool-usage"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "antipattern",
|
|
22
|
+
"content": "Don't use grep/rg via Bash. Use the Grep tool — it has optimized permissions, supports ripgrep syntax, and provides output modes (content, files, count).",
|
|
23
|
+
"domains": ["claude-code"],
|
|
24
|
+
"severity": "high",
|
|
25
|
+
"tags": ["tool-usage"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "antipattern",
|
|
29
|
+
"content": "Don't use cat/head/tail to read files. Use the Read tool — it shows line numbers, supports offset/limit for large files, and can read images and PDFs.",
|
|
30
|
+
"domains": ["claude-code"],
|
|
31
|
+
"severity": "medium",
|
|
32
|
+
"tags": ["tool-usage"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "antipattern",
|
|
36
|
+
"content": "Don't commit unless explicitly asked. Creating unauthorized commits can lose work or include unintended changes. Always confirm with the user first.",
|
|
37
|
+
"domains": ["claude-code", "git"],
|
|
38
|
+
"severity": "high",
|
|
39
|
+
"tags": ["git", "workflow"]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "antipattern",
|
|
43
|
+
"content": "Don't use git push --force or git reset --hard without explicit user permission. These are destructive operations that can lose work.",
|
|
44
|
+
"domains": ["claude-code", "git"],
|
|
45
|
+
"severity": "critical",
|
|
46
|
+
"tags": ["git", "destructive"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"type": "semantic",
|
|
50
|
+
"content": "Read a file before editing it. The Edit tool requires the file to have been read first in the conversation. Always Read before Edit.",
|
|
51
|
+
"domains": ["claude-code"],
|
|
52
|
+
"tags": ["tool-usage"]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"type": "semantic",
|
|
56
|
+
"content": "Use the Agent tool for broad codebase exploration and deep research. Use Glob/Grep directly for simple, directed searches (specific file/class/function).",
|
|
57
|
+
"domains": ["claude-code"],
|
|
58
|
+
"tags": ["tool-usage"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": "semantic",
|
|
62
|
+
"content": "Make independent tool calls in parallel. If multiple reads or searches don't depend on each other, call them all in the same response for efficiency.",
|
|
63
|
+
"domains": ["claude-code"],
|
|
64
|
+
"tags": ["performance"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "antipattern",
|
|
68
|
+
"content": "Don't over-engineer. Only make changes directly requested or clearly necessary. A bug fix doesn't need surrounding code cleaned up. Don't add docstrings, comments, or type annotations to code you didn't change.",
|
|
69
|
+
"domains": ["claude-code"],
|
|
70
|
+
"severity": "medium",
|
|
71
|
+
"tags": ["workflow"]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "semantic",
|
|
75
|
+
"content": "When editing, the old_string must be unique in the file. If it's not unique, include more surrounding context to make it unique, or use replace_all for global renames.",
|
|
76
|
+
"domains": ["claude-code"],
|
|
77
|
+
"tags": ["tool-usage"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"type": "antipattern",
|
|
81
|
+
"content": "Don't skip git hooks with --no-verify. If a pre-commit hook fails, investigate and fix the underlying issue rather than bypassing it.",
|
|
82
|
+
"domains": ["claude-code", "git"],
|
|
83
|
+
"severity": "high",
|
|
84
|
+
"tags": ["git"]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "semantic",
|
|
88
|
+
"content": "For git commits, always use a HEREDOC for the message to ensure proper formatting: git commit -m \"$(cat <<'EOF'\\nMessage here\\nEOF\\n)\"",
|
|
89
|
+
"domains": ["claude-code", "git"],
|
|
90
|
+
"tags": ["git"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"type": "semantic",
|
|
94
|
+
"content": "Avoid backwards-compatibility hacks like renaming unused _vars, re-exporting types, or adding // removed comments. If something is unused, delete it completely.",
|
|
95
|
+
"domains": ["claude-code"],
|
|
96
|
+
"tags": ["code-quality"]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"type": "antipattern",
|
|
100
|
+
"content": "Don't generate or guess URLs unless confident they're for programming help. Only use URLs provided by the user or found in local files.",
|
|
101
|
+
"domains": ["claude-code"],
|
|
102
|
+
"severity": "medium",
|
|
103
|
+
"tags": ["security"]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Python Common Patterns",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Common Python antipatterns and best practices",
|
|
5
|
+
"memories": [
|
|
6
|
+
{
|
|
7
|
+
"type": "antipattern",
|
|
8
|
+
"content": "Never use mutable default arguments: def foo(items=[]). The list is shared across all calls. Use def foo(items=None): items = items or [] instead.",
|
|
9
|
+
"domains": ["python"],
|
|
10
|
+
"severity": "high",
|
|
11
|
+
"tags": ["gotchas"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "antipattern",
|
|
15
|
+
"content": "Don't use bare except: clauses. Always catch specific exceptions: except ValueError or at minimum except Exception. Bare except catches SystemExit and KeyboardInterrupt.",
|
|
16
|
+
"domains": ["python"],
|
|
17
|
+
"severity": "high",
|
|
18
|
+
"tags": ["error-handling"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "antipattern",
|
|
22
|
+
"content": "Don't use 'is' to compare values. 'is' checks identity (same object), not equality. Use == for value comparison. Exception: 'is None' is correct and preferred.",
|
|
23
|
+
"domains": ["python"],
|
|
24
|
+
"severity": "medium",
|
|
25
|
+
"tags": ["gotchas"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "semantic",
|
|
29
|
+
"content": "Use pathlib.Path instead of os.path for path manipulation. Path objects support / operator, are more readable, and handle cross-platform differences: Path('src') / 'main.py'.",
|
|
30
|
+
"domains": ["python"],
|
|
31
|
+
"tags": ["stdlib"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "antipattern",
|
|
35
|
+
"content": "Don't modify a list while iterating over it. Use a list comprehension to filter: items = [x for x in items if x.valid] or iterate over a copy: for x in items[:].",
|
|
36
|
+
"domains": ["python"],
|
|
37
|
+
"severity": "high",
|
|
38
|
+
"tags": ["gotchas"]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "semantic",
|
|
42
|
+
"content": "Use f-strings for string formatting (Python 3.6+): f'{name} is {age}' — faster than .format() and more readable than % formatting.",
|
|
43
|
+
"domains": ["python"],
|
|
44
|
+
"tags": ["style"]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "antipattern",
|
|
48
|
+
"content": "Don't use 'type(x) == int' for type checking. Use isinstance(x, int) — it handles subclasses correctly and supports tuple of types: isinstance(x, (int, float)).",
|
|
49
|
+
"domains": ["python"],
|
|
50
|
+
"severity": "medium",
|
|
51
|
+
"tags": ["type-checking"]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "semantic",
|
|
55
|
+
"content": "Use context managers (with statement) for resource management: files, database connections, locks. They guarantee cleanup even on exceptions: with open('f') as fh:",
|
|
56
|
+
"domains": ["python"],
|
|
57
|
+
"tags": ["patterns"]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"type": "antipattern",
|
|
61
|
+
"content": "Don't use wildcard imports: from module import *. It pollutes the namespace, makes code harder to read, and can cause subtle name collisions. Import explicitly.",
|
|
62
|
+
"domains": ["python"],
|
|
63
|
+
"severity": "medium",
|
|
64
|
+
"tags": ["imports"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "semantic",
|
|
68
|
+
"content": "Use dataclasses for simple data containers (Python 3.7+): @dataclass with type annotations. For immutable data, use @dataclass(frozen=True). Prefer over plain dicts or namedtuples.",
|
|
69
|
+
"domains": ["python"],
|
|
70
|
+
"tags": ["patterns"]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"type": "antipattern",
|
|
74
|
+
"content": "Don't use global variables for shared state. Use function parameters, class attributes, or dependency injection. Globals make testing hard and introduce hidden coupling.",
|
|
75
|
+
"domains": ["python"],
|
|
76
|
+
"severity": "medium",
|
|
77
|
+
"tags": ["architecture"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"type": "semantic",
|
|
81
|
+
"content": "Use enumerate() instead of manual index tracking: for i, item in enumerate(items) — not for i in range(len(items)). Cleaner and less error-prone.",
|
|
82
|
+
"domains": ["python"],
|
|
83
|
+
"tags": ["style"]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"type": "antipattern",
|
|
87
|
+
"content": "Don't concatenate strings in loops with +=. Use ''.join(parts) — string concatenation creates a new string each time (O(n^2)), while join is O(n).",
|
|
88
|
+
"domains": ["python"],
|
|
89
|
+
"severity": "medium",
|
|
90
|
+
"tags": ["performance"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"type": "semantic",
|
|
94
|
+
"content": "Use typing module for type hints: def greet(name: str) -> str. For complex types: list[str], dict[str, int], Optional[str], Union[int, str]. Enables IDE support and static analysis.",
|
|
95
|
+
"domains": ["python"],
|
|
96
|
+
"tags": ["type-checking"]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"type": "antipattern",
|
|
100
|
+
"content": "Don't silence exceptions with pass: except SomeError: pass. At minimum log the error. Silent failures hide bugs and make debugging extremely difficult.",
|
|
101
|
+
"domains": ["python"],
|
|
102
|
+
"severity": "high",
|
|
103
|
+
"tags": ["error-handling"]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "TypeScript Common Patterns",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Common TypeScript antipatterns and best practices",
|
|
5
|
+
"memories": [
|
|
6
|
+
{
|
|
7
|
+
"type": "antipattern",
|
|
8
|
+
"content": "Never use 'as any' to silence type errors. Use type guards (typeof, instanceof, discriminated unions) or proper generics. 'as any' hides real bugs and defeats the purpose of TypeScript.",
|
|
9
|
+
"domains": ["typescript"],
|
|
10
|
+
"severity": "high",
|
|
11
|
+
"tags": ["type-safety"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "antipattern",
|
|
15
|
+
"content": "Don't use == for null checks. Use === null or === undefined explicitly, or use the nullish coalescing operator (??) and optional chaining (?.).",
|
|
16
|
+
"domains": ["typescript"],
|
|
17
|
+
"severity": "medium",
|
|
18
|
+
"tags": ["type-safety"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "antipattern",
|
|
22
|
+
"content": "Don't use @ts-ignore to suppress errors. Use @ts-expect-error instead — it will alert you when the error is fixed, preventing stale suppressions.",
|
|
23
|
+
"domains": ["typescript"],
|
|
24
|
+
"severity": "medium",
|
|
25
|
+
"tags": ["type-safety"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "semantic",
|
|
29
|
+
"content": "ESM requires .js extension in import paths even when the source file is .ts. TypeScript does not rewrite extensions: import { foo } from './bar.js' (not ./bar.ts).",
|
|
30
|
+
"domains": ["typescript", "node"],
|
|
31
|
+
"tags": ["esm", "modules"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "antipattern",
|
|
35
|
+
"content": "Don't use enum for string unions. Use 'type Foo = \"a\" | \"b\" | \"c\"' instead — it produces no runtime JavaScript, tree-shakes better, and is more idiomatic.",
|
|
36
|
+
"domains": ["typescript"],
|
|
37
|
+
"severity": "medium",
|
|
38
|
+
"tags": ["patterns"]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "antipattern",
|
|
42
|
+
"content": "Don't mutate function parameters. TypeScript won't warn you but it causes subtle bugs. Clone objects/arrays before modifying: const copy = { ...obj } or [...arr].",
|
|
43
|
+
"domains": ["typescript"],
|
|
44
|
+
"severity": "medium",
|
|
45
|
+
"tags": ["immutability"]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"type": "semantic",
|
|
49
|
+
"content": "Use 'satisfies' to validate a value matches a type without widening: const config = { port: 3000 } satisfies Config. This preserves literal types while checking structure.",
|
|
50
|
+
"domains": ["typescript"],
|
|
51
|
+
"tags": ["type-safety"]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "antipattern",
|
|
55
|
+
"content": "Don't use ! (non-null assertion) liberally. It tells TypeScript 'trust me, this isn't null' — but if you're wrong, it's a runtime crash. Use proper null checks or optional chaining.",
|
|
56
|
+
"domains": ["typescript"],
|
|
57
|
+
"severity": "high",
|
|
58
|
+
"tags": ["type-safety"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"type": "semantic",
|
|
62
|
+
"content": "Use Omit<T, K> to create types without specific properties, Pick<T, K> to select specific ones. Prefer these over redefining interfaces manually.",
|
|
63
|
+
"domains": ["typescript"],
|
|
64
|
+
"tags": ["utility-types"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "antipattern",
|
|
68
|
+
"content": "Don't use Object as a type. Use Record<string, unknown> for dictionaries, or define a proper interface. 'Object' matches almost anything and provides no safety.",
|
|
69
|
+
"domains": ["typescript"],
|
|
70
|
+
"severity": "medium",
|
|
71
|
+
"tags": ["type-safety"]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "semantic",
|
|
75
|
+
"content": "Use 'readonly' on properties and arrays that shouldn't be mutated: readonly items: readonly string[]. This catches accidental mutation at compile time.",
|
|
76
|
+
"domains": ["typescript"],
|
|
77
|
+
"tags": ["immutability"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"type": "antipattern",
|
|
81
|
+
"content": "Don't catch errors as 'any'. Use 'catch (e: unknown)' and narrow with instanceof Error. In TypeScript strict mode, caught values are 'unknown' by default.",
|
|
82
|
+
"domains": ["typescript"],
|
|
83
|
+
"severity": "medium",
|
|
84
|
+
"tags": ["error-handling"]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "semantic",
|
|
88
|
+
"content": "Use discriminated unions for state management: type State = { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error }. Switch on the discriminant for exhaustive handling.",
|
|
89
|
+
"domains": ["typescript"],
|
|
90
|
+
"tags": ["patterns"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"type": "antipattern",
|
|
94
|
+
"content": "Don't use 'any' in generic constraints. Use 'unknown' instead: function foo<T extends unknown>() not function foo<T extends any>(). 'unknown' is the safe top type.",
|
|
95
|
+
"domains": ["typescript"],
|
|
96
|
+
"severity": "medium",
|
|
97
|
+
"tags": ["type-safety"]
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"type": "semantic",
|
|
101
|
+
"content": "For Node.js TypeScript projects: set 'module': 'node16' or 'nodenext' in tsconfig, not 'commonjs' or 'esnext'. This enables proper ESM/CJS interop resolution.",
|
|
102
|
+
"domains": ["typescript", "node"],
|
|
103
|
+
"tags": ["config"]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|