autosnippet 3.1.14 → 3.2.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.
Files changed (38) hide show
  1. package/README.md +1 -0
  2. package/bin/cli.js +242 -23
  3. package/dashboard/dist/assets/{icons-CC5R_iwL.js → icons-18VxiaCT.js} +108 -98
  4. package/dashboard/dist/assets/index-BJiuaVPD.css +1 -0
  5. package/dashboard/dist/assets/index-CRH5Umim.js +128 -0
  6. package/dashboard/dist/index.html +3 -3
  7. package/lib/domain/task/Task.js +214 -0
  8. package/lib/domain/task/TaskDependency.js +48 -0
  9. package/lib/domain/task/TaskIdGenerator.js +83 -0
  10. package/lib/domain/task/index.js +6 -0
  11. package/lib/external/mcp/McpServer.js +4 -4
  12. package/lib/external/mcp/handlers/task.js +295 -0
  13. package/lib/external/mcp/tools.js +100 -3
  14. package/lib/http/HttpServer.js +8 -0
  15. package/lib/http/routes/guard.js +283 -0
  16. package/lib/http/routes/task.js +282 -0
  17. package/lib/infrastructure/database/migrations/002_add_tasks.js +88 -0
  18. package/lib/injection/ServiceContainer.js +58 -0
  19. package/lib/repository/task/TaskRepository.impl.js +398 -0
  20. package/lib/service/cursor/AgentInstructionsGenerator.js +28 -9
  21. package/lib/service/cursor/CursorDeliveryPipeline.js +42 -20
  22. package/lib/service/cursor/KnowledgeCompressor.js +40 -0
  23. package/lib/service/cursor/TokenBudget.js +2 -2
  24. package/lib/service/guard/GuardFeedbackLoop.js +17 -2
  25. package/lib/service/knowledge/KnowledgeService.js +6 -0
  26. package/lib/service/task/TaskGraphService.js +410 -0
  27. package/lib/service/task/TaskKnowledgeBridge.js +86 -0
  28. package/lib/service/task/TaskReadyEngine.js +127 -0
  29. package/lib/shared/constants.js +3 -3
  30. package/package.json +1 -1
  31. package/skills/autosnippet-intent/SKILL.md +4 -1
  32. package/skills/autosnippet-recipes/SKILL.md +17 -2
  33. package/templates/claude-hooks.yaml +18 -0
  34. package/templates/copilot-instructions.md +31 -1
  35. package/templates/cursor-rules/autosnippet-conventions.mdc +12 -0
  36. package/templates/cursor-rules/autosnippet-workflow.mdc +43 -0
  37. package/dashboard/dist/assets/index-9EEk0svu.css +0 -1
  38. package/dashboard/dist/assets/index-CT2GV56F.js +0 -128
package/README.md CHANGED
@@ -123,6 +123,7 @@ The VS Code extension and `asd watch` (Xcode) pick these up automatically.
123
123
  | `asd guard:staged` | Pre-commit hook |
124
124
  | `asd watch` | Xcode file watcher |
125
125
  | `asd sync` | Sync recipe markdown → DB |
126
+ | `asd task` | Task management (TaskGraph) |
126
127
  | `asd upgrade` | Update IDE integrations |
127
128
  | `asd status` | Health check |
128
129
 
