autosnippet 3.2.4 → 3.2.7

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 (67) hide show
  1. package/README.md +18 -5
  2. package/bin/cli.js +164 -145
  3. package/config/constitution.yaml +2 -0
  4. package/dashboard/dist/assets/{index-DNOHYBhy.css → index-BaGY7kJI.css} +1 -1
  5. package/dashboard/dist/assets/{index-6itPuGFl.js → index-DfHY_3ln.js} +25 -25
  6. package/dashboard/dist/index.html +2 -2
  7. package/lib/cli/CliLogger.js +78 -0
  8. package/lib/cli/SetupService.js +9 -718
  9. package/lib/cli/UpgradeService.js +23 -398
  10. package/lib/cli/deploy/FileDeployer.js +562 -0
  11. package/lib/cli/deploy/FileManifest.js +272 -0
  12. package/lib/external/mcp/McpServer.js +22 -26
  13. package/lib/external/mcp/autoApproveInjector.js +1 -0
  14. package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +5 -5
  15. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +25 -3
  16. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +6 -6
  17. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +4 -0
  18. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +5 -5
  19. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +89 -44
  20. package/lib/external/mcp/handlers/consolidated.js +8 -9
  21. package/lib/external/mcp/handlers/dimension-complete-external.js +4 -4
  22. package/lib/external/mcp/handlers/guard.js +283 -5
  23. package/lib/external/mcp/handlers/task.js +361 -15
  24. package/lib/external/mcp/tools.js +32 -81
  25. package/lib/http/HttpServer.js +4 -0
  26. package/lib/http/routes/remote.js +1138 -0
  27. package/lib/http/routes/task.js +56 -0
  28. package/lib/infrastructure/database/migrations/003_add_remote_commands.js +27 -0
  29. package/lib/service/chat/AnalystAgent.js +12 -12
  30. package/lib/service/chat/ChatAgent.js +227 -545
  31. package/lib/service/chat/ChatAgentPrompts.js +9 -11
  32. package/lib/service/chat/ContextWindow.js +2 -296
  33. package/lib/service/chat/EpisodicConsolidator.js +15 -15
  34. package/lib/service/chat/ExplorationTracker.js +1262 -0
  35. package/lib/service/chat/HandoffProtocol.js +8 -9
  36. package/lib/service/chat/Memory.js +4 -0
  37. package/lib/service/chat/ProducerAgent.js +9 -6
  38. package/lib/service/chat/ProjectSemanticMemory.js +4 -0
  39. package/lib/service/chat/ReasoningTrace.js +182 -0
  40. package/lib/service/chat/WorkingMemory.js +4 -0
  41. package/lib/service/chat/memory/ActiveContext.js +910 -0
  42. package/lib/service/chat/memory/MemoryCoordinator.js +662 -0
  43. package/lib/service/chat/memory/PersistentMemory.js +450 -0
  44. package/lib/service/chat/memory/SessionStore.js +896 -0
  45. package/lib/service/chat/memory/index.js +13 -0
  46. package/lib/service/chat/tools/ast-graph.js +17 -16
  47. package/lib/service/cursor/AgentInstructionsGenerator.js +76 -47
  48. package/lib/service/cursor/FileProtection.js +4 -1
  49. package/lib/service/guard/GuardCheckEngine.js +10 -3
  50. package/lib/service/task/TaskGraphService.js +3 -3
  51. package/lib/shared/LanguageService.js +2 -1
  52. package/package.json +12 -1
  53. package/skills/autosnippet-intent/SKILL.md +1 -3
  54. package/skills/autosnippet-recipes/SKILL.md +1 -3
  55. package/templates/claude-code/commands/prime.md +19 -0
  56. package/templates/claude-code/hooks/autosnippet-session.sh +63 -0
  57. package/templates/claude-code/settings.json +21 -0
  58. package/templates/copilot-instructions.md +64 -177
  59. package/templates/cursor-hooks/commands/prime.md +12 -0
  60. package/templates/cursor-hooks/hooks/session-start.sh +10 -0
  61. package/templates/cursor-hooks/hooks.json +11 -0
  62. package/templates/cursor-rules/autosnippet-conventions.mdc +52 -3
  63. package/templates/cursor-rules/autosnippet-workflow.mdc +51 -27
  64. package/lib/external/mcp/handlers/decide.js +0 -109
  65. package/lib/external/mcp/handlers/ready.js +0 -42
  66. package/lib/service/chat/ReasoningLayer.js +0 -888
  67. package/templates/claude-hooks.yaml +0 -19
