@pcircle/memesh 2.10.0 → 2.10.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pcircle/memesh",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.1",
|
|
4
4
|
"description": "MeMesh — Persistent memory plugin for Claude Code. Remembers architecture decisions, coding patterns, and project context across sessions.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/",
|
|
12
|
-
".claude-plugin/plugin.json",
|
|
13
12
|
".mcp.json",
|
|
14
13
|
"hooks/hooks.json",
|
|
15
|
-
"skills/",
|
|
16
14
|
"scripts/postinstall-new.js",
|
|
17
15
|
"scripts/postinstall-lib.js",
|
|
18
16
|
"scripts/health-check.js",
|
|
19
17
|
"scripts/hooks/",
|
|
18
|
+
"scripts/skills/",
|
|
20
19
|
"plugin.json",
|
|
21
20
|
"README.md",
|
|
22
21
|
"README.zh-TW.md",
|
package/plugin.json
CHANGED
package/scripts/health-check.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* MeMesh Plugin Health Check
|
|
5
5
|
*
|
|
6
6
|
* Fast, non-invasive validation of plugin installation.
|
|
7
|
-
*
|
|
7
|
+
* Supports both npm global install and local dev install.
|
|
8
8
|
*
|
|
9
9
|
* Exit codes:
|
|
10
10
|
* 0 - All healthy
|
|
@@ -26,19 +26,47 @@ const silent = process.argv.includes('--silent');
|
|
|
26
26
|
const verbose = process.argv.includes('--verbose');
|
|
27
27
|
const json = process.argv.includes('--json');
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Detect installation mode based on directory structure.
|
|
31
|
+
* - 'npm-global': installed via npm install -g (dist/ is in package root)
|
|
32
|
+
* - 'dev': running from project source (needs .claude-plugin/memesh/dist/)
|
|
33
|
+
* - 'plugin': installed via /plugin marketplace add (managed by Claude Code)
|
|
34
|
+
*/
|
|
35
|
+
function detectInstallMode() {
|
|
36
|
+
// If dist/mcp/server-bootstrap.js exists at package root, it's npm or plugin install
|
|
37
|
+
if (existsSync(join(projectRoot, 'dist', 'mcp', 'server-bootstrap.js'))) {
|
|
38
|
+
// Check if we're inside node_modules (npm global)
|
|
39
|
+
if (projectRoot.includes('node_modules')) {
|
|
40
|
+
return 'npm-global';
|
|
41
|
+
}
|
|
42
|
+
// Check if src/ exists (dev environment)
|
|
43
|
+
if (existsSync(join(projectRoot, 'src'))) {
|
|
44
|
+
return 'dev';
|
|
45
|
+
}
|
|
46
|
+
return 'plugin';
|
|
47
|
+
}
|
|
48
|
+
// Fallback: dev mode without build
|
|
49
|
+
return 'dev';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const installMode = detectInstallMode();
|
|
53
|
+
|
|
29
54
|
/**
|
|
30
55
|
* Health check result structure
|
|
31
56
|
*/
|
|
32
57
|
const result = {
|
|
33
58
|
healthy: true,
|
|
59
|
+
installMode,
|
|
34
60
|
issues: [],
|
|
35
61
|
timestamp: new Date().toISOString(),
|
|
36
62
|
checks: {
|
|
37
63
|
dist: false,
|
|
64
|
+
pluginJson: false,
|
|
65
|
+
mcpJson: false,
|
|
66
|
+
hooks: false,
|
|
38
67
|
marketplace: false,
|
|
39
68
|
symlink: false,
|
|
40
69
|
settings: false,
|
|
41
|
-
mcp: false
|
|
42
70
|
}
|
|
43
71
|
};
|
|
44
72
|
|
|
@@ -58,95 +86,181 @@ function addIssue(path, severity, message, repairable = true) {
|
|
|
58
86
|
* Log success message
|
|
59
87
|
*/
|
|
60
88
|
function logSuccess(message) {
|
|
61
|
-
if (!silent && !json
|
|
62
|
-
|
|
89
|
+
if (!silent && !json) {
|
|
90
|
+
if (verbose) {
|
|
91
|
+
console.log(` ✅ ${message}`);
|
|
92
|
+
}
|
|
63
93
|
}
|
|
64
94
|
}
|
|
65
95
|
|
|
66
96
|
// ============================================================================
|
|
67
|
-
//
|
|
97
|
+
// Start
|
|
68
98
|
// ============================================================================
|
|
69
99
|
|
|
70
|
-
if (!silent && !json)
|
|
100
|
+
if (!silent && !json) {
|
|
101
|
+
console.log('🔍 Checking MeMesh Plugin installation...\n');
|
|
102
|
+
console.log(` Mode: ${installMode}`);
|
|
103
|
+
console.log(` Path: ${projectRoot}\n`);
|
|
104
|
+
}
|
|
71
105
|
|
|
72
|
-
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Check 1: Server bootstrap exists
|
|
108
|
+
// ============================================================================
|
|
73
109
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
110
|
+
const serverPath = installMode === 'dev'
|
|
111
|
+
? join(projectRoot, '.claude-plugin', 'memesh', 'dist', 'mcp', 'server-bootstrap.js')
|
|
112
|
+
: join(projectRoot, 'dist', 'mcp', 'server-bootstrap.js');
|
|
113
|
+
|
|
114
|
+
if (!existsSync(serverPath)) {
|
|
115
|
+
const hint = installMode === 'dev' ? 'Run: npm run build' : 'Reinstall: npm install -g @pcircle/memesh';
|
|
116
|
+
addIssue('dist', 'error', `server-bootstrap.js not found at ${serverPath}`, false);
|
|
77
117
|
|
|
78
118
|
if (!silent && !json) {
|
|
79
|
-
console.error(
|
|
119
|
+
console.error(`\n❌ Plugin not built. ${hint}\n`);
|
|
80
120
|
}
|
|
81
121
|
|
|
82
122
|
if (json) {
|
|
83
123
|
console.log(JSON.stringify(result, null, 2));
|
|
84
124
|
}
|
|
85
125
|
|
|
86
|
-
process.exit(2);
|
|
126
|
+
process.exit(2);
|
|
87
127
|
} else {
|
|
88
128
|
result.checks.dist = true;
|
|
89
|
-
logSuccess('
|
|
129
|
+
logSuccess('server-bootstrap.js exists');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// Check 2: plugin.json exists and is valid
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
const pluginJsonPath = join(projectRoot, 'plugin.json');
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
if (!existsSync(pluginJsonPath)) {
|
|
140
|
+
addIssue('pluginJson', 'error', 'plugin.json not found');
|
|
141
|
+
} else {
|
|
142
|
+
const plugin = JSON.parse(readFileSync(pluginJsonPath, 'utf-8'));
|
|
143
|
+
if (!plugin.name || !plugin.version) {
|
|
144
|
+
addIssue('pluginJson', 'error', 'plugin.json missing name or version');
|
|
145
|
+
} else {
|
|
146
|
+
result.checks.pluginJson = true;
|
|
147
|
+
logSuccess(`plugin.json valid (${plugin.name} v${plugin.version})`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
addIssue('pluginJson', 'error', `Failed to parse plugin.json: ${error.message}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// Check 3: .mcp.json exists and is valid
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
const mcpJsonPath = join(projectRoot, '.mcp.json');
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
if (!existsSync(mcpJsonPath)) {
|
|
162
|
+
addIssue('mcpJson', 'error', '.mcp.json not found');
|
|
163
|
+
} else {
|
|
164
|
+
const mcp = JSON.parse(readFileSync(mcpJsonPath, 'utf-8'));
|
|
165
|
+
if (!mcp.mcpServers?.memesh) {
|
|
166
|
+
addIssue('mcpJson', 'error', '.mcp.json missing memesh server definition');
|
|
167
|
+
} else {
|
|
168
|
+
result.checks.mcpJson = true;
|
|
169
|
+
logSuccess('.mcp.json valid');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
addIssue('mcpJson', 'error', `Failed to parse .mcp.json: ${error.message}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Check 4: hooks/hooks.json exists and scripts are present
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
const hooksJsonPath = join(projectRoot, 'hooks', 'hooks.json');
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
if (!existsSync(hooksJsonPath)) {
|
|
184
|
+
addIssue('hooks', 'error', 'hooks/hooks.json not found');
|
|
185
|
+
} else {
|
|
186
|
+
const hooksConfig = JSON.parse(readFileSync(hooksJsonPath, 'utf-8'));
|
|
187
|
+
const events = Object.keys(hooksConfig.hooks || {});
|
|
188
|
+
|
|
189
|
+
if (events.length === 0) {
|
|
190
|
+
addIssue('hooks', 'warning', 'No hook events defined');
|
|
191
|
+
} else {
|
|
192
|
+
let allScriptsExist = true;
|
|
193
|
+
for (const event of events) {
|
|
194
|
+
for (const entry of hooksConfig.hooks[event]) {
|
|
195
|
+
for (const hook of entry.hooks) {
|
|
196
|
+
const scriptPath = hook.command.replace('${CLAUDE_PLUGIN_ROOT}', projectRoot);
|
|
197
|
+
if (!existsSync(scriptPath)) {
|
|
198
|
+
addIssue('hooks', 'error', `Hook script missing: ${scriptPath}`);
|
|
199
|
+
allScriptsExist = false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (allScriptsExist) {
|
|
205
|
+
result.checks.hooks = true;
|
|
206
|
+
logSuccess(`hooks valid (${events.length} events)`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
addIssue('hooks', 'error', `Failed to parse hooks.json: ${error.message}`);
|
|
90
212
|
}
|
|
91
213
|
|
|
92
214
|
// ============================================================================
|
|
93
|
-
// Check
|
|
215
|
+
// Check 5: Marketplace registration (npm-global and dev modes)
|
|
94
216
|
// ============================================================================
|
|
95
217
|
|
|
96
218
|
const knownMarketplacesPath = join(homedir(), '.claude', 'plugins', 'known_marketplaces.json');
|
|
97
|
-
const claudePluginRoot = join(projectRoot, '.claude-plugin');
|
|
98
219
|
|
|
99
220
|
try {
|
|
100
221
|
if (!existsSync(knownMarketplacesPath)) {
|
|
101
|
-
addIssue('marketplace', '
|
|
222
|
+
addIssue('marketplace', 'warning', 'known_marketplaces.json not found (plugin may be installed via /plugin command)');
|
|
102
223
|
} else {
|
|
103
224
|
const content = readFileSync(knownMarketplacesPath, 'utf-8');
|
|
104
225
|
const marketplaces = JSON.parse(content);
|
|
105
226
|
|
|
106
|
-
if (
|
|
107
|
-
|
|
227
|
+
if (marketplaces['pcircle-ai']) {
|
|
228
|
+
result.checks.marketplace = true;
|
|
229
|
+
logSuccess('Marketplace registered');
|
|
108
230
|
} else {
|
|
109
|
-
|
|
110
|
-
const expectedPath = claudePluginRoot;
|
|
111
|
-
|
|
112
|
-
if (entry.source?.path !== expectedPath) {
|
|
113
|
-
addIssue('marketplace', 'warning', `Marketplace path incorrect (expected: ${expectedPath}, got: ${entry.source?.path})`);
|
|
114
|
-
} else {
|
|
115
|
-
result.checks.marketplace = true;
|
|
116
|
-
logSuccess('Marketplace registered correctly');
|
|
117
|
-
}
|
|
231
|
+
addIssue('marketplace', 'warning', 'pcircle-ai not in known_marketplaces.json (may be installed via /plugin command)');
|
|
118
232
|
}
|
|
119
233
|
}
|
|
120
234
|
} catch (error) {
|
|
121
|
-
addIssue('marketplace', 'error', `Failed to
|
|
235
|
+
addIssue('marketplace', 'error', `Failed to check marketplace: ${error.message}`);
|
|
122
236
|
}
|
|
123
237
|
|
|
124
238
|
// ============================================================================
|
|
125
|
-
// Check
|
|
239
|
+
// Check 6: Symlink or plugin discovery
|
|
126
240
|
// ============================================================================
|
|
127
241
|
|
|
128
242
|
const symlinkPath = join(homedir(), '.claude', 'plugins', 'marketplaces', 'pcircle-ai');
|
|
129
243
|
|
|
130
244
|
try {
|
|
131
245
|
if (!existsSync(symlinkPath)) {
|
|
132
|
-
addIssue('symlink', '
|
|
246
|
+
addIssue('symlink', 'warning', 'Marketplace symlink not found (may be installed via /plugin command)');
|
|
133
247
|
} else {
|
|
134
248
|
const stats = lstatSync(symlinkPath);
|
|
135
249
|
|
|
136
|
-
if (
|
|
137
|
-
addIssue('symlink', 'error', 'Marketplace path is not a symlink');
|
|
138
|
-
} else {
|
|
250
|
+
if (stats.isSymbolicLink()) {
|
|
139
251
|
const target = realpathSync(symlinkPath);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (target !== expectedTarget) {
|
|
143
|
-
addIssue('symlink', 'warning', `Symlink points to wrong location (expected: ${expectedTarget}, got: ${target})`);
|
|
144
|
-
} else if (!existsSync(target)) {
|
|
145
|
-
addIssue('symlink', 'error', 'Symlink target does not exist');
|
|
252
|
+
if (!existsSync(target)) {
|
|
253
|
+
addIssue('symlink', 'error', 'Symlink target does not exist (broken symlink)');
|
|
146
254
|
} else {
|
|
147
255
|
result.checks.symlink = true;
|
|
148
|
-
logSuccess(
|
|
256
|
+
logSuccess(`Symlink valid → ${target}`);
|
|
149
257
|
}
|
|
258
|
+
} else if (stats.isDirectory()) {
|
|
259
|
+
// Could be a direct clone (plugin marketplace install)
|
|
260
|
+
result.checks.symlink = true;
|
|
261
|
+
logSuccess('Plugin directory exists (marketplace install)');
|
|
262
|
+
} else {
|
|
263
|
+
addIssue('symlink', 'error', 'Marketplace path is not a symlink or directory');
|
|
150
264
|
}
|
|
151
265
|
}
|
|
152
266
|
} catch (error) {
|
|
@@ -154,22 +268,22 @@ try {
|
|
|
154
268
|
}
|
|
155
269
|
|
|
156
270
|
// ============================================================================
|
|
157
|
-
// Check
|
|
271
|
+
// Check 7: Plugin enabled in settings
|
|
158
272
|
// ============================================================================
|
|
159
273
|
|
|
160
274
|
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
161
275
|
|
|
162
276
|
try {
|
|
163
277
|
if (!existsSync(settingsPath)) {
|
|
164
|
-
addIssue('settings', '
|
|
278
|
+
addIssue('settings', 'warning', 'settings.json not found');
|
|
165
279
|
} else {
|
|
166
280
|
const content = readFileSync(settingsPath, 'utf-8');
|
|
167
281
|
const settings = JSON.parse(content);
|
|
168
282
|
|
|
169
283
|
if (!settings.enabledPlugins) {
|
|
170
|
-
addIssue('settings', '
|
|
284
|
+
addIssue('settings', 'warning', 'enabledPlugins not found in settings.json');
|
|
171
285
|
} else if (!settings.enabledPlugins['memesh@pcircle-ai']) {
|
|
172
|
-
addIssue('settings', '
|
|
286
|
+
addIssue('settings', 'warning', 'memesh@pcircle-ai not enabled (may need to enable via /plugin command)');
|
|
173
287
|
} else if (settings.enabledPlugins['memesh@pcircle-ai'] !== true) {
|
|
174
288
|
addIssue('settings', 'warning', 'memesh@pcircle-ai is disabled');
|
|
175
289
|
} else {
|
|
@@ -178,48 +292,7 @@ try {
|
|
|
178
292
|
}
|
|
179
293
|
}
|
|
180
294
|
} catch (error) {
|
|
181
|
-
addIssue('settings', 'error', `Failed to
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ============================================================================
|
|
185
|
-
// Check 5: MCP server configuration
|
|
186
|
-
// ============================================================================
|
|
187
|
-
|
|
188
|
-
const mcpSettingsPath = join(homedir(), '.claude', 'mcp_settings.json');
|
|
189
|
-
const expectedMcpPath = distPath;
|
|
190
|
-
|
|
191
|
-
try {
|
|
192
|
-
if (!existsSync(mcpSettingsPath)) {
|
|
193
|
-
addIssue('mcp', 'error', 'mcp_settings.json not found');
|
|
194
|
-
} else {
|
|
195
|
-
const content = readFileSync(mcpSettingsPath, 'utf-8');
|
|
196
|
-
const mcpSettings = JSON.parse(content);
|
|
197
|
-
|
|
198
|
-
if (!mcpSettings.mcpServers) {
|
|
199
|
-
addIssue('mcp', 'error', 'mcpServers object missing');
|
|
200
|
-
} else if (!mcpSettings.mcpServers.memesh) {
|
|
201
|
-
addIssue('mcp', 'error', 'memesh server not configured');
|
|
202
|
-
} else {
|
|
203
|
-
const memeshConfig = mcpSettings.mcpServers.memesh;
|
|
204
|
-
|
|
205
|
-
// Check command
|
|
206
|
-
if (memeshConfig.command !== 'node') {
|
|
207
|
-
addIssue('mcp', 'warning', 'MCP server command should be "node"');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Check args
|
|
211
|
-
if (!Array.isArray(memeshConfig.args) || memeshConfig.args.length === 0) {
|
|
212
|
-
addIssue('mcp', 'error', 'MCP server args missing');
|
|
213
|
-
} else if (memeshConfig.args[0] !== expectedMcpPath) {
|
|
214
|
-
addIssue('mcp', 'warning', `MCP server path incorrect (expected: ${expectedMcpPath}, got: ${memeshConfig.args[0]})`);
|
|
215
|
-
} else {
|
|
216
|
-
result.checks.mcp = true;
|
|
217
|
-
logSuccess('MCP server configured correctly');
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
} catch (error) {
|
|
222
|
-
addIssue('mcp', 'error', `Failed to parse mcp_settings.json: ${error.message}`);
|
|
295
|
+
addIssue('settings', 'error', `Failed to check settings: ${error.message}`);
|
|
223
296
|
}
|
|
224
297
|
|
|
225
298
|
// ============================================================================
|
|
@@ -235,21 +308,24 @@ if (json) {
|
|
|
235
308
|
console.log('✅ All checks passed - plugin installation healthy');
|
|
236
309
|
console.log('═'.repeat(60));
|
|
237
310
|
} else {
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
const repairableCount = result.issues.filter(i => i.repairable).length;
|
|
311
|
+
const errors = result.issues.filter(i => i.severity === 'error');
|
|
312
|
+
const warnings = result.issues.filter(i => i.severity === 'warning');
|
|
241
313
|
|
|
242
|
-
|
|
314
|
+
if (errors.length > 0) {
|
|
315
|
+
console.log(`❌ Found ${errors.length} error(s), ${warnings.length} warning(s)`);
|
|
316
|
+
} else {
|
|
317
|
+
console.log(`⚠️ Found ${warnings.length} warning(s) (non-critical)`);
|
|
318
|
+
}
|
|
243
319
|
console.log('═'.repeat(60));
|
|
244
320
|
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
console.log('\n⚠️ Issues require manual intervention. Run: npm run build\n');
|
|
321
|
+
if (errors.length > 0) {
|
|
322
|
+
const hint = installMode === 'dev' ? 'npm run build' : 'npm install -g @pcircle/memesh';
|
|
323
|
+
console.log(`\n🔧 Fix errors first. Try: ${hint}\n`);
|
|
249
324
|
}
|
|
250
325
|
}
|
|
251
326
|
}
|
|
252
327
|
|
|
253
|
-
// Exit
|
|
254
|
-
const
|
|
255
|
-
|
|
328
|
+
// Exit: errors → 1 or 2, warnings only → 0
|
|
329
|
+
const hasErrors = result.issues.some(i => i.severity === 'error');
|
|
330
|
+
const hasUnrepairableErrors = result.issues.some(i => i.severity === 'error' && !i.repairable);
|
|
331
|
+
process.exit(hasErrors ? (hasUnrepairableErrors ? 2 : 1) : 0);
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "memesh",
|
|
3
|
-
"description": "MeMesh — Persistent memory plugin for Claude Code. Remembers architecture decisions, coding patterns, and project context across sessions.",
|
|
4
|
-
"author": {
|
|
5
|
-
"name": "PCIRCLE AI"
|
|
6
|
-
},
|
|
7
|
-
"version": "2.10.0",
|
|
8
|
-
"homepage": "https://github.com/PCIRCLE-AI/claude-code-buddy",
|
|
9
|
-
"repository": "https://github.com/PCIRCLE-AI/claude-code-buddy",
|
|
10
|
-
"license": "MIT",
|
|
11
|
-
"keywords": ["claude-code", "mcp", "knowledge-graph", "ai-memory", "persistent-memory"],
|
|
12
|
-
"mcpServers": "./.mcp.json",
|
|
13
|
-
"hooks": "./hooks/hooks.json",
|
|
14
|
-
"skills": "./skills/"
|
|
15
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
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.
|
|
File without changes
|