autosnippet 3.1.2 → 3.1.4
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/bin/mcp-server.js +0 -0
- package/lib/cli/SetupService.js +6 -14
- package/lib/cli/UpgradeService.js +6 -13
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -2
- package/lib/service/knowledge/KnowledgeFileWriter.js +5 -0
- package/lib/service/wiki/WikiUtils.js +8 -0
- package/package.json +3 -2
package/bin/mcp-server.js
CHANGED
|
File without changes
|
package/lib/cli/SetupService.js
CHANGED
|
@@ -60,8 +60,6 @@ const __dirname = dirname(__filename);
|
|
|
60
60
|
|
|
61
61
|
/** AutoSnippet 源码仓库根目录(定位 templates/ 等资源) */
|
|
62
62
|
const REPO_ROOT = resolve(__dirname, '..', '..');
|
|
63
|
-
/** V2 子项目根目录(定位 bin/mcp-server.js 等) */
|
|
64
|
-
const V2_ROOT = resolve(__dirname, '..', '..');
|
|
65
63
|
|
|
66
64
|
// ─────────────────────────────────────────────────────
|
|
67
65
|
|
|
@@ -434,10 +432,8 @@ export class SetupService {
|
|
|
434
432
|
/* ═══ Step 3: IDE 集成 ═══════════════════════════════ */
|
|
435
433
|
|
|
436
434
|
stepIDE() {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
this._configureVSCodeMCP(mcpServerPath);
|
|
440
|
-
this._configureCursorMCP(mcpServerPath);
|
|
435
|
+
this._configureVSCodeMCP();
|
|
436
|
+
this._configureCursorMCP();
|
|
441
437
|
this._copyCopilotInstructions();
|
|
442
438
|
this._generateAgentInstructionFiles();
|
|
443
439
|
this._copyCursorRules();
|
|
@@ -649,7 +645,7 @@ export class SetupService {
|
|
|
649
645
|
}
|
|
650
646
|
|
|
651
647
|
/** @private .vscode/mcp.json → VSCode MCP (新标准格式) */
|
|
652
|
-
_configureVSCodeMCP(
|
|
648
|
+
_configureVSCodeMCP() {
|
|
653
649
|
const vscodeDir = join(this.projectRoot, '.vscode');
|
|
654
650
|
const mcpConfigPath = join(vscodeDir, 'mcp.json');
|
|
655
651
|
mkdirSync(vscodeDir, { recursive: true });
|
|
@@ -668,11 +664,9 @@ export class SetupService {
|
|
|
668
664
|
}
|
|
669
665
|
config.servers.autosnippet = {
|
|
670
666
|
type: 'stdio',
|
|
671
|
-
command: '
|
|
672
|
-
args: [mcpServerPath],
|
|
667
|
+
command: 'asd-mcp',
|
|
673
668
|
env: {
|
|
674
669
|
ASD_PROJECT_DIR: this.projectRoot,
|
|
675
|
-
NODE_PATH: join(V2_ROOT, 'node_modules'),
|
|
676
670
|
},
|
|
677
671
|
};
|
|
678
672
|
|
|
@@ -680,7 +674,7 @@ export class SetupService {
|
|
|
680
674
|
}
|
|
681
675
|
|
|
682
676
|
/** @private .cursor/mcp.json */
|
|
683
|
-
_configureCursorMCP(
|
|
677
|
+
_configureCursorMCP() {
|
|
684
678
|
const cursorDir = join(this.projectRoot, '.cursor');
|
|
685
679
|
const configPath = join(cursorDir, 'mcp.json');
|
|
686
680
|
mkdirSync(cursorDir, { recursive: true });
|
|
@@ -698,11 +692,9 @@ export class SetupService {
|
|
|
698
692
|
existing.mcpServers = {};
|
|
699
693
|
}
|
|
700
694
|
existing.mcpServers.autosnippet = {
|
|
701
|
-
command: '
|
|
702
|
-
args: [mcpServerPath],
|
|
695
|
+
command: 'asd-mcp',
|
|
703
696
|
env: {
|
|
704
697
|
ASD_PROJECT_DIR: this.projectRoot,
|
|
705
|
-
NODE_PATH: join(V2_ROOT, 'node_modules'),
|
|
706
698
|
},
|
|
707
699
|
};
|
|
708
700
|
|
|
@@ -63,16 +63,13 @@ export class UpgradeService {
|
|
|
63
63
|
/* ═══ MCP 配置 ══════════════════════════════════════ */
|
|
64
64
|
|
|
65
65
|
_upgradeMCP() {
|
|
66
|
-
const mcpServerPath = join(REPO_ROOT, 'bin', 'mcp-server.js');
|
|
67
|
-
const nodePath = join(REPO_ROOT, 'node_modules');
|
|
68
|
-
|
|
69
66
|
// Cursor
|
|
70
|
-
this._updateCursorMCP(
|
|
67
|
+
this._updateCursorMCP();
|
|
71
68
|
// VSCode
|
|
72
|
-
this._updateVSCodeMCP(
|
|
69
|
+
this._updateVSCodeMCP();
|
|
73
70
|
}
|
|
74
71
|
|
|
75
|
-
_updateCursorMCP(
|
|
72
|
+
_updateCursorMCP() {
|
|
76
73
|
const configPath = join(this.projectRoot, '.cursor', 'mcp.json');
|
|
77
74
|
if (!existsSync(configPath)) {
|
|
78
75
|
return;
|
|
@@ -89,18 +86,16 @@ export class UpgradeService {
|
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
config.mcpServers.autosnippet = {
|
|
92
|
-
command: '
|
|
93
|
-
args: [mcpServerPath],
|
|
89
|
+
command: 'asd-mcp',
|
|
94
90
|
env: {
|
|
95
91
|
ASD_PROJECT_DIR: this.projectRoot,
|
|
96
|
-
NODE_PATH: nodePath,
|
|
97
92
|
},
|
|
98
93
|
};
|
|
99
94
|
|
|
100
95
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
101
96
|
}
|
|
102
97
|
|
|
103
|
-
_updateVSCodeMCP(
|
|
98
|
+
_updateVSCodeMCP() {
|
|
104
99
|
const vscodeDir = join(this.projectRoot, '.vscode');
|
|
105
100
|
const mcpConfigPath = join(vscodeDir, 'mcp.json');
|
|
106
101
|
|
|
@@ -119,11 +114,9 @@ export class UpgradeService {
|
|
|
119
114
|
|
|
120
115
|
config.servers.autosnippet = {
|
|
121
116
|
type: 'stdio',
|
|
122
|
-
command: '
|
|
123
|
-
args: [mcpServerPath],
|
|
117
|
+
command: 'asd-mcp',
|
|
124
118
|
env: {
|
|
125
119
|
ASD_PROJECT_DIR: this.projectRoot,
|
|
126
|
-
NODE_PATH: nodePath,
|
|
127
120
|
},
|
|
128
121
|
};
|
|
129
122
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import fs from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import Logger from '../../../../../infrastructure/logging/Logger.js';
|
|
12
|
+
import pathGuard from '../../../../../shared/PathGuard.js';
|
|
12
13
|
|
|
13
14
|
const logger = Logger.getInstance();
|
|
14
15
|
|
|
@@ -79,8 +80,12 @@ export async function loadCheckpoints(projectRoot) {
|
|
|
79
80
|
export async function clearCheckpoints(projectRoot) {
|
|
80
81
|
try {
|
|
81
82
|
const checkpointDir = path.join(projectRoot, '.autosnippet', 'bootstrap-checkpoint');
|
|
83
|
+
pathGuard.assertSafe(checkpointDir);
|
|
82
84
|
await fs.rm(checkpointDir, { recursive: true, force: true });
|
|
83
|
-
} catch {
|
|
84
|
-
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (err?.name === 'PathGuardError') {
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
/* ignore other errors */
|
|
85
90
|
}
|
|
86
91
|
}
|
|
@@ -309,6 +309,7 @@ export class KnowledgeFileWriter {
|
|
|
309
309
|
if (entry.sourceFile) {
|
|
310
310
|
const fullPath = path.join(this.projectRoot, entry.sourceFile);
|
|
311
311
|
if (fs.existsSync(fullPath)) {
|
|
312
|
+
pathGuard.assertSafe(fullPath);
|
|
312
313
|
fs.unlinkSync(fullPath);
|
|
313
314
|
this.logger.info('Knowledge entry file removed', {
|
|
314
315
|
entryId: entry.id,
|
|
@@ -328,6 +329,7 @@ export class KnowledgeFileWriter {
|
|
|
328
329
|
for (const dir of searchDirs) {
|
|
329
330
|
const fp = path.join(dir, filename);
|
|
330
331
|
if (fs.existsSync(fp)) {
|
|
332
|
+
pathGuard.assertSafe(fp);
|
|
331
333
|
fs.unlinkSync(fp);
|
|
332
334
|
this.logger.info('Knowledge entry file removed', { entryId: entry.id, path: fp });
|
|
333
335
|
return true;
|
|
@@ -358,6 +360,7 @@ export class KnowledgeFileWriter {
|
|
|
358
360
|
|
|
359
361
|
// 删除旧文件
|
|
360
362
|
if (oldPath && fs.existsSync(oldPath)) {
|
|
363
|
+
pathGuard.assertSafe(oldPath);
|
|
361
364
|
fs.unlinkSync(oldPath);
|
|
362
365
|
this.logger.info('Removed old knowledge entry file on lifecycle change', {
|
|
363
366
|
entryId: entry.id,
|
|
@@ -392,6 +395,7 @@ export class KnowledgeFileWriter {
|
|
|
392
395
|
}
|
|
393
396
|
const oldPath = path.join(this.projectRoot, entry.sourceFile);
|
|
394
397
|
if (oldPath !== newPath && fs.existsSync(oldPath)) {
|
|
398
|
+
pathGuard.assertSafe(oldPath);
|
|
395
399
|
fs.unlinkSync(oldPath);
|
|
396
400
|
this.logger.info('Cleaned up old knowledge entry file', {
|
|
397
401
|
entryId: entry.id,
|
|
@@ -675,6 +679,7 @@ function _walkAndRemoveById(dir, id) {
|
|
|
675
679
|
} else if (entry.name.endsWith('.md') && !entry.name.startsWith('_')) {
|
|
676
680
|
const head = fs.readFileSync(full, 'utf8').slice(0, 500);
|
|
677
681
|
if (head.includes(`id: ${id}`)) {
|
|
682
|
+
pathGuard.assertSafe(full);
|
|
678
683
|
fs.unlinkSync(full);
|
|
679
684
|
return true;
|
|
680
685
|
}
|
|
@@ -332,6 +332,10 @@ export function dedup(files, wikiDir, emit) {
|
|
|
332
332
|
// 完全相同 hash → 移除后来的
|
|
333
333
|
if (existing.hash === file.hash) {
|
|
334
334
|
const fullPath = path.join(wikiDir, file.path);
|
|
335
|
+
if (!fullPath.startsWith(path.resolve(wikiDir) + path.sep)) {
|
|
336
|
+
logger.warn(`[WikiGenerator] Dedup: path escape blocked — ${file.path}`);
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
335
339
|
try {
|
|
336
340
|
fs.unlinkSync(fullPath);
|
|
337
341
|
} catch {
|
|
@@ -361,6 +365,10 @@ export function dedup(files, wikiDir, emit) {
|
|
|
361
365
|
if (isCurrentSynced && !isFirstSynced) {
|
|
362
366
|
// 当前是 synced,first 是 codegen → 删除 synced
|
|
363
367
|
const fullPath = path.join(wikiDir, file.path);
|
|
368
|
+
if (!fullPath.startsWith(path.resolve(wikiDir) + path.sep)) {
|
|
369
|
+
logger.warn(`[WikiGenerator] Dedup: path escape blocked — ${file.path}`);
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
364
372
|
try {
|
|
365
373
|
fs.unlinkSync(fullPath);
|
|
366
374
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autosnippet",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.4",
|
|
4
4
|
"description": "Extract code patterns into a knowledge base for AI coding assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/bootstrap.js",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"author": "gaoxuefeng",
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"bin": {
|
|
43
|
-
"asd": "bin/cli.js"
|
|
43
|
+
"asd": "bin/cli.js",
|
|
44
|
+
"asd-mcp": "bin/mcp-server.js"
|
|
44
45
|
},
|
|
45
46
|
"keywords": [
|
|
46
47
|
"snippet",
|