@@ -1,36 +1,21 @@
1
1
  /**
2
2
  * UpgradeService — IDE 集成升级服务
3
3
  *
4
- * 当 AutoSnippet 发布新版本后,老用户执行 `asd upgrade` 即可更新:
5
- * MCP 配置(.cursor/mcp.json + .vscode/mcp.json)
6
- * ② Cursor Skills(.cursor/skills/)
7
- * ③ Cursor Rules(.cursor/rules/autosnippet-conventions.mdc + autosnippet-skills.mdc)
8
- * ④ Agent Instructions(AGENTS.md + CLAUDE.md + .github/copilot-instructions.md — 通过 Channel F 动态生成)
9
- * ⑤ Constitution(AutoSnippet/constitution.yaml)
10
- * ⑥ .gitignore(升级规则 + 清理旧版本残留)
11
- * ⑦ Skills 路径迁移(.autosnippet/skills/ → AutoSnippet/skills/)
4
+ * 当 AutoSnippet 发布新版本后,老用户执行 `asd upgrade` 即可更新所有 IDE 集成文件。
5
+ * 底层委托 FileDeployer MANIFEST 定义的策略执行,确保与 SetupService 使用同一套部署逻辑。
12
6
  *
13
- * 不会重建数据库、子仓库或运行时目录。
7
+ * 额外职责:
8
+ * - Skills 路径迁移(.autosnippet/skills/ → AutoSnippet/skills/)
14
9
  */
15
10
 
16
11
  import { execSync } from 'node:child_process';
17
12
  import {
18
- copyFileSync,
19
13
  existsSync,
20
14
  mkdirSync,
21
15
  readdirSync,
22
- readFileSync,
23
- writeFileSync,
24
16
  } from 'node:fs';
25
- import { dirname, join, resolve } from 'node:path';
26
- import { fileURLToPath } from 'node:url';
27
- import { safeCopyFile } from '../service/cursor/FileProtection.js';
28
- import { injectAutoApprove } from '../external/mcp/autoApproveInjector.js';
29
-
30
- const __filename = fileURLToPath(import.meta.url);
31
- const __dirname = dirname(__filename);
32
-
33
- const REPO_ROOT = resolve(__dirname, '..', '..');
17
+ import { join, resolve } from 'node:path';
18
+ import { FileDeployer } from './deploy/FileDeployer.js';
34
19
 
