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.
Files changed (41) 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-WmnJCXq4.js → index-CRH5Umim.js} +50 -50
  6. package/dashboard/dist/index.html +3 -3
  7. package/lib/cli/SetupService.js +152 -21
  8. package/lib/domain/task/Task.js +214 -0
  9. package/lib/domain/task/TaskDependency.js +48 -0
  10. package/lib/domain/task/TaskIdGenerator.js +83 -0
  11. package/lib/domain/task/index.js +6 -0
  12. package/lib/external/mcp/McpServer.js +4 -4
  13. package/lib/external/mcp/handlers/task.js +295 -0
  14. package/lib/external/mcp/tools.js +100 -3
  15. package/lib/http/HttpServer.js +8 -0
  16. package/lib/http/routes/guard.js +283 -0
  17. package/lib/http/routes/task.js +282 -0
  18. package/lib/infrastructure/config/Paths.js +18 -8
  19. package/lib/infrastructure/database/migrations/002_add_tasks.js +88 -0
  20. package/lib/injection/ServiceContainer.js +58 -0
  21. package/lib/repository/task/TaskRepository.impl.js +398 -0
  22. package/lib/service/cursor/AgentInstructionsGenerator.js +28 -9
  23. package/lib/service/cursor/CursorDeliveryPipeline.js +42 -20
  24. package/lib/service/cursor/KnowledgeCompressor.js +40 -0
  25. package/lib/service/cursor/TokenBudget.js +2 -2
  26. package/lib/service/guard/GuardFeedbackLoop.js +17 -2
  27. package/lib/service/knowledge/KnowledgeService.js +6 -0
  28. package/lib/service/task/TaskGraphService.js +410 -0
  29. package/lib/service/task/TaskKnowledgeBridge.js +86 -0
  30. package/lib/service/task/TaskReadyEngine.js +127 -0
  31. package/lib/shared/constants.js +3 -3
  32. package/package.json +1 -1
  33. package/skills/autosnippet-intent/SKILL.md +4 -1
  34. package/skills/autosnippet-recipes/SKILL.md +17 -2
  35. package/templates/claude-hooks.yaml +19 -0
  36. package/templates/copilot-instructions.md +33 -1
  37. package/templates/cursor-rules/autosnippet-conventions.mdc +12 -0
  38. package/templates/cursor-rules/autosnippet-workflow.mdc +43 -0
  39. package/templates/guard-ci.yml +1 -0
  40. package/templates/pre-commit-guard.sh +2 -1
  41. package/dashboard/dist/assets/index-6iola4rb.css +0 -1
