@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.
Files changed (79) hide show
  1. package/LICENSE +21 -661
  2. package/README.de.md +171 -0
  3. package/README.es.md +171 -0
  4. package/README.fr.md +171 -0
  5. package/README.id.md +171 -0
  6. package/README.ja.md +171 -0
  7. package/README.ko.md +171 -0
  8. package/README.md +73 -100
  9. package/README.th.md +171 -0
  10. package/README.vi.md +171 -0
  11. package/README.zh-CN.md +171 -0
  12. package/README.zh-TW.md +71 -98
  13. package/dist/knowledge-graph/index.d.ts +22 -1
  14. package/dist/knowledge-graph/index.d.ts.map +1 -1
  15. package/dist/knowledge-graph/index.js +144 -3
  16. package/dist/knowledge-graph/index.js.map +1 -1
  17. package/dist/mcp/ServerInitializer.d.ts.map +1 -1
  18. package/dist/mcp/ServerInitializer.js +1 -1
  19. package/dist/mcp/ServerInitializer.js.map +1 -1
  20. package/dist/mcp/ToolDefinitions.d.ts.map +1 -1
  21. package/dist/mcp/ToolDefinitions.js +47 -55
  22. package/dist/mcp/ToolDefinitions.js.map +1 -1
  23. package/dist/mcp/ToolRouter.d.ts.map +1 -1
  24. package/dist/mcp/ToolRouter.js +4 -4
  25. package/dist/mcp/ToolRouter.js.map +1 -1
  26. package/dist/mcp/daemon/StdioProxyClient.d.ts.map +1 -1
  27. package/dist/mcp/daemon/StdioProxyClient.js +9 -1
  28. package/dist/mcp/daemon/StdioProxyClient.js.map +1 -1
  29. package/dist/mcp/handlers/BuddyHandlers.d.ts +3 -1
  30. package/dist/mcp/handlers/BuddyHandlers.d.ts.map +1 -1
  31. package/dist/mcp/handlers/BuddyHandlers.js +6 -5
  32. package/dist/mcp/handlers/BuddyHandlers.js.map +1 -1
  33. package/dist/mcp/handlers/ToolHandlers.d.ts.map +1 -1
  34. package/dist/mcp/handlers/ToolHandlers.js +1 -2
  35. package/dist/mcp/handlers/ToolHandlers.js.map +1 -1
  36. package/dist/mcp/resources/quick-reference.md +1 -1
  37. package/dist/mcp/schemas/OutputSchemas.d.ts +116 -53
  38. package/dist/mcp/schemas/OutputSchemas.d.ts.map +1 -1
  39. package/dist/mcp/schemas/OutputSchemas.js +64 -26
  40. package/dist/mcp/schemas/OutputSchemas.js.map +1 -1
  41. package/dist/mcp/server-bootstrap.js +89 -9
  42. package/dist/mcp/server-bootstrap.js.map +1 -1
  43. package/dist/mcp/tools/buddy-do.d.ts +2 -1
  44. package/dist/mcp/tools/buddy-do.d.ts.map +1 -1
  45. package/dist/mcp/tools/buddy-do.js +91 -4
  46. package/dist/mcp/tools/buddy-do.js.map +1 -1
  47. package/dist/mcp/tools/buddy-remember.d.ts +0 -5
  48. package/dist/mcp/tools/buddy-remember.d.ts.map +1 -1
  49. package/dist/mcp/tools/buddy-remember.js.map +1 -1
  50. package/dist/mcp/tools/memesh-agent-register.d.ts +20 -0
  51. package/dist/mcp/tools/memesh-agent-register.d.ts.map +1 -0
  52. package/dist/mcp/tools/memesh-agent-register.js +80 -0
  53. package/dist/mcp/tools/memesh-agent-register.js.map +1 -0
  54. package/dist/mcp/tools/memesh-cloud-sync.js +27 -8
  55. package/dist/mcp/tools/memesh-cloud-sync.js.map +1 -1
  56. package/dist/mcp/tools/memesh-metrics.d.ts +13 -0
  57. package/dist/mcp/tools/memesh-metrics.d.ts.map +1 -0
  58. package/dist/mcp/tools/memesh-metrics.js +193 -0
  59. package/dist/mcp/tools/memesh-metrics.js.map +1 -0
  60. package/dist/memory/UnifiedMemoryStore.d.ts +1 -1
  61. package/dist/memory/UnifiedMemoryStore.d.ts.map +1 -1
  62. package/dist/memory/UnifiedMemoryStore.js +4 -3
  63. package/dist/memory/UnifiedMemoryStore.js.map +1 -1
  64. package/package.json +9 -12
  65. package/plugin.json +2 -2
  66. package/scripts/hooks/README.md +230 -0
  67. package/scripts/hooks/__tests__/hook-test-harness.js +218 -0
  68. package/scripts/hooks/__tests__/hooks.test.js +267 -0
  69. package/scripts/hooks/hook-utils.js +899 -0
  70. package/scripts/hooks/post-commit.js +307 -0
  71. package/scripts/hooks/post-tool-use.js +812 -0
  72. package/scripts/hooks/pre-tool-use.js +462 -0
  73. package/scripts/hooks/session-start.js +544 -0
  74. package/scripts/hooks/stop.js +673 -0
  75. package/scripts/hooks/subagent-stop.js +184 -0
  76. package/scripts/hooks/templates/planning-template.md +46 -0
  77. package/scripts/postinstall-lib.js +8 -4
  78. package/scripts/postinstall-new.js +110 -7
  79. 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();