@pcircle/memesh 2.9.0 → 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/dist/mcp/ToolDefinitions.d.ts.map +1 -1
- package/dist/mcp/ToolDefinitions.js +0 -104
- package/dist/mcp/ToolDefinitions.js.map +1 -1
- package/package.json +2 -1
- package/plugin.json +1 -1
- 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 +15 -7
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SubagentStop Hook - Capture Subagent Results to MeMesh Knowledge Graph
|
|
5
|
+
*
|
|
6
|
+
* Triggered when a subagent finishes execution.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Saves code review findings to MeMesh KG (high-value results)
|
|
10
|
+
* - Tracks code review completion for pre-commit enforcement
|
|
11
|
+
* - Updates session state with subagent activity
|
|
12
|
+
* - Silent operation (no console output)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
STATE_DIR,
|
|
17
|
+
MEMESH_DB_PATH,
|
|
18
|
+
readJSONFile,
|
|
19
|
+
writeJSONFile,
|
|
20
|
+
sqliteBatchEntity,
|
|
21
|
+
getDateString,
|
|
22
|
+
readStdin,
|
|
23
|
+
logError,
|
|
24
|
+
logMemorySave,
|
|
25
|
+
} from './hook-utils.js';
|
|
26
|
+
import fs from 'fs';
|
|
27
|
+
import path from 'path';
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Constants
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
const CURRENT_SESSION_FILE = path.join(STATE_DIR, 'current-session.json');
|
|
34
|
+
|
|
35
|
+
/** Agent types that count as code review */
|
|
36
|
+
const CODE_REVIEWER_TYPES = [
|
|
37
|
+
'code-reviewer',
|
|
38
|
+
'code-review',
|
|
39
|
+
'superpowers:code-reviewer',
|
|
40
|
+
'pr-review-toolkit:code-reviewer',
|
|
41
|
+
'feature-dev:code-reviewer',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Code Review Detection
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if this subagent is a code reviewer
|
|
50
|
+
* @param {string} agentType - Subagent type identifier
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
function isCodeReviewer(agentType) {
|
|
54
|
+
if (!agentType) return false;
|
|
55
|
+
const lower = agentType.toLowerCase();
|
|
56
|
+
return CODE_REVIEWER_TYPES.some(t => lower.includes(t.toLowerCase()));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Mark code review as done in session state.
|
|
61
|
+
* This flag is checked by pre-tool-use.js before git commits.
|
|
62
|
+
*/
|
|
63
|
+
function markCodeReviewDone() {
|
|
64
|
+
const session = readJSONFile(CURRENT_SESSION_FILE, {});
|
|
65
|
+
session.codeReviewDone = true;
|
|
66
|
+
session.codeReviewTimestamp = new Date().toISOString();
|
|
67
|
+
writeJSONFile(CURRENT_SESSION_FILE, session);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// MeMesh KG Save
|
|
72
|
+
// ============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Save code review results to MeMesh knowledge graph.
|
|
76
|
+
* Only saves code reviewer subagent results (high-value findings).
|
|
77
|
+
*
|
|
78
|
+
* @param {string} agentType - Subagent type
|
|
79
|
+
* @param {string} lastMessage - Agent's final response
|
|
80
|
+
* @returns {boolean} True if saved
|
|
81
|
+
*/
|
|
82
|
+
function saveSubagentToKG(agentType, lastMessage) {
|
|
83
|
+
try {
|
|
84
|
+
if (!fs.existsSync(MEMESH_DB_PATH)) return false;
|
|
85
|
+
if (!lastMessage || lastMessage.length < 50) return false;
|
|
86
|
+
|
|
87
|
+
// Truncate very long messages
|
|
88
|
+
const shortMessage = lastMessage.length > 1000
|
|
89
|
+
? lastMessage.substring(0, 1000) + '...'
|
|
90
|
+
: lastMessage;
|
|
91
|
+
|
|
92
|
+
const entityName = `Code Review: ${getDateString()} ${Date.now()} ${agentType}`;
|
|
93
|
+
const metadata = JSON.stringify({
|
|
94
|
+
agentType,
|
|
95
|
+
messageLength: lastMessage.length,
|
|
96
|
+
source: 'subagent-stop-hook',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const tags = [
|
|
100
|
+
'code-review',
|
|
101
|
+
`agent:${agentType}`,
|
|
102
|
+
`date:${getDateString()}`,
|
|
103
|
+
'auto-tracked',
|
|
104
|
+
'scope:project',
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const entityId = sqliteBatchEntity(
|
|
108
|
+
MEMESH_DB_PATH,
|
|
109
|
+
{ name: entityName, type: 'code_review', metadata },
|
|
110
|
+
[`[${agentType}] ${shortMessage}`],
|
|
111
|
+
tags
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (entityId === null) return false;
|
|
115
|
+
|
|
116
|
+
logMemorySave(`Code review saved: ${agentType} (${lastMessage.length} chars)`);
|
|
117
|
+
return true;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
logError('saveSubagentToKG', error);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Session State Update
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Track subagent activity in session state
|
|
130
|
+
* @param {string} agentType - Subagent type
|
|
131
|
+
*/
|
|
132
|
+
function trackSubagentInSession(agentType) {
|
|
133
|
+
const session = readJSONFile(CURRENT_SESSION_FILE, { subagents: [] });
|
|
134
|
+
if (!session.subagents) session.subagents = [];
|
|
135
|
+
|
|
136
|
+
session.subagents.push({
|
|
137
|
+
type: agentType,
|
|
138
|
+
completedAt: new Date().toISOString(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Keep only last 20 subagent entries
|
|
142
|
+
if (session.subagents.length > 20) {
|
|
143
|
+
session.subagents = session.subagents.slice(-20);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
writeJSONFile(CURRENT_SESSION_FILE, session);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Main
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
async function subagentStop() {
|
|
154
|
+
try {
|
|
155
|
+
const input = await readStdin(3000);
|
|
156
|
+
if (!input || input.trim() === '') {
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const data = JSON.parse(input);
|
|
161
|
+
const agentType = data.agent_type || data.agentType || 'unknown';
|
|
162
|
+
const lastMessage = data.last_assistant_message || data.lastAssistantMessage || '';
|
|
163
|
+
|
|
164
|
+
// Track all subagent completions in session state
|
|
165
|
+
trackSubagentInSession(agentType);
|
|
166
|
+
|
|
167
|
+
// Track code review completion (for pre-commit enforcement)
|
|
168
|
+
if (isCodeReviewer(agentType)) {
|
|
169
|
+
markCodeReviewDone();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Save code reviewer results to MeMesh KG (high-value findings)
|
|
173
|
+
if (isCodeReviewer(agentType) && lastMessage.length > 50) {
|
|
174
|
+
saveSubagentToKG(agentType, lastMessage);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
process.exit(0);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
logError('SubagentStop', error);
|
|
180
|
+
process.exit(0); // Never block on hook errors
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
subagentStop();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
## Required Plan Sections
|
|
2
|
+
|
|
3
|
+
Your plan MUST include all of the following sections. Incomplete plans will be rejected.
|
|
4
|
+
|
|
5
|
+
### 1. System Design Description (SDD)
|
|
6
|
+
- Component architecture and responsibilities
|
|
7
|
+
- Data flow between components
|
|
8
|
+
- Interface contracts (input/output types)
|
|
9
|
+
- Dependencies and integration points
|
|
10
|
+
|
|
11
|
+
### 2. Behavior-Driven Design (BDD)
|
|
12
|
+
For EACH feature, write Gherkin scenarios:
|
|
13
|
+
|
|
14
|
+
```gherkin
|
|
15
|
+
Scenario: [descriptive name]
|
|
16
|
+
Given [precondition]
|
|
17
|
+
When [action]
|
|
18
|
+
Then [expected outcome]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Cover: happy path, error path, edge cases.
|
|
22
|
+
|
|
23
|
+
### 3. Edge Case Handling
|
|
24
|
+
|
|
25
|
+
| Edge Case | How Handled | Test Coverage |
|
|
26
|
+
|-----------|------------|---------------|
|
|
27
|
+
| [case] | [strategy] | [yes/no] |
|
|
28
|
+
|
|
29
|
+
Include at minimum: empty input, null/undefined, boundary values, concurrent access, timeout, large data.
|
|
30
|
+
|
|
31
|
+
### 4. Dry-Run Test Plan
|
|
32
|
+
Before dispatching heavy tasks, verify:
|
|
33
|
+
- [ ] Core function compiles (`tsc --noEmit` or `node --check`)
|
|
34
|
+
- [ ] Unit test for critical path passes
|
|
35
|
+
- [ ] Integration point verified with real call
|
|
36
|
+
- [ ] No regressions in existing tests
|
|
37
|
+
|
|
38
|
+
### 5. Risk Assessment
|
|
39
|
+
|
|
40
|
+
| Risk | Probability | Impact | Mitigation |
|
|
41
|
+
|------|------------|--------|------------|
|
|
42
|
+
| [risk] | High/Med/Low | High/Med/Low | [strategy] |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
**IMPORTANT**: After completing this plan, present it to the user and wait for explicit approval before proceeding to implementation. Do NOT auto-execute.
|
|
@@ -203,31 +203,35 @@ export async function ensurePluginEnabled(claudeDir = join(homedir(), '.claude')
|
|
|
203
203
|
*/
|
|
204
204
|
export async function ensureMCPConfigured(installPath, mode, claudeDir = join(homedir(), '.claude')) {
|
|
205
205
|
const mcpSettingsFile = join(claudeDir, 'mcp_settings.json');
|
|
206
|
+
|
|
206
207
|
// Read existing config or create new
|
|
207
208
|
let config = readJSONFile(mcpSettingsFile) || { mcpServers: {} };
|
|
208
209
|
if (!config.mcpServers) {
|
|
209
210
|
config.mcpServers = {};
|
|
210
211
|
}
|
|
212
|
+
|
|
211
213
|
// Configure memesh entry based on mode
|
|
212
214
|
if (mode === 'global') {
|
|
215
|
+
// Global install: use npx (always uses latest published version)
|
|
213
216
|
config.mcpServers.memesh = {
|
|
214
217
|
command: 'npx',
|
|
215
218
|
args: ['-y', '@pcircle/memesh'],
|
|
216
219
|
env: { NODE_ENV: 'production' }
|
|
217
220
|
};
|
|
218
|
-
}
|
|
219
|
-
|
|
221
|
+
} else {
|
|
222
|
+
// Local dev: use node + absolute path (for testing)
|
|
220
223
|
const serverPath = join(installPath, 'dist', 'mcp', 'server-bootstrap.js');
|
|
221
224
|
config.mcpServers.memesh = {
|
|
222
225
|
command: 'node',
|
|
223
|
-
args: [serverPath]
|
|
224
|
-
env: { NODE_ENV: 'production' }
|
|
226
|
+
args: [serverPath]
|
|
225
227
|
};
|
|
226
228
|
}
|
|
229
|
+
|
|
227
230
|
// Remove legacy claude-code-buddy entry if exists
|
|
228
231
|
if (config.mcpServers['claude-code-buddy']) {
|
|
229
232
|
delete config.mcpServers['claude-code-buddy'];
|
|
230
233
|
}
|
|
234
|
+
|
|
231
235
|
// Write back
|
|
232
236
|
writeJSONFile(mcpSettingsFile, config);
|
|
233
237
|
}
|
|
@@ -173,13 +173,21 @@ async function main() {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// Step 5: MCP Configuration
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
console.log(chalk.
|
|
180
|
-
|
|
181
|
-
results.
|
|
182
|
-
|
|
176
|
+
// IMPORTANT: Skip MCP config for local dev to prevent path pollution
|
|
177
|
+
// (see v2.9.0 Issue 2: Local Development Path Pollution)
|
|
178
|
+
if (mode === 'local') {
|
|
179
|
+
console.log(chalk.yellow(' ⚠️ Skipping MCP configuration (local development mode)'));
|
|
180
|
+
console.log(chalk.gray(' This prevents writing local dev paths to ~/.claude/mcp_settings.json'));
|
|
181
|
+
results.mcpConfigured = false;
|
|
182
|
+
} else {
|
|
183
|
+
try {
|
|
184
|
+
await ensureMCPConfigured(installPath, mode, claudeDir);
|
|
185
|
+
results.mcpConfigured = true;
|
|
186
|
+
console.log(chalk.green(' ✅ MCP configured'));
|
|
187
|
+
} catch (error) {
|
|
188
|
+
results.errors.push(`MCP: ${error.message}`);
|
|
189
|
+
console.log(chalk.yellow(` ⚠️ MCP configuration failed (non-fatal)`));
|
|
190
|
+
}
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
// Step 6: Legacy Installation Fix
|