package/bin/cli.js CHANGED
@@ -89,6 +89,7 @@ program
89
89
  .action(async (opts) => {
90
90
  const projectRoot = resolve(opts.dir);
91
91
  if (opts.skipGuard) {
92
+ console.log('ℹ️ Guard 审计已跳过');
92
93
  }
93
94
 
94
95
  try {
@@ -110,45 +111,62 @@ program
110
111
  spinner.stop();
111
112
 
112
113
  if (opts.json) {
114
+ console.log(JSON.stringify(result, null, 2));
113
115
  } else {
114
116
  // 输出骨架报告
115
117
  const report = result.report || {};
116
- const _targets = result.targets || [];
118
+ const targets = result.targets || [];
117
119
  const langStats = result.languageStats || {};
118
120
  const guardSummary = result.guardSummary;
119
121
  const astSummary = result.astSummary;
120
- const _bootstrapCandidates = result.bootstrapCandidates || {};
121
122
  const framework = result.analysisFramework || {};
123
+
124
+ console.log('\n📊 Coldstart Report');
125
+ console.log(`${'─'.repeat(50)}`);
126
+
127
+ if (targets.length > 0) {
128
+ console.log(`\n Targets: ${targets.map(t => t.name || t).join(', ')}`);
129
+ }
130
+
122
131
  if (Object.keys(langStats).length > 0) {
123
- const _langParts = Object.entries(langStats)
132
+ const langParts = Object.entries(langStats)
124
133
  .sort((a, b) => b[1] - a[1])
125
134
  .slice(0, 5)
126
135
  .map(([ext, count]) => `${ext}(${count})`);
136
+ console.log(` Languages: ${langParts.join(', ')}`);
127
137
  }
128
138
 
129
139
  // AST 分析
130
140
  if (astSummary) {
131
141
  if (astSummary.metrics) {
142
+ console.log(` AST Metrics: ${JSON.stringify(astSummary.metrics)}`);
132
143
  }
133
144
  }
134
145
 
135
146
  // SPM 依赖
136
147
  if (report.phases?.spmDependencyGraph) {
148
+ const spm = report.phases.spmDependencyGraph;
149
+ console.log(` SPM Dependencies: ${spm.packageCount ?? '?'} packages`);
137
150
  }
138
151
 
139
152
  // Guard 审计
140
153
  if (guardSummary) {
154
+ console.log(` Guard: ${guardSummary.totalViolations ?? guardSummary.total ?? '?'} violations (${guardSummary.errors ?? '?'} errors, ${guardSummary.warnings ?? '?'} warnings)`);
141
155
  }
142
156
 
143
157
  // 维度分析框架
144
158
  if (framework.dimensions) {
159
+ console.log('\n Analysis Dimensions:');
145
160
  for (const dim of framework.dimensions) {
146
- const _type = dim.skillWorthy ? (dim.dualOutput ? 'Dual' : 'Skill') : 'Candidate';
161
+ const type = dim.skillWorthy ? (dim.dualOutput ? 'Dual' : 'Skill') : 'Candidate';
162
+ console.log(` ${type.padEnd(10)} ${dim.id || dim.name || '?'}`);
147
163
  }
148
164
  }
149
165
  if (result.bootstrapSession) {
150
- const _session = result.bootstrapSession;
166
+ const session = result.bootstrapSession;
167
+ console.log(`\n Session: ${session.id || 'N/A'} (${session.status || 'unknown'})`);
151
168
  }
169
+ console.log('');
152
170
  }
153
171
 
154
172
  // 等待模式: 轮询 BootstrapTaskManager 直到所有维度完成
@@ -190,11 +208,14 @@ program
190
208
 
191
209
  // 输出各维度结果
192
210
  if (!opts.json) {
193
- const _succeeded = sessionStatus.tasks.filter((t) => t.status === 'done').length;
194
- const _failed = sessionStatus.tasks.filter((t) => t.status === 'error').length;
211
+ const succeeded = sessionStatus.tasks.filter((t) => t.status === 'done').length;
212
+ const failed = sessionStatus.tasks.filter((t) => t.status === 'error').length;
213
+ console.log(`\n Results: ${succeeded} succeeded, ${failed} failed`);
195
214
  for (const t of sessionStatus.tasks) {
196
- const _icon = t.status === 'done' ? '✅' : '❌';
215
+ const icon = t.status === 'done' ? '✅' : '❌';
216
+ console.log(` ${icon} ${t.meta?.label || t.id}`);
197
217
  }
218
+ console.log('');
198
219
  }
199
220
  break;
200
221
  }
@@ -207,6 +228,7 @@ program
207
228
  waitSpinner.warn('AI 填充超时(10 分钟),可通过 asd ui 查看进度');
208
229
  }
209
230
  } else if (!opts.json) {
231
+ console.log(' 💡 AI 填充已在后台运行。用 --wait 等待完成,或用 asd ui 查看进度。');
210
232
  }
211
233
 
212
234
  await bootstrap.shutdown();
