autosnippet 3.1.15 → 3.2.2
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/README.md +1 -0
- package/bin/cli.js +242 -23
- package/dashboard/dist/assets/{icons-CC5R_iwL.js → icons-18VxiaCT.js} +108 -98
- package/dashboard/dist/assets/index-BJiuaVPD.css +1 -0
- package/dashboard/dist/assets/{index-WmnJCXq4.js → index-CRH5Umim.js} +50 -50
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/SetupService.js +152 -21
- package/lib/domain/task/Task.js +214 -0
- package/lib/domain/task/TaskDependency.js +48 -0
- package/lib/domain/task/TaskIdGenerator.js +83 -0
- package/lib/domain/task/index.js +6 -0
- package/lib/external/mcp/McpServer.js +4 -4
- package/lib/external/mcp/handlers/task.js +295 -0
- package/lib/external/mcp/tools.js +100 -3
- package/lib/http/HttpServer.js +8 -0
- package/lib/http/routes/guard.js +283 -0
- package/lib/http/routes/task.js +282 -0
- package/lib/infrastructure/config/Paths.js +18 -8
- package/lib/infrastructure/database/migrations/002_add_tasks.js +88 -0
- package/lib/injection/ServiceContainer.js +58 -0
- package/lib/repository/task/TaskRepository.impl.js +398 -0
- package/lib/service/cursor/AgentInstructionsGenerator.js +28 -9
- package/lib/service/cursor/CursorDeliveryPipeline.js +42 -20
- package/lib/service/cursor/KnowledgeCompressor.js +40 -0
- package/lib/service/cursor/TokenBudget.js +2 -2
- package/lib/service/guard/GuardFeedbackLoop.js +17 -2
- package/lib/service/knowledge/KnowledgeService.js +6 -0
- package/lib/service/task/TaskGraphService.js +410 -0
- package/lib/service/task/TaskKnowledgeBridge.js +86 -0
- package/lib/service/task/TaskReadyEngine.js +127 -0
- package/lib/shared/constants.js +3 -3
- package/package.json +1 -1
- package/skills/autosnippet-intent/SKILL.md +4 -1
- package/skills/autosnippet-recipes/SKILL.md +17 -2
- package/templates/claude-hooks.yaml +19 -0
- package/templates/copilot-instructions.md +33 -1
- package/templates/cursor-rules/autosnippet-conventions.mdc +12 -0
- package/templates/cursor-rules/autosnippet-workflow.mdc +43 -0
- package/templates/guard-ci.yml +1 -0
- package/templates/pre-commit-guard.sh +2 -1
- package/dashboard/dist/assets/index-6iola4rb.css +0 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
194
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
300
|
-
const
|
|
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
|
|
341
|
-
const
|
|
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
|
|
344
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|