@pcircle/memesh 2.8.11 → 2.9.1
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/LICENSE +21 -661
- package/README.de.md +171 -0
- package/README.es.md +171 -0
- package/README.fr.md +171 -0
- package/README.id.md +171 -0
- package/README.ja.md +171 -0
- package/README.ko.md +171 -0
- package/README.md +73 -100
- package/README.th.md +171 -0
- package/README.vi.md +171 -0
- package/README.zh-CN.md +171 -0
- package/README.zh-TW.md +71 -98
- package/dist/knowledge-graph/index.d.ts +22 -1
- package/dist/knowledge-graph/index.d.ts.map +1 -1
- package/dist/knowledge-graph/index.js +144 -3
- package/dist/knowledge-graph/index.js.map +1 -1
- package/dist/mcp/ServerInitializer.d.ts.map +1 -1
- package/dist/mcp/ServerInitializer.js +1 -1
- package/dist/mcp/ServerInitializer.js.map +1 -1
- package/dist/mcp/ToolDefinitions.d.ts.map +1 -1
- package/dist/mcp/ToolDefinitions.js +47 -55
- package/dist/mcp/ToolDefinitions.js.map +1 -1
- package/dist/mcp/ToolRouter.d.ts.map +1 -1
- package/dist/mcp/ToolRouter.js +4 -4
- package/dist/mcp/ToolRouter.js.map +1 -1
- package/dist/mcp/daemon/StdioProxyClient.d.ts.map +1 -1
- package/dist/mcp/daemon/StdioProxyClient.js +9 -1
- package/dist/mcp/daemon/StdioProxyClient.js.map +1 -1
- package/dist/mcp/handlers/BuddyHandlers.d.ts +3 -1
- package/dist/mcp/handlers/BuddyHandlers.d.ts.map +1 -1
- package/dist/mcp/handlers/BuddyHandlers.js +6 -5
- package/dist/mcp/handlers/BuddyHandlers.js.map +1 -1
- package/dist/mcp/handlers/ToolHandlers.d.ts.map +1 -1
- package/dist/mcp/handlers/ToolHandlers.js +1 -2
- package/dist/mcp/handlers/ToolHandlers.js.map +1 -1
- package/dist/mcp/resources/quick-reference.md +1 -1
- package/dist/mcp/schemas/OutputSchemas.d.ts +116 -53
- package/dist/mcp/schemas/OutputSchemas.d.ts.map +1 -1
- package/dist/mcp/schemas/OutputSchemas.js +64 -26
- package/dist/mcp/schemas/OutputSchemas.js.map +1 -1
- package/dist/mcp/server-bootstrap.js +89 -9
- package/dist/mcp/server-bootstrap.js.map +1 -1
- package/dist/mcp/tools/buddy-do.d.ts +2 -1
- package/dist/mcp/tools/buddy-do.d.ts.map +1 -1
- package/dist/mcp/tools/buddy-do.js +91 -4
- package/dist/mcp/tools/buddy-do.js.map +1 -1
- package/dist/mcp/tools/buddy-remember.d.ts +0 -5
- package/dist/mcp/tools/buddy-remember.d.ts.map +1 -1
- package/dist/mcp/tools/buddy-remember.js.map +1 -1
- package/dist/mcp/tools/memesh-agent-register.d.ts +20 -0
- package/dist/mcp/tools/memesh-agent-register.d.ts.map +1 -0
- package/dist/mcp/tools/memesh-agent-register.js +80 -0
- package/dist/mcp/tools/memesh-agent-register.js.map +1 -0
- package/dist/mcp/tools/memesh-cloud-sync.js +27 -8
- package/dist/mcp/tools/memesh-cloud-sync.js.map +1 -1
- package/dist/mcp/tools/memesh-metrics.d.ts +13 -0
- package/dist/mcp/tools/memesh-metrics.d.ts.map +1 -0
- package/dist/mcp/tools/memesh-metrics.js +193 -0
- package/dist/mcp/tools/memesh-metrics.js.map +1 -0
- package/dist/memory/UnifiedMemoryStore.d.ts +1 -1
- package/dist/memory/UnifiedMemoryStore.d.ts.map +1 -1
- package/dist/memory/UnifiedMemoryStore.js +4 -3
- package/dist/memory/UnifiedMemoryStore.js.map +1 -1
- package/package.json +9 -12
- package/plugin.json +2 -2
- package/scripts/hooks/README.md +230 -0
- package/scripts/hooks/__tests__/hook-test-harness.js +218 -0
- package/scripts/hooks/__tests__/hooks.test.js +267 -0
- package/scripts/hooks/hook-utils.js +899 -0
- package/scripts/hooks/post-commit.js +307 -0
- package/scripts/hooks/post-tool-use.js +812 -0
- package/scripts/hooks/pre-tool-use.js +462 -0
- package/scripts/hooks/session-start.js +544 -0
- package/scripts/hooks/stop.js +673 -0
- package/scripts/hooks/subagent-stop.js +184 -0
- package/scripts/hooks/templates/planning-template.md +46 -0
- package/scripts/postinstall-lib.js +8 -4
- package/scripts/postinstall-new.js +110 -7
- package/scripts/skills/comprehensive-code-review/SKILL.md +276 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostCommit Hook - Auto-save commit context to MeMesh Knowledge Graph
|
|
5
|
+
*
|
|
6
|
+
* Registered as a PostToolUse hook. Detects git commit commands and
|
|
7
|
+
* saves the commit details (message, files changed, diff summary)
|
|
8
|
+
* to the MeMesh knowledge graph for future recall.
|
|
9
|
+
*
|
|
10
|
+
* Commit saving runs silently. Plan progress prints a timeline to stderr.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
MEMESH_DB_PATH,
|
|
15
|
+
readStdin,
|
|
16
|
+
sqliteBatchEntity,
|
|
17
|
+
getDateString,
|
|
18
|
+
logError,
|
|
19
|
+
logMemorySave,
|
|
20
|
+
queryActivePlans,
|
|
21
|
+
matchCommitToStep,
|
|
22
|
+
addObservation,
|
|
23
|
+
updateEntityMetadata,
|
|
24
|
+
updateEntityTag,
|
|
25
|
+
createRelation,
|
|
26
|
+
renderTimeline,
|
|
27
|
+
} from './hook-utils.js';
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
import { execFileSync } from 'child_process';
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Git Commit Detection
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if a Bash tool call is a git commit
|
|
37
|
+
* @param {Object} toolData - Normalized tool data
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function isGitCommit(toolData) {
|
|
41
|
+
if (toolData.toolName !== 'Bash') return false;
|
|
42
|
+
if (!toolData.success) return false;
|
|
43
|
+
|
|
44
|
+
const cmd = toolData.arguments?.command || '';
|
|
45
|
+
return /git\s+commit\s/.test(cmd) && !cmd.includes('--amend');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Git Info Extraction
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extract latest commit details using git CLI
|
|
54
|
+
* @returns {{ hash: string, subject: string, body: string, filesChanged: string[], diffStat: string } | null}
|
|
55
|
+
*/
|
|
56
|
+
function getLatestCommitInfo() {
|
|
57
|
+
try {
|
|
58
|
+
// Get commit hash and message
|
|
59
|
+
const logOutput = execFileSync('git', [
|
|
60
|
+
'log', '-1',
|
|
61
|
+
'--format=%H%n%s%n%b',
|
|
62
|
+
], { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
63
|
+
|
|
64
|
+
const lines = logOutput.split('\n');
|
|
65
|
+
const hash = lines[0] || '';
|
|
66
|
+
const subject = lines[1] || '';
|
|
67
|
+
const body = lines.slice(2).join('\n').trim();
|
|
68
|
+
|
|
69
|
+
// Get files changed
|
|
70
|
+
const diffNameOnly = execFileSync('git', [
|
|
71
|
+
'diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD',
|
|
72
|
+
], { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
73
|
+
|
|
74
|
+
const filesChanged = diffNameOnly ? diffNameOnly.split('\n').filter(Boolean) : [];
|
|
75
|
+
|
|
76
|
+
// Get diff stat (compact summary)
|
|
77
|
+
const diffStat = execFileSync('git', [
|
|
78
|
+
'diff-tree', '--no-commit-id', '--stat', 'HEAD',
|
|
79
|
+
], { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
80
|
+
|
|
81
|
+
return { hash, subject, body, filesChanged, diffStat };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logError('getLatestCommitInfo', error);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// MeMesh KG Save
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Save commit context to MeMesh knowledge graph.
|
|
94
|
+
* Uses sqliteBatchEntity for performance (3 spawns instead of 8+).
|
|
95
|
+
* @param {Object} commitInfo - Commit details
|
|
96
|
+
* @returns {boolean} True if saved
|
|
97
|
+
*/
|
|
98
|
+
function saveCommitToKG(commitInfo) {
|
|
99
|
+
try {
|
|
100
|
+
if (!fs.existsSync(MEMESH_DB_PATH)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { hash, subject, body, filesChanged, diffStat } = commitInfo;
|
|
105
|
+
const shortHash = hash.substring(0, 7);
|
|
106
|
+
const entityName = `Commit ${shortHash}: ${subject}`;
|
|
107
|
+
|
|
108
|
+
// Build observations
|
|
109
|
+
const observations = [];
|
|
110
|
+
observations.push(`Commit: ${shortHash} - ${subject}`);
|
|
111
|
+
|
|
112
|
+
if (body) {
|
|
113
|
+
observations.push(`Details: ${body.substring(0, 200)}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (filesChanged.length > 0) {
|
|
117
|
+
const fileList = filesChanged.length <= 10
|
|
118
|
+
? filesChanged.join(', ')
|
|
119
|
+
: `${filesChanged.slice(0, 10).join(', ')} (+${filesChanged.length - 10} more)`;
|
|
120
|
+
observations.push(`Files changed (${filesChanged.length}): ${fileList}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Group files by directory for context
|
|
124
|
+
if (filesChanged.length > 0) {
|
|
125
|
+
const dirs = {};
|
|
126
|
+
filesChanged.forEach(f => {
|
|
127
|
+
const dir = f.split('/').slice(0, 2).join('/');
|
|
128
|
+
dirs[dir] = (dirs[dir] || 0) + 1;
|
|
129
|
+
});
|
|
130
|
+
const areaSummary = Object.entries(dirs)
|
|
131
|
+
.sort((a, b) => b[1] - a[1])
|
|
132
|
+
.slice(0, 5)
|
|
133
|
+
.map(([dir, count]) => `${dir} (${count})`)
|
|
134
|
+
.join(', ');
|
|
135
|
+
observations.push(`Areas: ${areaSummary}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (diffStat) {
|
|
139
|
+
const statLines = diffStat.split('\n');
|
|
140
|
+
const summaryLine = statLines[statLines.length - 1]?.trim();
|
|
141
|
+
if (summaryLine) {
|
|
142
|
+
observations.push(`Stats: ${summaryLine}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Batch: entity + observations + tags in 2 process spawns (was 8+)
|
|
147
|
+
const metadata = JSON.stringify({
|
|
148
|
+
hash: shortHash,
|
|
149
|
+
fullHash: hash,
|
|
150
|
+
filesCount: filesChanged.length,
|
|
151
|
+
source: 'post-commit-hook',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const tags = ['commit', 'auto-tracked', `date:${getDateString()}`, 'scope:project'];
|
|
155
|
+
|
|
156
|
+
const entityId = sqliteBatchEntity(
|
|
157
|
+
MEMESH_DB_PATH,
|
|
158
|
+
{ name: entityName, type: 'commit', metadata },
|
|
159
|
+
observations,
|
|
160
|
+
tags
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (entityId === null) return false;
|
|
164
|
+
|
|
165
|
+
logMemorySave(`Commit saved: ${shortHash} - ${subject} (${filesChanged.length} files)`);
|
|
166
|
+
return true;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
logError('saveCommitToKG', error);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Plan Progress Tracking (Beta)
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Match commit to active plan steps and update progress.
|
|
179
|
+
* Prints Style B timeline to stderr (visible to user).
|
|
180
|
+
* @param {Object} commitInfo - Commit details from git
|
|
181
|
+
* @param {boolean} commitEntitySaved - Whether commit entity was saved to KG
|
|
182
|
+
*/
|
|
183
|
+
function updatePlanProgress(commitInfo, commitEntitySaved = false) {
|
|
184
|
+
try {
|
|
185
|
+
const activePlans = queryActivePlans(MEMESH_DB_PATH);
|
|
186
|
+
if (activePlans.length === 0) return;
|
|
187
|
+
|
|
188
|
+
const { hash, subject, filesChanged } = commitInfo;
|
|
189
|
+
const shortHash = hash.substring(0, 7);
|
|
190
|
+
const commitEntityName = `Commit ${shortHash}: ${subject}`;
|
|
191
|
+
|
|
192
|
+
for (const plan of activePlans) {
|
|
193
|
+
const stepsDetail = plan.metadata?.stepsDetail;
|
|
194
|
+
if (!stepsDetail || stepsDetail.length === 0) continue;
|
|
195
|
+
|
|
196
|
+
const matchResult = matchCommitToStep(
|
|
197
|
+
{ subject, filesChanged },
|
|
198
|
+
stepsDetail
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (!matchResult) continue;
|
|
202
|
+
|
|
203
|
+
const { step: matched, confidence } = matchResult;
|
|
204
|
+
|
|
205
|
+
// Update step as completed
|
|
206
|
+
const updatedSteps = stepsDetail.map(s =>
|
|
207
|
+
s.number === matched.number
|
|
208
|
+
? { ...s, completed: true, commitHash: shortHash, date: getDateString(), confidence }
|
|
209
|
+
: s
|
|
210
|
+
);
|
|
211
|
+
const rawCompleted = updatedSteps.filter(s => s.completed).length;
|
|
212
|
+
const newCompleted = Math.min(rawCompleted, plan.metadata.totalSteps);
|
|
213
|
+
|
|
214
|
+
// Update entity metadata
|
|
215
|
+
updateEntityMetadata(MEMESH_DB_PATH, plan.name, {
|
|
216
|
+
...plan.metadata,
|
|
217
|
+
completed: newCompleted,
|
|
218
|
+
stepsDetail: updatedSteps,
|
|
219
|
+
status: newCompleted >= plan.metadata.totalSteps ? 'completed' : 'active',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Add completion observation
|
|
223
|
+
addObservation(MEMESH_DB_PATH, plan.name,
|
|
224
|
+
`\u2705 Step ${matched.number} completed by ${shortHash} (${getDateString()})`
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Create relation: commit → plan (only if commit entity exists in KG)
|
|
228
|
+
if (commitEntitySaved) {
|
|
229
|
+
createRelation(MEMESH_DB_PATH, commitEntityName, plan.name, 'depends_on');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// If all steps completed, swap tag and create lesson_learned
|
|
233
|
+
if (newCompleted === plan.metadata.totalSteps) {
|
|
234
|
+
updateEntityTag(MEMESH_DB_PATH, plan.name, 'active', 'completed');
|
|
235
|
+
addObservation(MEMESH_DB_PATH, plan.name,
|
|
236
|
+
`\ud83c\udf89 Plan completed on ${getDateString()}`
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// Auto-create lesson_learned entity for the completed plan
|
|
240
|
+
const planName = plan.name.replace('Plan: ', '');
|
|
241
|
+
const completedSteps = updatedSteps.filter(s => s.completed);
|
|
242
|
+
const lessonObs = [
|
|
243
|
+
`Plan "${planName}" completed (${plan.metadata.totalSteps} steps)`,
|
|
244
|
+
`Steps: ${completedSteps.map(s => s.description).join(', ')}`,
|
|
245
|
+
`Commits: ${completedSteps.filter(s => s.commitHash).map(s => s.commitHash).join(', ')}`,
|
|
246
|
+
];
|
|
247
|
+
sqliteBatchEntity(MEMESH_DB_PATH,
|
|
248
|
+
{ name: `Lesson: ${planName} completed`, type: 'lesson_learned', metadata: '{}' },
|
|
249
|
+
lessonObs,
|
|
250
|
+
['lesson', 'plan-completion', `plan:${planName}`, 'scope:project']
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Print timeline to stderr (visible to user — stdout goes to transcript)
|
|
255
|
+
const timelinePlan = {
|
|
256
|
+
...plan,
|
|
257
|
+
metadata: { ...plan.metadata, completed: newCompleted, stepsDetail: updatedSteps },
|
|
258
|
+
_lastCommit: shortHash,
|
|
259
|
+
_matchConfidence: confidence,
|
|
260
|
+
};
|
|
261
|
+
console.error('\n' + renderTimeline(timelinePlan, matched.number) + '\n');
|
|
262
|
+
|
|
263
|
+
logMemorySave(`Plan progress: ${plan.name} ${newCompleted}/${plan.metadata.totalSteps}`);
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
logError('updatePlanProgress', error);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ============================================================================
|
|
271
|
+
// Main
|
|
272
|
+
// ============================================================================
|
|
273
|
+
|
|
274
|
+
async function postCommit() {
|
|
275
|
+
try {
|
|
276
|
+
const input = await readStdin(3000);
|
|
277
|
+
if (!input || input.trim() === '') {
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const rawData = JSON.parse(input);
|
|
282
|
+
const toolData = {
|
|
283
|
+
toolName: rawData.tool_name || rawData.toolName || 'unknown',
|
|
284
|
+
arguments: rawData.tool_input || rawData.arguments || {},
|
|
285
|
+
success: rawData.success !== false,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Only act on successful git commits
|
|
289
|
+
if (!isGitCommit(toolData)) {
|
|
290
|
+
process.exit(0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Extract commit info and save
|
|
294
|
+
const commitInfo = getLatestCommitInfo();
|
|
295
|
+
if (commitInfo) {
|
|
296
|
+
const saved = saveCommitToKG(commitInfo);
|
|
297
|
+
updatePlanProgress(commitInfo, saved);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
process.exit(0);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
logError('PostCommit', error);
|
|
303
|
+
process.exit(0); // Never block Claude Code on hook errors
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
postCommit();
|