@@ -232,8 +254,10 @@ program
232
254
  .action(async (target, opts) => {
233
255
  const projectRoot = resolve(opts.dir);
234
256
  if (target) {
257
+ console.log(`Target: ${target}`);
235
258
  }
236
259
  if (opts.dryRun) {
260
+ console.log('ℹ️ Dry-run mode: no Recipes will be published');
237
261
  }
238
262
 
239
263
  try {
@@ -253,15 +277,25 @@ program
253
277
  spinner.stop();
254
278
 
255
279
  if (opts.json) {
280
+ console.log(JSON.stringify(report, null, 2));
256
281
  } else {
282
+ console.log(`\n📝 AI Scan Report`);
283
+ console.log(` Files scanned: ${report.files}`);
284
+ console.log(` Published: ${report.published}`);
285
+ console.log(` Skipped: ${report.skipped || 0}`);
257
286
  if (report.errors.length > 0) {
258
- for (const _err of report.errors.slice(0, 10)) {
287
+ console.log(` Errors: ${report.errors.length}`);
288
+ for (const err of report.errors.slice(0, 10)) {
289
+ console.log(` ❌ ${err}`);
259
290
  }
260
291
  if (report.errors.length > 10) {
292
+ console.log(` ... and ${report.errors.length - 10} more`);
261
293
  }
262
294
  }
263
295
  if (!opts.dryRun && report.published > 0) {
296
+ console.log(`\n ✅ ${report.published} Recipes published successfully.`);
264
297
  }
298
+ console.log('');
265
299
  }
266
300
 
267
301
  await bootstrap.shutdown();
@@ -294,13 +328,18 @@ program
294
328
  });
295
329
 
296
330
  if (results.items.length === 0) {
331
+ console.log('No results found.');
297
332
  } else {
333
+ console.log(`\n🔍 ${results.items.length} result(s) for "${query}"\n`);
298
334
  for (const item of results.items) {
299
- const _badge = item.type === 'recipe' ? '📘' : item.type === 'solution' ? '💡' : '🛡️';
300
- const _score = item.score ? ` [${item.score}]` : '';
335
+ const badge = item.type === 'recipe' ? '📘' : item.type === 'solution' ? '💡' : '🛡️';
336
+ const score = item.score ? ` [${(item.score * 100).toFixed(0)}%]` : '';
337
+ console.log(` ${badge} ${item.title || item.trigger || item.id}${score}`);
301
338
  if (item.description) {
339
+ console.log(` ${item.description.slice(0, 100)}`);
302
340
  }
303
341
  }
342
+ console.log('');
304
343
  }
305
344
 
306
345
  await bootstrap.shutdown();
@@ -335,15 +374,20 @@ program
335
374
  const violations = engine.checkCode(code, language, { scope: opts.scope });
336
375
 
337
376
  if (opts.json) {
377
+ console.log(JSON.stringify({ violations, summary: { total: violations.length, errors: violations.filter(v => v.severity === 'error').length, warnings: violations.filter(v => v.severity === 'warning').length } }, null, 2));
338
378
  } else if (violations.length === 0) {
379
+ console.log('✅ No violations found.');
339
380
  } else {
340
- const _errors = violations.filter((v) => v.severity === 'error');
341
- const _warnings = violations.filter((v) => v.severity === 'warning');
381
+ const errors = violations.filter((v) => v.severity === 'error');
382
+ const warnings = violations.filter((v) => v.severity === 'warning');
383
+ console.log(`\n🔍 Guard: ${violations.length} violation(s) — ${errors.length} error(s), ${warnings.length} warning(s)\n`);
342
384
  for (const v of violations) {
343
- const _icon = v.severity === 'error' ? '❌' : '⚠️';
344
- if (v.snippet) {
345
- }
385
+ const icon = v.severity === 'error' ? '❌' : v.severity === 'warning' ? '⚠️' : 'ℹ️';
386
+ console.log(` ${icon} [${v.ruleId}] ${v.message}`);
387
+ if (v.line) console.log(` Line ${v.line}: ${v.snippet || ''}`);
388
+ if (v.fixSuggestion) console.log(` 💡 Fix: ${v.fixSuggestion}`);
346
389
  }
390
+ console.log('');
347
391
  }
348
392
 
349
393
  await bootstrap.shutdown();
@@ -388,7 +432,9 @@ program
388
432
  if (opts.output) {
389
433
  const { writeFileSync } = await import('node:fs');
390
434
  writeFileSync(opts.output, output, 'utf8');
435
+ console.log(`Report written to ${opts.output}`);
391
436
  } else {
437
+ console.log(output);
392
438
  }
393
439
  } else {
394
440
  reporter.printReport(report, { format: opts.report });
@@ -474,16 +520,23 @@ program
474
520
  const { summary } = result;
475
521
 
476
522
  if (opts.json) {
523
+ console.log(JSON.stringify({ files: result.files, summary }, null, 2));
477
524
  } else if (summary.totalViolations === 0) {
525
+ console.log(`✅ ${sourceFiles.length} staged file(s) checked — no violations.`);
478
526
  } else {
527
+ console.log(`\n🔍 Guard (staged): ${summary.totalViolations} violation(s) in ${sourceFiles.length} file(s)\n`);
479
528
  const filesWithIssues = result.files.filter((f) => f.summary.total > 0);
480
529
  for (const file of filesWithIssues.slice(0, 10)) {
530
+ console.log(` 📄 ${file.filePath || file.path}`);
481
531
  for (const v of file.violations.slice(0, 5)) {
482
- const _icon = v.severity === 'error' ? '❌' : '⚠️';
532
+ const icon = v.severity === 'error' ? '❌' : '⚠️';
533
+ console.log(` ${icon} [${v.ruleId}] ${v.message}`);
483
534
  }
484
535
  if (file.violations.length > 5) {
536
+ console.log(` ... and ${file.violations.length - 5} more`);
485
537
  }
486
538
  }
539
+ console.log('');
487
540
  }
488
541
 
489
542
  await bootstrap.shutdown();
@@ -800,19 +853,35 @@ program
800
853
  .command('status')
801
854
  .description('检查环境状态')
802
855
  .action(async () => {
856
+ console.log('\n AutoSnippet Environment Status');
857
+ console.log(` ${'─'.repeat(40)}`);
858
+
803
859
  // AI 配置
804
860
  const { getAiConfigInfo } = await import('../lib/external/ai/AiFactory.js');
805
- const _aiInfo = getAiConfigInfo();
861
+ const aiInfo = getAiConfigInfo();
862
+ console.log(` AI Provider: ${aiInfo.provider || 'not configured'}`);
863
+ if (aiInfo.model) console.log(` AI Model: ${aiInfo.model}`);
806
864
 
807
865
  // 检查数据库
808
- const _dbPath = join(process.cwd(), '.autosnippet', 'autosnippet.db');
866
+ const dbPath = join(process.cwd(), '.autosnippet', 'autosnippet.db');
867
+ const dbExists = existsSync(dbPath);
868
+ console.log(` Database: ${dbExists ? '✅ ' + dbPath : '❌ not found'}`);
869
+
870
+ // 检查 .autosnippet 目录
871
+ const asdDir = join(process.cwd(), '.autosnippet');
872
+ console.log(` Workspace: ${existsSync(asdDir) ? '✅ .autosnippet/' : '❌ not initialized (run asd setup)'}`);
809
873
 
810
874
  // 检查依赖
875
+ console.log(' Dependencies:');
811
876
  for (const dep of ['better-sqlite3', 'commander', 'express']) {
812
877
  try {
813
878
  await import(dep);
814
- } catch {}
879
+ console.log(` ✅ ${dep}`);
880
+ } catch {
881
+ console.log(` ❌ ${dep} (missing)`);
882
+ }
815
883
  }
884
+ console.log('');
816
885
  });
817
886
 
818
887
  // ─────────────────────────────────────────────────────
@@ -849,13 +918,150 @@ program
849
918
  try {
850
919
  const pipeline = container.get('cursorDeliveryPipeline');
851
920
  const result = await pipeline.deliver();
921
+ console.log('\n Cursor Rules Delivery');
922
+ console.log(` ${'─'.repeat(40)}`);
923
+ console.log(` Channel A: ${result.channelA?.count ?? '?'} always-on rules`);
924
+ console.log(` Channel B: ${result.channelB?.count ?? Object.keys(result.channelB?.topics || {}).length} topic rules`);
925
+ console.log(` Channel C: ${result.channelC?.count ?? '?'} skills (${result.channelC?.errors ?? 0} errors)`);
852
926
  if (result.channelC.errors > 0) {
927
+ console.log(` ⚠️ ${result.channelC.errors} skill(s) failed to deliver`);
853
928
  }
854
929
 
855
930
  if (opts.verbose && result.channelB.topics) {
856
- for (const [_topic, _info] of Object.entries(result.channelB.topics)) {
931
+ console.log('\n Channel B Topics:');
932
+ for (const [topic, info] of Object.entries(result.channelB.topics)) {
933
+ console.log(` ${topic}: ${info.count ?? info.rules?.length ?? '?'} rules`);
934
+ }
935
+ }
936
+ console.log('');
937
+ } finally {
938
+ await bootstrap.shutdown?.();
939
+ }
940
+ });
941
+
942
+ // ─────────────────────────────────────────────────────
943
+ // task 命令 — TaskGraph CLI 管理
944
+ // ─────────────────────────────────────────────────────
945
+ const taskCmd = program
946
+ .command('task')
947
+ .description('TaskGraph 任务管理(列表 / 就绪 / 上下文恢复 / 统计)');
948
+
949
+ taskCmd
950
+ .command('list')
951
+ .description('列出任务')
952
+ .option('-d, --dir <path>', '项目目录', '.')
953
+ .option('-s, --status <status>', '按状态过滤(open/in_progress/closed/deferred)')
954
+ .option('-l, --limit <n>', '最大条数', '20')
955
+ .action(async (opts) => {
956
+ const projectRoot = resolve(opts.dir);
957
+ const { bootstrap, container } = await initContainer({ projectRoot });
958
+ try {
959
+ const svc = container.get('taskGraphService');
960
+ const filters = {};
961
+ if (opts.status) filters.status = opts.status;
962
+ const tasks = await svc.list(filters, { limit: parseInt(opts.limit, 10) });
963
+ if (tasks.length === 0) {
964
+ console.log('No tasks found.');
965
+ } else {
966
+ console.log(`\n ID Status Priority Title`);
967
+ console.log(` ${'─'.repeat(70)}`);
968
+ for (const t of tasks) {
969
+ const j = t.toJSON ? t.toJSON() : t;
970
+ const id = (j.id || '').padEnd(16);
971
+ const status = (j.status || '').padEnd(13);
972
+ const pri = String(j.priority ?? '-').padEnd(9);
973
+ console.log(` ${id} ${status} ${pri} ${j.title}`);
974
+ }
975
+ console.log(`\n Total: ${tasks.length}\n`);
976
+ }
977
+ } finally {
978
+ await bootstrap.shutdown?.();
979
+ }
980
+ });
981
+
982
+ taskCmd
983
+ .command('ready')
984
+ .description('显示就绪任务(带知识上下文)')
985
+ .option('-d, --dir <path>', '项目目录', '.')
986
+ .option('-l, --limit <n>', '最大条数', '5')
987
+ .action(async (opts) => {
988
+ const projectRoot = resolve(opts.dir);
989
+ const { bootstrap, container } = await initContainer({ projectRoot });
990
+ try {
991
+ const svc = container.get('taskGraphService');
992
+ const tasks = await svc.ready({
993
+ limit: parseInt(opts.limit, 10),
994
+ withKnowledge: true,
995
+ });
996
+ if (tasks.length === 0) {
997
+ console.log('No ready tasks.');
998
+ } else {
999
+ for (const t of tasks) {
1000
+ const j = t.toJSON ? t.toJSON() : t;
1001
+ console.log(`\n ▸ ${j.id} — ${j.title} (P${j.priority ?? '?'})`);
1002
+ if (t.knowledgeContext?.relatedKnowledge?.length) {
1003
+ console.log(` Knowledge: ${t.knowledgeContext.relatedKnowledge.map(k => k.title).join(', ')}`);
1004
+ }
1005
+ if (t.knowledgeContext?.guardRules?.length) {
1006
+ console.log(` Guard: ${t.knowledgeContext.guardRules.map(r => r.title).join(', ')}`);
1007
+ }
1008
+ }
1009
+ console.log('');
1010
+ }
1011
+ } finally {
1012
+ await bootstrap.shutdown?.();
1013
+ }
1014
+ });
1015
+
1016
+ taskCmd
1017
+ .command('prime')
1018
+ .description('恢复 TaskGraph 会话上下文(等同 MCP prime 操作)')
1019
+ .option('-d, --dir <path>', '项目目录', '.')
1020
+ .action(async (opts) => {
1021
+ const projectRoot = resolve(opts.dir);
1022
+ const { bootstrap, container } = await initContainer({ projectRoot });
1023
+ try {
1024
+ const svc = container.get('taskGraphService');
1025
+ const result = await svc.prime({ withKnowledge: true });
1026
+ console.log(`\n TaskGraph Prime`);
1027
+ console.log(` ${'─'.repeat(40)}`);
1028
+ console.log(` In Progress: ${result.inProgress.length}`);
1029
+ console.log(` Ready: ${result.ready.length}`);
1030
+ console.log(` Stats: ${JSON.stringify(result.stats)}`);
1031
+ if (result.inProgress.length > 0) {
1032
+ console.log(`\n ▸ In Progress:`);
1033
+ for (const t of result.inProgress) {
1034
+ console.log(` ${t.id} — ${t.title}`);
1035
+ }
1036
+ }
1037
+ if (result.ready.length > 0) {
1038
+ console.log(`\n ▸ Ready:`);
1039
+ for (const t of result.ready) {
1040
+ console.log(` ${t.id} — ${t.title}`);
857
1041
  }
858
1042
  }
1043
+ console.log('');
1044
+ } finally {
1045
+ await bootstrap.shutdown?.();
1046
+ }
1047
+ });
1048
+
1049
+ taskCmd
1050
+ .command('stats')
1051
+ .description('TaskGraph 统计信息')
1052
+ .option('-d, --dir <path>', '项目目录', '.')
1053
+ .action(async (opts) => {
1054
+ const projectRoot = resolve(opts.dir);
1055
+ const { bootstrap, container } = await initContainer({ projectRoot });
1056
+ try {
1057
+ const svc = container.get('taskGraphService');
1058
+ const stats = await svc.stats();
1059
+ console.log(`\n TaskGraph Statistics`);
1060
+ console.log(` ${'─'.repeat(30)}`);
1061
+ for (const [key, val] of Object.entries(stats)) {
1062
+ console.log(` ${key.padEnd(15)} ${val}`);
1063
+ }
1064
+ console.log('');
859
1065
  } finally {
860
1066
  await bootstrap.shutdown?.();
861
1067
  }
@@ -934,6 +1140,7 @@ program
934
1140
  const { KnowledgeSyncService } = await import('../lib/cli/KnowledgeSyncService.js');
935
1141
  const syncService = new KnowledgeSyncService(projectRoot);
936
1142
  if (opts.dryRun) {
1143
+ console.log('ℹ️ Dry-run mode: no changes will be written');
937
1144
  }
938
1145
 
939
1146
  // 通过 Bootstrap 打开目标项目的 DB
@@ -956,15 +1163,27 @@ program
956
1163
  force: opts.force,
957
1164
  });
958
1165
 
1166
+ console.log('\n Knowledge Sync Report');
1167
+ console.log(` ${'─'.repeat(40)}`);
1168
+ console.log(` Created: ${report.created ?? 0}`);
1169
+ console.log(` Updated: ${report.updated ?? 0}`);
1170
+ console.log(` Unchanged: ${report.unchanged ?? 0}`);
1171
+ console.log(` Deleted: ${report.deleted ?? 0}`);
1172
+
959
1173
  if (report.violations.length > 0) {
960
- for (const _v of report.violations) {
1174
+ console.log(`\n ⚠️ Violations (${report.violations.length}):`);
1175
+ for (const v of report.violations) {
1176
+ console.log(` ❌ ${v.file || v.id}: ${v.message || v}`);
961
1177
  }
962
1178
  }
963
1179
 
964
1180
  if (report.orphaned.length > 0) {
965
- for (const _id of report.orphaned) {
1181
+ console.log(`\n 👻 Orphaned entries (${report.orphaned.length}):`);
1182
+ for (const id of report.orphaned) {
1183
+ console.log(` ${id}`);
966
1184
  }
967
1185
  }
1186
+ console.log('');
968
1187
  } finally {
969
1188
  await bootstrap.shutdown?.();
970
1189
  }