@@ -0,0 +1,295 @@
1
+ /**
2
+ * MCP Handler — TaskGraph 任务管理
3
+ *
4
+ * 操作路由:
5
+ * create / decompose / claim / close / fail / defer / progress
6
+ * ready / prime / show / list / blocked / dep_add / dep_tree / stats
7
+ */
8
+
9
+ import { envelope } from '../envelope.js';
10
+
11
+ /**
12
+ * 统一入口
13
+ * @param {object} ctx — { container }
14
+ * @param {object} args — { operation, ...params }
15
+ */
16
+ export async function taskHandler(ctx, args) {
17
+ const taskService = ctx.container.get('taskGraphService');
18
+
19
+ switch (args.operation) {
20
+ case 'create':
21
+ return _create(taskService, args);
22
+ case 'ready':
23
+ return _ready(taskService, args);
24
+ case 'claim':
25
+ return _claim(taskService, args);
26
+ case 'close':
27
+ return _close(taskService, args);
28
+ case 'fail':
29
+ return _fail(taskService, args);
30
+ case 'defer':
31
+ return _defer(taskService, args);
32
+ case 'progress':
33
+ return _progress(taskService, args);
34
+ case 'prime':
35
+ return _prime(taskService);
36
+ case 'decompose':
37
+ return _decompose(taskService, args);
38
+ case 'show':
39
+ return _show(taskService, args);
40
+ case 'list':
41
+ return _list(taskService, args);
42
+ case 'blocked':
43
+ return _blocked(taskService);
44
+ case 'dep_add':
45
+ return _depAdd(taskService, args);
46
+ case 'dep_tree':
47
+ return _depTree(taskService, args);
48
+ case 'stats':
49
+ return _stats(taskService);
50
+ default:
51
+ return envelope({
52
+ success: false,
53
+ message: `Unknown operation: ${args.operation}`,
54
+ meta: { tool: 'autosnippet_task' },
55
+ });
56
+ }
57
+ }
58
+
59
+ // ── create ──
60
+
61
+ async function _create(svc, args) {
62
+ if (!args.title) {
63
+ return envelope({ success: false, message: 'title is required', meta: { tool: 'autosnippet_task' } });
64
+ }
65
+ const { task, isDuplicate } = await svc.create({
66
+ title: args.title,
67
+ description: args.description || '',
68
+ design: args.design || '',
69
+ acceptance: args.acceptance || '',
70
+ priority: args.priority ?? 2,
71
+ taskType: args.taskType || 'task',
72
+ parentId: args.parentId || null,
73
+ });
74
+ return envelope({
75
+ success: true,
76
+ data: task.toJSON(),
77
+ message: isDuplicate
78
+ ? `⚠ Duplicate detected: ${task.id} already exists`
79
+ : `Created ${task.id}: ${task.title}`,
80
+ meta: { tool: 'autosnippet_task' },
81
+ });
82
+ }
83
+
84
+ // ── ready ──
85
+
86
+ async function _ready(svc, args) {
87
+ const tasks = await svc.ready({
88
+ limit: args.limit || 10,
89
+ withKnowledge: args.withKnowledge !== false,
90
+ });
91
+ return envelope({
92
+ success: true,
93
+ data: tasks.map((t) => (t.toJSON ? t.toJSON() : t)),
94
+ message: `${tasks.length} task(s) ready`,
95
+ meta: { tool: 'autosnippet_task' },
96
+ });
97
+ }
98
+
99
+ // ── claim ──
100
+
101
+ async function _claim(svc, args) {
102
+ if (!args.id) {
103
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
104
+ }
105
+ const task = await svc.claim(args.id);
106
+ return envelope({
107
+ success: true,
108
+ data: task.toJSON(),
109
+ message: `Claimed ${args.id}`,
110
+ meta: { tool: 'autosnippet_task' },
111
+ });
112
+ }
113
+
114
+ // ── close ──
115
+
116
+ async function _close(svc, args) {
117
+ if (!args.id) {
118
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
119
+ }
120
+ const { task, newlyReady } = await svc.close(args.id, args.reason || 'Completed');
121
+ return envelope({
122
+ success: true,
123
+ data: {
124
+ closed: task.toJSON(),
125
+ newlyReady,
126
+ },
127
+ message: `Closed ${args.id}. ${newlyReady.length} task(s) newly ready.`,
128
+ meta: { tool: 'autosnippet_task' },
129
+ });
130
+ }
131
+
132
+ // ── fail ──
133
+
134
+ async function _fail(svc, args) {
135
+ if (!args.id) {
136
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
137
+ }
138
+ const task = await svc.fail(args.id, args.reason || 'Agent execution failed');
139
+ return envelope({
140
+ success: true,
141
+ data: task.toJSON(),
142
+ message: `Failed ${args.id} (attempt #${task.failCount}): ${task.lastFailReason}`,
143
+ meta: { tool: 'autosnippet_task' },
144
+ });
145
+ }
146
+
147
+ // ── defer ──
148
+
149
+ async function _defer(svc, args) {
150
+ if (!args.id) {
151
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
152
+ }
153
+ const task = await svc.defer(args.id, args.reason || '');
154
+ return envelope({
155
+ success: true,
156
+ data: task.toJSON(),
157
+ message: `Deferred ${args.id}`,
158
+ meta: { tool: 'autosnippet_task' },
159
+ });
160
+ }
161
+
162
+ // ── progress ──
163
+
164
+ async function _progress(svc, args) {
165
+ if (!args.id) {
166
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
167
+ }
168
+ const note = args.reason || args.description || '';
169
+ const task = await svc.progress(args.id, note);
170
+ return envelope({
171
+ success: true,
172
+ data: task.toJSON(),
173
+ message: `Progress updated for ${args.id}`,
174
+ meta: { tool: 'autosnippet_task' },
175
+ });
176
+ }
177
+
178
+ // ── prime — 会话恢复 ──
179
+
180
+ async function _prime(svc) {
181
+ const result = await svc.prime({ withKnowledge: true });
182
+ return envelope({
183
+ success: true,
184
+ data: result,
185
+ message: `${result.inProgress.length} in-progress, ${result.ready.length} ready, ${result.stats.total} total`,
186
+ meta: { tool: 'autosnippet_task' },
187
+ });
188
+ }
189
+
190
+ // ── decompose ──
191
+
192
+ async function _decompose(svc, args) {
193
+ if (!args.id) {
194
+ return envelope({ success: false, message: 'Epic id is required', meta: { tool: 'autosnippet_task' } });
195
+ }
196
+ if (!args.subtasks || !Array.isArray(args.subtasks) || args.subtasks.length === 0) {
197
+ return envelope({ success: false, message: 'subtasks array is required', meta: { tool: 'autosnippet_task' } });
198
+ }
199
+ const tasks = await svc.decompose(args.id, args.subtasks);
200
+ return envelope({
201
+ success: true,
202
+ data: tasks.map((t) => (t.toJSON ? t.toJSON() : t)),
203
+ message: `Decomposed ${args.id} into ${tasks.length} subtasks`,
204
+ meta: { tool: 'autosnippet_task' },
205
+ });
206
+ }
207
+
208
+ // ── show ──
209
+
210
+ async function _show(svc, args) {
211
+ if (!args.id) {
212
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
213
+ }
214
+ const task = await svc.show(args.id);
215
+ if (!task) {
216
+ return envelope({ success: false, message: `Task not found: ${args.id}`, meta: { tool: 'autosnippet_task' } });
217
+ }
218
+ return envelope({
219
+ success: true,
220
+ data: task.toJSON(),
221
+ meta: { tool: 'autosnippet_task' },
222
+ });
223
+ }
224
+
225
+ // ── list ──
226
+
227
+ async function _list(svc, args) {
228
+ const filters = {};
229
+ if (args.status) filters.status = args.status;
230
+ if (args.taskType) filters.taskType = args.taskType;
231
+
232
+ const tasks = await svc.list(filters, { limit: args.limit || 20 });
233
+ return envelope({
234
+ success: true,
235
+ data: tasks.map((t) => t.toJSON()),
236
+ message: `${tasks.length} task(s)`,
237
+ meta: { tool: 'autosnippet_task' },
238
+ });
239
+ }
240
+
241
+ // ── blocked ──
242
+
243
+ async function _blocked(svc) {
244
+ const tasks = await svc.blocked();
245
+ return envelope({
246
+ success: true,
247
+ data: tasks,
248
+ message: `${tasks.length} blocked task(s)`,
249
+ meta: { tool: 'autosnippet_task' },
250
+ });
251
+ }
252
+
253
+ // ── dep_add ──
254
+
255
+ async function _depAdd(svc, args) {
256
+ if (!args.id || !args.dependsOn) {
257
+ return envelope({
258
+ success: false,
259
+ message: 'id and dependsOn are required',
260
+ meta: { tool: 'autosnippet_task' },
261
+ });
262
+ }
263
+ await svc.addDependency(args.id, args.dependsOn, args.depType || 'blocks');
264
+ return envelope({
265
+ success: true,
266
+ message: `${args.id} ${args.depType || 'blocks'} ${args.dependsOn}`,
267
+ meta: { tool: 'autosnippet_task' },
268
+ });
269
+ }
270
+
271
+ // ── dep_tree ──
272
+
273
+ async function _depTree(svc, args) {
274
+ if (!args.id) {
275
+ return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
276
+ }
277
+ const tree = await svc.depTree(args.id);
278
+ return envelope({
279
+ success: true,
280
+ data: tree,
281
+ message: `${tree.length} node(s) in dependency tree`,
282
+ meta: { tool: 'autosnippet_task' },
283
+ });
284
+ }
285
+
286
+ // ── stats ──
287
+
288
+ async function _stats(svc) {
289
+ const stats = await svc.stats();
290
+ return envelope({
291
+ success: true,
292
+ data: stats,
293
+ meta: { tool: 'autosnippet_task' },
294
+ });
295
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
- * MCP 工具定义 — V3 整合版 (15 agent + 4 admin = 19 工具)
2
+ * MCP 工具定义 — V3 整合版 (16 agent + 4 admin = 20 工具)
3
3
  *
4
- * 从 39 → 19 工具(参数路由合并同类工具 + 外部 Agent 冷启动新架构)。
4
+ * 从 39 → 20 工具(参数路由合并同类工具 + 外部 Agent 冷启动新架构 + TaskGraph)。
5
5
  * 每个工具声明增加 tier 字段(agent / admin)。
6
6
  * tools.js 只包含 JSON Schema 声明 + Gateway 映射,不含业务逻辑。
7
7
  *
@@ -44,6 +44,20 @@ export const TOOL_GATEWAY_MAP = {
44
44
  autosnippet_submit_knowledge: { action: 'knowledge:create', resource: 'knowledge' },
45
45
  autosnippet_submit_knowledge_batch: { action: 'knowledge:create', resource: 'knowledge' },
46
46
  autosnippet_save_document: { action: 'knowledge:create', resource: 'knowledge' },
47
+ // task 写操作(create/claim/close/fail/defer/decompose/dep_add)
48
+ autosnippet_task: {
49
+ resolver: (args) =>
50
+ ({
51
+ create: { action: 'task:create', resource: 'tasks' },
52
+ claim: { action: 'task:update', resource: 'tasks' },
53
+ close: { action: 'task:update', resource: 'tasks' },
54
+ fail: { action: 'task:update', resource: 'tasks' },
55
+ defer: { action: 'task:update', resource: 'tasks' },
56
+ progress: { action: 'task:update', resource: 'tasks' },
57
+ decompose: { action: 'task:create', resource: 'tasks' },
58
+ dep_add: { action: 'task:update', resource: 'tasks' },
59
+ })[args?.operation] || null, // ready/prime/show/list/blocked/dep_tree/stats 只读
60
+ },
47
61
  // admin 工具
48
62
  autosnippet_enrich_candidates: { action: 'knowledge:update', resource: 'knowledge' },
49
63
  autosnippet_knowledge_lifecycle: { action: 'knowledge:update', resource: 'knowledge' },
@@ -53,7 +67,7 @@ export const TOOL_GATEWAY_MAP = {
53
67
 
54
68
  export const TOOLS = [
55
69
  // ══════════════════════════════════════════════════════
56
- // Tier: agent — Agent 核心工具集 (15 个)
70
+ // Tier: agent — Agent 核心工具集 (16 个)
57
71
  // ══════════════════════════════════════════════════════
58
72
 
59
73
  // 1. 健康检查
@@ -514,6 +528,89 @@ export const TOOLS = [
514
528
  inputSchema: { type: 'object', properties: {}, required: [] },
515
529
  },
516
530
 
531
+ // 13. TaskGraph 任务管理
532
+ {
533
+ name: 'autosnippet_task',
534
+ tier: 'agent',
535
+ description:
536
+ '任务图管理。创建/查询/认领/关闭任务,管理依赖关系。' +
537
+ '用于 Agent 自主拆解和执行复杂多步骤工作。' +
538
+ 'ready 返回任务 + 相关知识上下文。prime 恢复会话状态。',
539
+ inputSchema: {
540
+ type: 'object',
541
+ properties: {
542
+ operation: {
543
+ type: 'string',
544
+ enum: [
545
+ 'create',
546
+ 'ready',
547
+ 'claim',
548
+ 'close',
549
+ 'fail',
550
+ 'defer',
551
+ 'progress',
552
+ 'show',
553
+ 'list',
554
+ 'blocked',
555
+ 'decompose',
556
+ 'dep_add',
557
+ 'dep_tree',
558
+ 'prime',
559
+ 'stats',
560
+ ],
561
+ description: '操作类型',
562
+ },
563
+ title: { type: 'string', description: '任务标题(create)' },
564
+ description: { type: 'string', description: '任务描述(create/progress)' },
565
+ design: { type: 'string', description: '设计说明(create)' },
566
+ acceptance: { type: 'string', description: '验收标准(create)' },
567
+ priority: { type: 'number', description: '优先级 0-4, 0=最高(create)' },
568
+ taskType: {
569
+ type: 'string',
570
+ enum: ['epic', 'task', 'bug', 'chore'],
571
+ description: '任务类型(create)',
572
+ },
573
+ parentId: { type: 'string', description: '父任务 ID(create 子任务)' },
574
+ id: { type: 'string', description: '任务 ID(claim/close/fail/defer/show/dep_add/dep_tree/progress)' },
575
+ reason: { type: 'string', description: '原因(close/fail/defer)' },
576
+ dependsOn: { type: 'string', description: '依赖的任务 ID(dep_add)' },
577
+ depType: {
578
+ type: 'string',
579
+ enum: ['blocks', 'parent-child', 'waits-for', 'discovered-from', 'related', 'knowledge-ref'],
580
+ default: 'blocks',
581
+ description: '依赖类型(dep_add)',
582
+ },
583
+ limit: { type: 'number', default: 10, description: '结果数量限制(ready/list)' },
584
+ status: {
585
+ type: 'string',
586
+ enum: ['open', 'in_progress', 'deferred', 'closed'],
587
+ description: '状态过滤(list)',
588
+ },
589
+ withKnowledge: {
590
+ type: 'boolean',
591
+ default: true,
592
+ description: '是否附加知识上下文(ready)',
593
+ },
594
+ subtasks: {
595
+ type: 'array',
596
+ items: {
597
+ type: 'object',
598
+ properties: {
599
+ title: { type: 'string' },
600
+ description: { type: 'string' },
601
+ priority: { type: 'number' },
602
+ taskType: { type: 'string' },
603
+ blockedByIndex: { type: 'number', description: '被 subtasks 数组中哪个 index 的子任务阻塞' },
604
+ },
605
+ required: ['title'],
606
+ },
607
+ description: '子任务列表(decompose)',
608
+ },
609
+ },
610
+ required: ['operation'],
611
+ },
612
+ },
613
+
517
614
  // ══════════════════════════════════════════════════════
518
615
  // Tier: admin — 管理员/CI 工具 (额外 +4)
519
616
  // ══════════════════════════════════════════════════════
@@ -26,6 +26,7 @@ import authRouter from './routes/auth.js';
26
26
  import candidatesRouter from './routes/candidates.js';
27
27
  import commandsRouter from './routes/commands.js';
28
28
  import extractRouter from './routes/extract.js';
29
+ import guardRouter from './routes/guard.js';
29
30
  import guardRuleRouter from './routes/guardRules.js';
30
31
  import healthRouter from './routes/health.js';
31
32
  import knowledgeRouter from './routes/knowledge.js';
@@ -36,6 +37,7 @@ import searchRouter from './routes/search.js';
36
37
  import skillsRouter from './routes/skills.js';
37
38
  import snippetRouter from './routes/snippets.js';
38
39
  import spmRouter from './routes/spm.js';
40
+ import taskRouter from './routes/task.js';
39
41
  import violationsRouter from './routes/violations.js';
40
42
  import wikiRouter from './routes/wiki.js';
41
43
 
@@ -252,9 +254,15 @@ export class HttpServer {
252
254
  this.app.use(`${apiPrefix}/monitoring`, monitoringRouter);
253
255
  }
254
256
 
257
+ // Guard 实时检查路由(Extension DiagnosticCollection 调用)
258
+ this.app.use(`${apiPrefix}/guard`, guardRouter);
259
+
255
260
  // 守护规则路由
256
261
  this.app.use(`${apiPrefix}/rules`, guardRuleRouter);
257
262
 
263
+ // TaskGraph 路由(Extension taskTool.ts 转发调用)
264
+ this.app.use(`${apiPrefix}/task`, taskRouter);
265
+
258
266
  // 搜索路由
259
267
  this.app.use(`${apiPrefix}/search`, searchRouter);
260
268