@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,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
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
import { dirname, join } from 'path';
|
|
18
18
|
import { homedir } from 'os';
|
|
19
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync } from 'fs';
|
|
19
20
|
import chalk from 'chalk';
|
|
20
21
|
import boxen from 'boxen';
|
|
21
22
|
|
|
@@ -34,6 +35,81 @@ import {
|
|
|
34
35
|
detectAndFixLegacyInstall
|
|
35
36
|
} from './postinstall-lib.js';
|
|
36
37
|
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Bundled Skills Installation
|
|
40
|
+
// ============================================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Install bundled skills to ~/.claude/skills/sa:<name>/
|
|
44
|
+
* Skills are shipped in scripts/skills/<name>/SKILL.md
|
|
45
|
+
* Only installs if skill doesn't exist or bundled version is newer.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} claudeDir - Path to ~/.claude
|
|
48
|
+
* @returns {{ installed: string[], skipped: string[], errors: string[] }}
|
|
49
|
+
*/
|
|
50
|
+
function installBundledSkills(claudeDir) {
|
|
51
|
+
const result = { installed: [], skipped: [], errors: [] };
|
|
52
|
+
const bundledDir = join(__dirname, 'skills');
|
|
53
|
+
const skillsDir = join(claudeDir, 'skills');
|
|
54
|
+
|
|
55
|
+
if (!existsSync(bundledDir)) {
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Ensure skills directory exists
|
|
60
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
61
|
+
|
|
62
|
+
// Read all bundled skills
|
|
63
|
+
let entries;
|
|
64
|
+
try {
|
|
65
|
+
entries = readdirSync(bundledDir, { withFileTypes: true });
|
|
66
|
+
} catch {
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (!entry.isDirectory()) continue;
|
|
72
|
+
|
|
73
|
+
const skillName = entry.name;
|
|
74
|
+
const prefixedName = `sa:${skillName}`;
|
|
75
|
+
const sourceFile = join(bundledDir, skillName, 'SKILL.md');
|
|
76
|
+
const targetDir = join(skillsDir, prefixedName);
|
|
77
|
+
const targetFile = join(targetDir, 'SKILL.md');
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Read source first — if it doesn't exist, skip (no TOCTOU with existsSync)
|
|
81
|
+
let content;
|
|
82
|
+
let sourceStat;
|
|
83
|
+
try {
|
|
84
|
+
content = readFileSync(sourceFile, 'utf-8');
|
|
85
|
+
sourceStat = statSync(sourceFile);
|
|
86
|
+
} catch {
|
|
87
|
+
continue; // source doesn't exist, skip
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Skip if target exists and is newer than source
|
|
91
|
+
try {
|
|
92
|
+
const targetStat = statSync(targetFile);
|
|
93
|
+
if (targetStat.mtimeMs >= sourceStat.mtimeMs) {
|
|
94
|
+
result.skipped.push(skillName);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
// target doesn't exist yet, proceed to install
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Install: create dir + write SKILL.md
|
|
102
|
+
mkdirSync(targetDir, { recursive: true });
|
|
103
|
+
writeFileSync(targetFile, content, 'utf-8');
|
|
104
|
+
result.installed.push(skillName);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
result.errors.push(`${skillName}: ${error.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
37
113
|
// ============================================================================
|
|
38
114
|
// Main Installation Flow
|
|
39
115
|
// ============================================================================
|
|
@@ -97,13 +173,21 @@ async function main() {
|
|
|
97
173
|
}
|
|
98
174
|
|
|
99
175
|
// Step 5: MCP Configuration
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
console.log(chalk.
|
|
104
|
-
|
|
105
|
-
results.
|
|
106
|
-
|
|
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
|
+
}
|
|
107
191
|
}
|
|
108
192
|
|
|
109
193
|
// Step 6: Legacy Installation Fix
|
|
@@ -120,6 +204,25 @@ async function main() {
|
|
|
120
204
|
console.log(chalk.dim(' ℹ️ Legacy check skipped'));
|
|
121
205
|
}
|
|
122
206
|
|
|
207
|
+
// Step 7: Install Bundled Skills
|
|
208
|
+
try {
|
|
209
|
+
const skillResult = installBundledSkills(claudeDir);
|
|
210
|
+
results.skillsInstalled = skillResult.installed;
|
|
211
|
+
results.skillsSkipped = skillResult.skipped;
|
|
212
|
+
|
|
213
|
+
if (skillResult.installed.length > 0) {
|
|
214
|
+
console.log(chalk.green(` ✅ Skills installed: ${skillResult.installed.join(', ')}`));
|
|
215
|
+
} else if (skillResult.skipped.length > 0) {
|
|
216
|
+
console.log(chalk.dim(` ℹ️ Skills up to date (${skillResult.skipped.length} already installed)`));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (skillResult.errors.length > 0) {
|
|
220
|
+
results.errors.push(`Skills: ${skillResult.errors.join('; ')}`);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.log(chalk.dim(' ℹ️ Skills installation skipped'));
|
|
224
|
+
}
|
|
225
|
+
|
|
123
226
|
} catch (error) {
|
|
124
227
|
console.error(chalk.red('\n❌ Installation failed:'), error.message);
|
|
125
228
|
console.error(chalk.yellow('\n💡 You can configure manually (see instructions below)\n'));
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: comprehensive-code-review
|
|
3
|
+
description: |
|
|
4
|
+
Complete code review framework with mandatory tool-verified checks and anti-hallucination enforcement.
|
|
5
|
+
Use when: performing code reviews, reviewing PRs, checking code quality before merge.
|
|
6
|
+
Keywords: code review, review code, 代碼審查, PR review, pull request, 檢查代碼.
|
|
7
|
+
Auto-triggers on: "code review", "review the code", "run code review".
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Comprehensive Code Review Framework v4.0
|
|
11
|
+
|
|
12
|
+
**Philosophy**: 不信任自己。用工具證明一切。沒有 evidence 的 check 等於沒做。
|
|
13
|
+
|
|
14
|
+
**v4.0 vs v3.0**: v3.0 說了「應該檢查什麼」但沒強制怎麼做。v4.0 的每一步都是**必須用工具執行並產出 evidence** 的指令,不是建議。
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🔴 Step 0: Ripple Map(強制首步,不可跳過)
|
|
19
|
+
|
|
20
|
+
**PURPOSE**: 在做任何 review 之前,先找出所有「改了 A 就必須改 B」的連鎖反應。
|
|
21
|
+
|
|
22
|
+
**這是 v4.0 最重要的改變。** 之前的 review 只看「已改的檔案寫得對不對」,從不問「還有哪些檔案該改但沒改」。
|
|
23
|
+
|
|
24
|
+
### 執行方式(必須用工具)
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
FOR EACH modified file:
|
|
28
|
+
1. 找出這個文件中被新增/修改的 export(type, function, constant)
|
|
29
|
+
2. Grep 整個 codebase 找出所有 import 這些 export 的文件
|
|
30
|
+
3. 這些文件是否也在本次修改範圍內?
|
|
31
|
+
- 是 → 後續 review 時檢查一致性
|
|
32
|
+
- 否 → 🔴 SUSPECT: 可能遺漏。立即 Read 該文件確認是否需要更新
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 必須執行的工具命令
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 1. 列出所有修改的文件
|
|
39
|
+
git diff --name-only HEAD # 或 review 指定的文件列表
|
|
40
|
+
|
|
41
|
+
# 2. 對每個被修改的 interface/type/function,搜尋所有引用
|
|
42
|
+
Grep: pattern="DeliverParams" # 每個被修改的型別名
|
|
43
|
+
Grep: pattern="deliverOrder" # 每個被修改的函數名
|
|
44
|
+
Grep: pattern="delivery_result" # 每個被新增的 DB column
|
|
45
|
+
|
|
46
|
+
# 3. 比對:引用這些東西的文件 vs 本次修改的文件
|
|
47
|
+
# 差集 = 可能遺漏的文件
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Ripple Map Output 格式(必須產出)
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
## Ripple Map
|
|
54
|
+
|
|
55
|
+
| Changed Symbol | Files That Reference It | In Scope? | Verified? |
|
|
56
|
+
|----------------|------------------------|-----------|-----------|
|
|
57
|
+
| `DeliverParams` | sdk/types.ts, sdk/client.ts, deliver/route.ts | ✅/❌ | Read 確認 |
|
|
58
|
+
| `deliverOrder()` | client.ts, sdk/client.ts, example/index.ts | ✅/❌ | Read 確認 |
|
|
59
|
+
| `delivery_result` | supabase.ts, api-utils.ts, deliver/route.ts | ✅/❌ | Read 確認 |
|
|
60
|
+
|
|
61
|
+
🔴 Unsynchronized: [列出所有引用但未修改的文件]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**如果 Ripple Map 發現 unsynchronized 文件,直接列為 CRITICAL issue,不需要進入後續維度。**
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Step 1: Scope Analysis
|
|
69
|
+
|
|
70
|
+
確認審查範圍:
|
|
71
|
+
- 哪些文件被修改?(用 git diff 或文件列表,不可憑記憶)
|
|
72
|
+
- 變更的目的是什麼?
|
|
73
|
+
- **有幾個 package/module 邊界被跨越?**
|
|
74
|
+
- **Ripple Map 發現多少個 unsynchronized 文件?**
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Step 2: Mandatory Verification Dimensions
|
|
79
|
+
|
|
80
|
+
**12 個維度。Dim 11 和 12 是最高優先級,必須用 Explore subagent 或多個 Read/Grep 並行執行。**
|
|
81
|
+
|
|
82
|
+
**每個 check 必須產出 evidence。格式:**
|
|
83
|
+
```
|
|
84
|
+
[DIM-11a] ✅ PASS — import { validateOutputSchema } from '@/lib/api-utils'
|
|
85
|
+
Evidence: Read api-utils.ts → line 118: export function validateOutputSchema(...)
|
|
86
|
+
|
|
87
|
+
[DIM-12d] 🔴 FAIL — SDK DeliverParams missing deliveryResult
|
|
88
|
+
Evidence: Read packages/sdk/src/types.ts:177-180 → only has deliveryHash, deliveryMetadata
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**沒有 evidence 的 ✅ 等於 ❌。**
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### 🔴 Dim 11: Reality Check(必須用 Explore subagent 執行)
|
|
96
|
+
|
|
97
|
+
**IMPORTANT: 這個維度必須 dispatch 至少一個 Explore subagent 來獨立驗證。不可自己看一眼就說 PASS。**
|
|
98
|
+
|
|
99
|
+
**Subagent prompt 模板**:
|
|
100
|
+
```
|
|
101
|
+
In [project_path], verify the following for files [list]:
|
|
102
|
+
1. Every import resolves to a real export (read the source file to confirm)
|
|
103
|
+
2. Every method call references a method that exists in the target class
|
|
104
|
+
3. Every DB column referenced exists in migrations
|
|
105
|
+
4. Search for TODO/FIXME/STUB in all modified files
|
|
106
|
+
5. For every new function: grep for callers — is it actually called from somewhere?
|
|
107
|
+
Report exact findings with file:line evidence for each.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**11a-11f 檢查項目**(subagent 必須全部執行):
|
|
111
|
+
|
|
112
|
+
| Check | Tool | What to verify |
|
|
113
|
+
|-------|------|----------------|
|
|
114
|
+
| 11a. Import/Export | Read 被 import 的文件 | export 真的存在 |
|
|
115
|
+
| 11b. Method calls | Read 被呼叫的 class | method 真的定義了 |
|
|
116
|
+
| 11c. DB schema | Read migration files | column 在 migration 裡 |
|
|
117
|
+
| 11d. Example code | Read constructor + method 簽名 | 參數和回傳值匹配 |
|
|
118
|
+
| 11e. Stubs | Grep TODO/FIXME/STUB/throw.*not.impl | 無假完成 |
|
|
119
|
+
| 11f. Dead wire | Grep function name across codebase | 有 caller 存在 |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### 🔴 Dim 12: Cross-boundary Sync(必須用 Explore subagent 執行)
|
|
124
|
+
|
|
125
|
+
**IMPORTANT: Monorepo 或多 package 專案時,此維度必須 dispatch 獨立 subagent。**
|
|
126
|
+
|
|
127
|
+
**Subagent prompt 模板**:
|
|
128
|
+
```
|
|
129
|
+
In [project_path], check cross-boundary type synchronization:
|
|
130
|
+
1. Compare [platform types file] with [SDK types file] — list every interface
|
|
131
|
+
that exists in both. For each, compare field-by-field.
|
|
132
|
+
2. Compare [platform client file] with [SDK client file] — list every method
|
|
133
|
+
that exists in both. For each, compare signature.
|
|
134
|
+
3. For every API route that accepts a body: read the route handler AND the SDK
|
|
135
|
+
method that calls it. Do the field names match?
|
|
136
|
+
4. Trace: migration column → DbType → mapResponse → API type → SDK type.
|
|
137
|
+
Report any broken links.
|
|
138
|
+
Report ALL mismatches with exact file:line evidence.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**12a-12d 檢查項目**(subagent 必須全部執行):
|
|
142
|
+
|
|
143
|
+
| Check | What | How |
|
|
144
|
+
|-------|------|-----|
|
|
145
|
+
| 12a. Type parity | 同名 interface 欄位一致 | Read 兩個 type 文件,逐欄比對 |
|
|
146
|
+
| 12b. Client parity | 同名 method 簽名一致 | Read 兩個 client 文件,逐方法比對 |
|
|
147
|
+
| 12c. Route↔SDK match | Route body 欄位 = SDK params 欄位 | Read route + SDK method |
|
|
148
|
+
| 12d. Full chain | migration→DbType→mapper→ApiType→SdkType | 追蹤新增欄位通過每一層 |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### Dim 1-10: Standard Quality Dimensions
|
|
153
|
+
|
|
154
|
+
#### 1. 🔒 Security
|
|
155
|
+
- Injection attacks (SQL, Command, Path traversal)
|
|
156
|
+
- Sensitive data exposure
|
|
157
|
+
- Auth/AuthZ gaps, hardcoded secrets
|
|
158
|
+
|
|
159
|
+
#### 2. 🔄 Concurrency
|
|
160
|
+
- Race conditions (TOCTOU), deadlocks
|
|
161
|
+
- Atomic operation needs
|
|
162
|
+
|
|
163
|
+
#### 3. 💾 Resource Management
|
|
164
|
+
- Resource leaks, unbounded buffers
|
|
165
|
+
- Timer/listener cleanup
|
|
166
|
+
|
|
167
|
+
#### 4. ❌ Error Handling
|
|
168
|
+
- Uncaught exceptions, empty catch
|
|
169
|
+
- Unhandled Promise rejections
|
|
170
|
+
|
|
171
|
+
#### 5. 📊 Edge Cases
|
|
172
|
+
- Empty/null/NaN/zero/MAX values
|
|
173
|
+
- First run / cold start
|
|
174
|
+
|
|
175
|
+
#### 6. ✅ Input Validation
|
|
176
|
+
- Type, range, format, length validation
|
|
177
|
+
|
|
178
|
+
#### 7. ⚡ Performance
|
|
179
|
+
- O(n²), blocking I/O, N+1 queries
|
|
180
|
+
|
|
181
|
+
#### 8. 📝 Code Quality
|
|
182
|
+
- Dead code, magic numbers, naming
|
|
183
|
+
|
|
184
|
+
#### 9. 📖 Documentation
|
|
185
|
+
- API docs, outdated comments
|
|
186
|
+
|
|
187
|
+
#### 10. 🧪 Test Coverage
|
|
188
|
+
- Happy/error/edge paths tested
|
|
189
|
+
- New functions have corresponding tests
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Step 3: Issue Classification
|
|
194
|
+
|
|
195
|
+
| Level | Symbol | Criteria | Action |
|
|
196
|
+
|-------|--------|----------|--------|
|
|
197
|
+
| CRITICAL | 🔴 | Security, crash, **hallucination, broken integration, missing sync** | Fix immediately |
|
|
198
|
+
| MAJOR | 🟠 | Bugs, leaks, performance, type mismatch | Fix before merge |
|
|
199
|
+
| MINOR | 🟡 | Quality, maintenance | Document |
|
|
200
|
+
|
|
201
|
+
**鐵則**: Dim 11/12 的 issue 最低 MAJOR,大部分 CRITICAL。
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Step 4: Fix Dispatch
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
Dispatch subagents in parallel (max 6):
|
|
209
|
+
- Group by file or logical module
|
|
210
|
+
- Each subagent: fix root cause → run related tests → validate no regression
|
|
211
|
+
- After all complete: run full test suite
|
|
212
|
+
- After full suite: re-run Ripple Map to verify no new gaps
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Step 5: Final Verification Gate
|
|
218
|
+
|
|
219
|
+
**在宣稱 review 完成前,必須全部通過:**
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
□ Ripple Map 無 unsynchronized 文件
|
|
223
|
+
□ Dim 11 所有 check 有 evidence 且 PASS
|
|
224
|
+
□ Dim 12 所有 check 有 evidence 且 PASS
|
|
225
|
+
□ 所有 CRITICAL issues 已修復
|
|
226
|
+
□ 所有 MAJOR issues 已修復
|
|
227
|
+
□ MINOR issues 已記錄
|
|
228
|
+
□ tsc --noEmit pass(主專案)
|
|
229
|
+
□ tsc --noEmit pass(每個子 package 獨立跑)
|
|
230
|
+
□ 全部測試 pass
|
|
231
|
+
□ Build pass
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**如果任何 check 缺少 evidence,整個 review 標記為 INCOMPLETE。**
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## AI 幻想模式速查表
|
|
239
|
+
|
|
240
|
+
| # | Pattern | 怎麼偵測 | 工具 |
|
|
241
|
+
|---|---------|---------|------|
|
|
242
|
+
| 1 | **Ghost Method** — 呼叫不存在的方法 | Read class file, search method | Read |
|
|
243
|
+
| 2 | **Phantom Import** — import 不存在的 export | Read source file | Read/Grep |
|
|
244
|
+
| 3 | **Schema Drift** — 讀寫不存在的 DB column | Read all migrations | Read |
|
|
245
|
+
| 4 | **Type Island** — 只改一邊的 type | Ripple Map 自動抓 | Grep |
|
|
246
|
+
| 5 | **Dead Wire** — 定義了但沒被呼叫 | Grep function name | Grep |
|
|
247
|
+
| 6 | **Stub Disguise** — TODO/return {} 偽裝完成 | Grep TODO/FIXME/STUB | Grep |
|
|
248
|
+
| 7 | **Constructor Lie** — 參數不匹配 | Read constructor | Read |
|
|
249
|
+
| 8 | **Mock Leak** — test mock 了但 prod 沒實作 | Check real implementation | Read |
|
|
250
|
+
| 9 | **One-side Fix** — 改了 A 忘改 B | Ripple Map 自動抓 | Grep |
|
|
251
|
+
| 10 | **Verify Theater** — tsc pass 但只跑主 package | 獨立跑每個子 package tsc | Bash |
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 何時執行
|
|
256
|
+
|
|
257
|
+
| 觸發條件 | 執行範圍 |
|
|
258
|
+
|---------|---------|
|
|
259
|
+
| 新功能 > 100 lines | Full review (all 12 dims) |
|
|
260
|
+
| API/DB schema 變更 | Full review + 強制 Dim 11/12 |
|
|
261
|
+
| 跨 package 變更 | Full review + 強制 Dim 12 |
|
|
262
|
+
| Bug fix < 50 lines | Quick review (Dim 11 + 改動相關維度) |
|
|
263
|
+
| 任何 monorepo 變更 | 至少 Ripple Map + Dim 12 |
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
**Version**: 4.0
|
|
268
|
+
**Last Updated**: 2026-02-22
|
|
269
|
+
**Changelog**:
|
|
270
|
+
- v4.0: 新增 Step 0 Ripple Map — 強制發現遺漏的連鎖修改
|
|
271
|
+
- v4.0: Dim 11/12 必須 dispatch Explore subagent 獨立驗證
|
|
272
|
+
- v4.0: 每個 check 必須產出 evidence,無 evidence = 未完成
|
|
273
|
+
- v4.0: 新增 Verify Theater pattern(tsc pass 但只跑主 package)
|
|
274
|
+
- v4.0: 新增 Final Verification Gate — 子 package 必須獨立跑 tsc
|
|
275
|
+
- v3.0: Added Dim 11/12, hallucination pattern table
|
|
276
|
+
- v2.0: Initial 10-dimension framework
|