35
20
  export class UpgradeService {
36
21
  constructor(options) {
@@ -39,378 +24,32 @@ export class UpgradeService {
39
24
  }
40
25
 
41
26
  async run({ skillsOnly = false, mcpOnly = false } = {}) {
42
- const results = [];
43
-
44
- if (!skillsOnly) {
45
- results.push(this._upgradeMCP());
46
- }
47
- if (!mcpOnly) {
48
- results.push(this._upgradeSkills());
49
- }
50
- if (!skillsOnly && !mcpOnly) {
51
- results.push(this._upgradeCursorRules());
52
- // NOTE: .qoder/ .trae/ 不再自动镜像,用户可通过 `asd mirror` 按需同步
53
- results.push(this._upgradeSkillsTemplate());
54
- results.push(this._upgradeCopilotInstructions());
55
- results.push(this._upgradeConstitution());
56
- results.push(this._upgradeGitignore());
57
- results.push(this._migrateSkillsPath());
58
- results.push(this._ensureSkillsDir());
59
- }
60
-
61
- return results;
62
- }
63
-
64
- /* ═══ MCP 配置 ══════════════════════════════════════ */
65
-
66
- _upgradeMCP() {
67
- // Cursor
68
- this._updateCursorMCP();
69
- // VSCode
70
- this._updateVSCodeMCP();
71
- }
72
-
73
- _updateCursorMCP() {
74
- const configPath = join(this.projectRoot, '.cursor', 'mcp.json');
75
- if (!existsSync(configPath)) {
76
- return;
77
- }
78
-
79
- let config = {};
80
- try {
81
- config = JSON.parse(readFileSync(configPath, 'utf8'));
82
- } catch {
83
- /* */
84
- }
85
- if (!config.mcpServers) {
86
- config.mcpServers = {};
87
- }
88
-
89
- config.mcpServers.autosnippet = {
90
- command: 'asd-mcp',
91
- env: {
92
- ASD_PROJECT_DIR: this.projectRoot,
93
- },
94
- };
95
-
96
- writeFileSync(configPath, JSON.stringify(config, null, 2));
97
-
98
- // 升级时注入 autoApprove(老用户已经使用过,无需首次授权体验)
99
- injectAutoApprove(this.projectRoot);
100
- }
101
-
102
- _updateVSCodeMCP() {
103
- const vscodeDir = join(this.projectRoot, '.vscode');
104
- const mcpConfigPath = join(vscodeDir, 'mcp.json');
105
-
106
- let config = {};
107
- if (existsSync(mcpConfigPath)) {
108
- try {
109
- config = JSON.parse(readFileSync(mcpConfigPath, 'utf8'));
110
- } catch {
111
- /* */
112
- }
113
- }
114
-
115
- if (!config.servers) {
116
- config.servers = {};
117
- }
118
-
119
- config.servers.autosnippet = {
120
- type: 'stdio',
121
- command: 'asd-mcp',
122
- env: {
123
- ASD_PROJECT_DIR: this.projectRoot,
124
- },
125
- };
126
-
127
- mkdirSync(vscodeDir, { recursive: true });
128
- writeFileSync(mcpConfigPath, JSON.stringify(config, null, 2));
129
- }
130
-
131
- /* ═══ Skills ════════════════════════════════════════ */
132
-
133
- _upgradeSkills() {
134
- const installScript = join(REPO_ROOT, 'scripts', 'install-cursor-skill.js');
135
- if (!existsSync(installScript)) {
136
- return;
137
- }
138
-
139
- try {
140
- execSync(`node "${installScript}"`, {
141
- cwd: this.projectRoot,
142
- stdio: 'inherit',
143
- env: { ...process.env, NODE_PATH: join(REPO_ROOT, 'node_modules') },
144
- });
145
- } catch (e) {
146
- console.error(` ❌ Skills 安装失败: ${e.message}`);
147
- }
148
- }
149
-
150
- /* ═══ Cursor Rules ══════════════════════════════════ */
151
-
152
- _upgradeCursorRules() {
153
- const src = join(REPO_ROOT, 'templates', 'cursor-rules', 'autosnippet-conventions.mdc');
154
- if (!existsSync(src)) {
155
- return;
156
- }
157
-
158
- const destDir = join(this.projectRoot, '.cursor', 'rules');
159
- const dest = join(destDir, 'autosnippet-conventions.mdc');
160
- mkdirSync(destDir, { recursive: true });
161
- copyFileSync(src, dest);
162
-
163
- // 动态生成 4 通道交付物料(如 ServiceContainer 已初始化)
164
- this._triggerCursorDelivery();
165
- }
166
-
167
- /**
168
- * 触发 Cursor Delivery Pipeline 动态生成
169
- * 包含 Channel A-D + Channel F (AGENTS.md / CLAUDE.md / copilot-instructions)
170
- * 非阻塞 — 失败不影响 upgrade 流程
171
- */
172
- _triggerCursorDelivery() {
173
- import('../injection/ServiceContainer.js')
174
- .then(({ getServiceContainer }) => {
175
- const container = getServiceContainer();
176
- if (container.services.cursorDeliveryPipeline) {
177
- const pipeline = container.get('cursorDeliveryPipeline');
178
- pipeline
179
- .deliver()
180
- .then((_result) => {})
181
- .catch((_err) => {
182
- /* fire-and-forget: delivery failure is non-critical during upgrade */
183
- });
184
- }
185
- })
186
- .catch(() => {
187
- // ServiceContainer 未初始化 — 正常(upgrade 可能在无 DB 环境执行)
188
- });
189
- }
190
-
191
- /* ═══ Mirror IDE Rules (Qoder / Trae) ═══════════════ */
192
-
193
- /**
194
- * 镜像 .cursor/rules/ 中的 autosnippet-* 文件到目标 IDE 目录
195
- * 只触碰 autosnippet- 前缀的文件,保留用户自定义规则
196
- * @param {string} targetDirName - '.qoder' 或 '.trae'
197
- */
198
- _upgradeMirrorIDE(targetDirName) {
199
- const _label = targetDirName.replace('.', '').charAt(0).toUpperCase() + targetDirName.slice(2);
200
-
201
- // 镜像 .cursor/rules/ 中的 autosnippet-* 文件
202
- const cursorRulesDir = join(this.projectRoot, '.cursor', 'rules');
203
- if (!existsSync(cursorRulesDir)) {
204
- return;
205
- }
206
-
207
- const targetRulesDir = join(this.projectRoot, targetDirName, 'rules');
208
- mkdirSync(targetRulesDir, { recursive: true });
209
-
210
- // 只复制 autosnippet- 前缀的文件,不触碰用户自己创建的规则
211
- const files = readdirSync(cursorRulesDir).filter(
212
- (f) => (f.endsWith('.mdc') || f.endsWith('.md')) && f.startsWith('autosnippet-')
213
- );
214
- for (const file of files) {
215
- const destName = file.endsWith('.mdc') ? file.replace(/\.mdc$/, '.md') : file;
216
- copyFileSync(join(cursorRulesDir, file), join(targetRulesDir, destName));
217
- }
218
-
219
- // 镜像 .cursor/skills/ 中的 autosnippet-* 技能目录
220
- const cursorSkillsDir = join(this.projectRoot, '.cursor', 'skills');
221
- if (existsSync(cursorSkillsDir)) {
222
- const targetSkillsDir = join(this.projectRoot, targetDirName, 'skills');
223
- mkdirSync(targetSkillsDir, { recursive: true });
224
- const skillDirs = readdirSync(cursorSkillsDir, { withFileTypes: true }).filter(
225
- (d) => d.isDirectory() && d.name.startsWith('autosnippet-')
226
- );
227
- for (const dir of skillDirs) {
228
- this._copyDirRecursiveSkill(
229
- join(cursorSkillsDir, dir.name),
230
- join(targetSkillsDir, dir.name)
231
- );
232
- }
233
- }
234
- }
235
-
236
- /** @private 递归复制目录(合并模式,不删除目标已有文件) */
237
- _copyDirRecursiveSkill(src, dest) {
238
- mkdirSync(dest, { recursive: true });
239
- for (const entry of readdirSync(src, { withFileTypes: true })) {
240
- const srcPath = join(src, entry.name);
241
- const destPath = join(dest, entry.name);
242
- if (entry.isDirectory()) {
243
- this._copyDirRecursiveSkill(srcPath, destPath);
244
- } else {
245
- copyFileSync(srcPath, destPath);
246
- }
247
- }
248
- }
249
-
250
- /* ═══ Copilot Instructions (fallback static copy) ══════════════════ */
251
-
252
- /**
253
- * 静态模板回退 — 当 _triggerCursorDelivery() 无法运行时
254
- * (无 DB 环境),至少保证有一份基础模板。
255
- * Channel F 动态生成会覆盖此文件。
256
- */
257
- _upgradeCopilotInstructions() {
258
- const src = join(REPO_ROOT, 'templates', 'copilot-instructions.md');
259
- if (!existsSync(src)) {
260
- return;
261
- }
262
-
263
- const destDir = join(this.projectRoot, '.github');
264
- const dest = join(destDir, 'copilot-instructions.md');
265
- mkdirSync(destDir, { recursive: true });
266
- const { written } = safeCopyFile(src, dest);
267
- if (!written) {
268
- }
269
- }
270
-
271
- /* ═══ Constitution ══════════════════════════════════ */
27
+ const deployer = new FileDeployer({
28
+ projectRoot: this.projectRoot,
29
+ force: false,
30
+ });
272
31
 
273
- _upgradeConstitution() {
274
- const src = join(REPO_ROOT, 'templates', 'constitution.yaml');
275
- if (!existsSync(src)) {
276
- return;
32
+ let filter;
33
+ if (skillsOnly) {
34
+ filter = ['skills'];
35
+ } else if (mcpOnly) {
36
+ filter = ['mcp'];
277
37
  }
278
38
 
279
- // 子仓库路径:AutoSnippet/constitution.yaml
280
- const dest = join(this.projectRoot, 'AutoSnippet', 'constitution.yaml');
281
- if (!existsSync(join(this.projectRoot, 'AutoSnippet'))) {
282
- return;
283
- }
39
+ const { deployed, skipped, errors } = deployer.deployAll('upgrade', { filter });
284
40
 
285
- // 如果目标已存在,备份旧版本
286
- if (existsSync(dest)) {
287
- const oldContent = readFileSync(dest, 'utf8');
288
- const newContent = readFileSync(src, 'utf8');
289
- if (oldContent === newContent) {
290
- return;
41
+ if (errors.length > 0) {
42
+ for (const { id, error } of errors) {
43
+ console.error(` ⚠ ${id}: ${error}`);
291
44
  }
292
- const backupPath = `${dest}.bak`;
293
- copyFileSync(dest, backupPath);
294
45
  }
295
46
 
296
- copyFileSync(src, dest);
297
-
298
- // 如果子仓库是 git 仓库,提示用户提交
299
- const gitDir = join(this.projectRoot, 'AutoSnippet', '.git');
300
- if (existsSync(gitDir)) {
301
- }
302
- }
303
- /* ═══ Skills Template ════════════════════════════════ */
304
-
305
- _upgradeSkillsTemplate() {
306
- const src = join(REPO_ROOT, 'templates', 'cursor-rules', 'autosnippet-skills.mdc');
307
- if (!existsSync(src)) {
308
- return;
309
- }
310
-
311
- const destDir = join(this.projectRoot, '.cursor', 'rules');
312
- const dest = join(destDir, 'autosnippet-skills.mdc');
313
- mkdirSync(destDir, { recursive: true });
314
- copyFileSync(src, dest);
315
- }
316
-
317
- /* ═══ .gitignore ════════════════════════════════════ */
318
-
319
- _upgradeGitignore() {
320
- const giPath = join(this.projectRoot, '.gitignore');
321
- if (!existsSync(giPath)) {
322
- return;
323
- }
324
-
325
- let content = readFileSync(giPath, 'utf8');
326
- let changed = false;
327
-
328
- // v2.4.0 迁移:旧格式 ".autosnippet/" → 新格式 ".autosnippet/*"
329
- if (content.includes('.autosnippet/') && !content.includes('.autosnippet/*')) {
330
- content = content.replace(/^\.autosnippet\/$/m, '.autosnippet/*');
331
- changed = true;
332
- }
333
-
334
- // 确保有 .autosnippet/*
335
- if (!content.includes('.autosnippet/') && !content.includes('.autosnippet/*')) {
336
- content += `\n# AutoSnippet 运行时缓存(不入库)\n.autosnippet/*\n`;
337
- changed = true;
338
- }
339
-
340
- // 确保 config.json 跟踪
341
- if (!content.includes('!.autosnippet/config.json')) {
342
- content += `!.autosnippet/config.json\n`;
343
- changed = true;
344
- }
345
-
346
- // 清理旧版本的 .autosnippet/skills/ negation(已迁移到 AutoSnippet/skills/)
347
- if (content.includes('!.autosnippet/skills/')) {
348
- content = content.replace(/^!?\.autosnippet\/skills\/.*\n?/gm, '');
349
- changed = true;
350
- }
351
-
352
- // ── v2.8.1: 新增缺失的 gitignore 规则 ──
353
-
354
- // _draft_*.md — AI Agent 在项目根目录创建的草稿文件
355
- if (!content.includes('_draft_*.md')) {
356
- content += `\n# AutoSnippet AI 草稿文件(项目根目录临时文件)\n_draft_*.md\n`;
357
- changed = true;
358
- }
359
-
360
- // .DS_Store — macOS 元数据
361
- if (!content.includes('.DS_Store')) {
362
- content += `\n# macOS 元数据\n.DS_Store\n`;
363
- changed = true;
364
- }
365
-
366
- // nohup.out — 后台进程输出
367
- if (!content.includes('nohup.out')) {
368
- content += `nohup.out\n`;
369
- changed = true;
370
- }
371
-
372
- // *.sw[a-p] — vim swap 文件
373
- if (!content.match(/\*\.sw\[a-p\]/)) {
374
- content += `*.sw[a-p]\n`;
375
- changed = true;
376
- }
377
-
378
- // .autosnippet-drafts/ — AI 草稿目录
379
- if (!content.includes('.autosnippet-drafts')) {
380
- content += `\n# AutoSnippet AI 草稿(临时)\n.autosnippet-drafts/\n`;
381
- changed = true;
382
- }
383
-
384
- // .env — 环境变量
385
- if (!content.includes('.env') || (!content.match(/^\.env$/m) && !content.match(/^\.env\s/m))) {
386
- content += `\n# AutoSnippet 环境变量(含 API Key,不入库)\n.env\n`;
387
- changed = true;
388
- }
389
-
390
- // logs/ — 运行日志
391
- if (!content.match(/^logs\/?$/m)) {
392
- content += `\n# AutoSnippet 运行日志\nlogs/\n`;
393
- changed = true;
394
- }
395
-
396
- // 确保 AutoSnippet/ 不被忽略
397
- const lines = content.split('\n');
398
- const hasIgnoreAS = lines.some((l) => {
399
- const t = l.trim();
400
- return (
401
- (t === 'AutoSnippet/' || t === 'AutoSnippet') && !t.startsWith('#') && !t.startsWith('!')
402
- );
403
- });
404
- const hasNegation = lines.some((l) => l.trim() === '!AutoSnippet/');
405
- if (hasIgnoreAS && !hasNegation) {
406
- content += `\n# AutoSnippet 知识库必须入库(取消上方忽略)\n!AutoSnippet/\n`;
407
- changed = true;
47
+ // Skills 路径迁移(一次性操作,不属于文件部署)
48
+ if (!skillsOnly && !mcpOnly) {
49
+ this._migrateSkillsPath();
408
50
  }
409
51
 
410
- if (changed) {
411
- writeFileSync(giPath, content);
412
- } else {
413
- }
52
+ return { deployed, skipped, errors };
414
53
  }
415
54
 
416
55
  /* ═══ Skills 路径迁移 ═══════════════════════════════ */
@@ -452,20 +91,6 @@ export class UpgradeService {
452
91
  console.error(` ❌ 迁移失败: ${e.message}`);
453
92
  }
454
93
  }
455
-
456
- /* ═══ 确保 Skills 目录存在 ══════════════════════════ */
457
-
458
- _ensureSkillsDir() {
459
- const skillsDir = join(this.projectRoot, 'AutoSnippet', 'skills');
460
- if (!existsSync(join(this.projectRoot, 'AutoSnippet'))) {
461
- return;
462
- }
463
- if (existsSync(skillsDir)) {
464
- return;
465
- }
466
-
467
- mkdirSync(skillsDir, { recursive: true });
468
- }
469
94
  }
470
95
 
471
96
  export default UpgradeService;