autosnippet 3.2.4 → 3.2.6
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 +2 -4
- package/bin/cli.js +164 -145
- package/config/constitution.yaml +2 -0
- package/dashboard/dist/assets/{index-DNOHYBhy.css → index-BaGY7kJI.css} +1 -1
- package/dashboard/dist/assets/{index-6itPuGFl.js → index-DfHY_3ln.js} +25 -25
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/CliLogger.js +78 -0
- package/lib/cli/SetupService.js +9 -718
- package/lib/cli/UpgradeService.js +23 -398
- package/lib/cli/deploy/FileDeployer.js +562 -0
- package/lib/cli/deploy/FileManifest.js +272 -0
- package/lib/external/mcp/McpServer.js +22 -26
- package/lib/external/mcp/autoApproveInjector.js +1 -0
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +5 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +25 -3
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +6 -6
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +4 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +5 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +89 -44
- package/lib/external/mcp/handlers/consolidated.js +8 -9
- package/lib/external/mcp/handlers/dimension-complete-external.js +4 -4
- package/lib/external/mcp/handlers/guard.js +283 -5
- package/lib/external/mcp/handlers/task.js +183 -9
- package/lib/external/mcp/tools.js +32 -81
- package/lib/http/routes/task.js +55 -0
- package/lib/service/chat/AnalystAgent.js +12 -12
- package/lib/service/chat/ChatAgent.js +227 -545
- package/lib/service/chat/ChatAgentPrompts.js +9 -11
- package/lib/service/chat/ContextWindow.js +2 -296
- package/lib/service/chat/EpisodicConsolidator.js +15 -15
- package/lib/service/chat/ExplorationTracker.js +1262 -0
- package/lib/service/chat/HandoffProtocol.js +8 -9
- package/lib/service/chat/Memory.js +4 -0
- package/lib/service/chat/ProducerAgent.js +9 -6
- package/lib/service/chat/ProjectSemanticMemory.js +4 -0
- package/lib/service/chat/ReasoningTrace.js +182 -0
- package/lib/service/chat/WorkingMemory.js +4 -0
- package/lib/service/chat/memory/ActiveContext.js +910 -0
- package/lib/service/chat/memory/MemoryCoordinator.js +662 -0
- package/lib/service/chat/memory/PersistentMemory.js +450 -0
- package/lib/service/chat/memory/SessionStore.js +896 -0
- package/lib/service/chat/memory/index.js +13 -0
- package/lib/service/chat/tools/ast-graph.js +17 -16
- package/lib/service/cursor/AgentInstructionsGenerator.js +76 -47
- package/lib/service/cursor/FileProtection.js +4 -1
- package/lib/service/guard/GuardCheckEngine.js +10 -3
- package/lib/service/task/TaskGraphService.js +3 -3
- package/lib/shared/LanguageService.js +2 -1
- package/package.json +1 -1
- package/skills/autosnippet-intent/SKILL.md +1 -3
- package/skills/autosnippet-recipes/SKILL.md +1 -3
- package/templates/claude-code/commands/prime.md +19 -0
- package/templates/claude-code/hooks/autosnippet-session.sh +63 -0
- package/templates/claude-code/settings.json +21 -0
- package/templates/copilot-instructions.md +64 -177
- package/templates/cursor-hooks/commands/prime.md +12 -0
- package/templates/cursor-hooks/hooks/session-start.sh +10 -0
- package/templates/cursor-hooks/hooks.json +11 -0
- package/templates/cursor-rules/autosnippet-conventions.mdc +52 -3
- package/templates/cursor-rules/autosnippet-workflow.mdc +51 -27
- package/lib/external/mcp/handlers/decide.js +0 -109
- package/lib/external/mcp/handlers/ready.js +0 -42
- package/lib/service/chat/ReasoningLayer.js +0 -888
- package/templates/claude-hooks.yaml +0 -19
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Handler — autosnippet_task (Task
|
|
2
|
+
* MCP Handler — autosnippet_task (Unified Task & Decision Management)
|
|
3
3
|
*
|
|
4
|
-
* Operations:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Operations:
|
|
5
|
+
* Session: prime (session entry — loads decisions + ready tasks + stats)
|
|
6
|
+
* Tasks: create / ready / claim / close / fail / defer / progress
|
|
7
|
+
* show / list / stats / blocked / decompose / dep_add / dep_tree
|
|
8
|
+
* Decisions: record_decision / revise_decision / unpin_decision / list_decisions
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { envelope } from '../envelope.js';
|
|
12
|
+
// guard is independent — no guardState dependency in task lifecycle
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* 统一入口
|
|
@@ -19,6 +20,10 @@ export async function taskHandler(ctx, args) {
|
|
|
19
20
|
const taskService = ctx.container.get('taskGraphService');
|
|
20
21
|
|
|
21
22
|
switch (args.operation) {
|
|
23
|
+
// ── Session ──
|
|
24
|
+
case 'prime':
|
|
25
|
+
return _prime(taskService, args);
|
|
26
|
+
// ── Task CRUD ──
|
|
22
27
|
case 'create':
|
|
23
28
|
return _create(taskService, args);
|
|
24
29
|
case 'ready':
|
|
@@ -26,7 +31,7 @@ export async function taskHandler(ctx, args) {
|
|
|
26
31
|
case 'claim':
|
|
27
32
|
return _claim(taskService, args);
|
|
28
33
|
case 'close':
|
|
29
|
-
return _close(taskService, args);
|
|
34
|
+
return _close(ctx, taskService, args);
|
|
30
35
|
case 'fail':
|
|
31
36
|
return _fail(taskService, args);
|
|
32
37
|
case 'defer':
|
|
@@ -47,10 +52,19 @@ export async function taskHandler(ctx, args) {
|
|
|
47
52
|
return _depTree(taskService, args);
|
|
48
53
|
case 'stats':
|
|
49
54
|
return _stats(taskService);
|
|
55
|
+
// ── Decisions ──
|
|
56
|
+
case 'record_decision':
|
|
57
|
+
return _recordDecision(taskService, args);
|
|
58
|
+
case 'revise_decision':
|
|
59
|
+
return _reviseDecision(taskService, args);
|
|
60
|
+
case 'unpin_decision':
|
|
61
|
+
return _unpinDecision(taskService, args);
|
|
62
|
+
case 'list_decisions':
|
|
63
|
+
return _listDecisions(taskService);
|
|
50
64
|
default:
|
|
51
65
|
return envelope({
|
|
52
66
|
success: false,
|
|
53
|
-
message: `Unknown operation: ${args.operation}.
|
|
67
|
+
message: `Unknown operation: ${args.operation}. Valid: prime, ready, create, claim, close, fail, defer, progress, decompose, dep_add, dep_tree, stats, list, record_decision, revise_decision, unpin_decision, list_decisions.`,
|
|
54
68
|
meta: { tool: 'autosnippet_task' },
|
|
55
69
|
});
|
|
56
70
|
}
|
|
@@ -113,10 +127,11 @@ async function _claim(svc, args) {
|
|
|
113
127
|
|
|
114
128
|
// ── close ──
|
|
115
129
|
|
|
116
|
-
async function _close(svc, args) {
|
|
130
|
+
async function _close(ctx, svc, args) {
|
|
117
131
|
if (!args.id) {
|
|
118
132
|
return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
|
|
119
133
|
}
|
|
134
|
+
|
|
120
135
|
const { task, newlyReady } = await svc.close(args.id, args.reason || 'Completed');
|
|
121
136
|
return envelope({
|
|
122
137
|
success: true,
|
|
@@ -281,3 +296,162 @@ async function _stats(svc) {
|
|
|
281
296
|
meta: { tool: 'autosnippet_task' },
|
|
282
297
|
});
|
|
283
298
|
}
|
|
299
|
+
|
|
300
|
+
// ═══ Session (prime) ═══════════════════════════════════
|
|
301
|
+
|
|
302
|
+
async function _prime(svc, args) {
|
|
303
|
+
const result = await svc.prime({
|
|
304
|
+
limit: args.limit || 10,
|
|
305
|
+
withKnowledge: args.withKnowledge !== false,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const decisionCount = (result.decisions || []).length;
|
|
309
|
+
const staleCount = (result.staleDecisions || []).length;
|
|
310
|
+
const decisionTitles = (result.decisions || []).map((d) => d.title).join('; ');
|
|
311
|
+
const statsLine = `${result.inProgress.length} in-progress, ${result.ready.length} ready, ${result.stats.total} total`;
|
|
312
|
+
|
|
313
|
+
// ── Behavioral Rules Reminder (survives compaction) ──
|
|
314
|
+
result._taskRules = {
|
|
315
|
+
reminder: [
|
|
316
|
+
'📋 TASK RULES (MANDATORY):',
|
|
317
|
+
'🔑 YOU are the task operator — user speaks naturally, you translate to task operations. NEVER tell user to run task commands.',
|
|
318
|
+
'• MUST prime on EVERY message BEFORE anything else',
|
|
319
|
+
'• MUST create task for non-trivial work (≥2 files OR ≥10 lines)',
|
|
320
|
+
'• MUST claim before coding, close when done with meaningful reason',
|
|
321
|
+
'• MUST handle unfinished tasks before starting new work (ask user: Continue/Defer/Abandon)',
|
|
322
|
+
'• NEVER skip prime, NEVER start new work with open in_progress tasks',
|
|
323
|
+
'• NEVER leave tasks in in_progress when session ends — close or defer ALL',
|
|
324
|
+
'• When in doubt → create a task. When idle → ready()',
|
|
325
|
+
'• Session end → close all tasks, defer incomplete, verify zero in_progress',
|
|
326
|
+
].join('\n'),
|
|
327
|
+
translationHint: [
|
|
328
|
+
'User Says → You Run:',
|
|
329
|
+
'"fix bug"/"implement" → create→claim→code→close',
|
|
330
|
+
'"continue" → resume in-progress→close',
|
|
331
|
+
'"pause" → defer | "abandon" → fail | "break down" → decompose',
|
|
332
|
+
'"what\'s next" → ready() | "agreed" → record_decision',
|
|
333
|
+
'Quick question → No task. Just answer.',
|
|
334
|
+
].join('\n'),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
let message;
|
|
338
|
+
if (decisionCount > 0) {
|
|
339
|
+
const stalePart = staleCount > 0 ? ` ${staleCount} stale.` : '';
|
|
340
|
+
message = `⚠️ ${decisionCount} ACTIVE DECISION(S): [${decisionTitles}].${stalePart} ${statsLine}.`;
|
|
341
|
+
} else {
|
|
342
|
+
message = `${statsLine}.`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ── Resume Prompt: 有 inProgress 任务时,提示 Agent 让用户选择 ──
|
|
346
|
+
if (result.inProgress.length > 0) {
|
|
347
|
+
const taskList = result.inProgress.map((t) => {
|
|
348
|
+
const age = t.updatedAt
|
|
349
|
+
? `${Math.floor((Date.now() / 1000 - t.updatedAt) / 86400)}d ago`
|
|
350
|
+
: '';
|
|
351
|
+
return `• **${t.id}** — ${t.title}${age ? ` (${age})` : ''}`;
|
|
352
|
+
}).join('\n');
|
|
353
|
+
|
|
354
|
+
result._resumePrompt = {
|
|
355
|
+
instruction: [
|
|
356
|
+
'There are unfinished tasks. You MUST present these options to the user BEFORE doing anything else:',
|
|
357
|
+
'',
|
|
358
|
+
'**Unfinished tasks:**',
|
|
359
|
+
taskList,
|
|
360
|
+
'',
|
|
361
|
+
'Ask the user to choose:',
|
|
362
|
+
'1. **Continue** — resume the unfinished task(s)',
|
|
363
|
+
'2. **Defer** — pause it and work on something else',
|
|
364
|
+
'3. **Abandon** — close/fail it and start fresh',
|
|
365
|
+
'',
|
|
366
|
+
'Wait for the user\'s answer. Do NOT auto-resume.',
|
|
367
|
+
].join('\n'),
|
|
368
|
+
taskIds: result.inProgress.map((t) => t.id),
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
message += ` ⏸️ ${result.inProgress.length} unfinished task(s) — ask user before resuming.`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return envelope({
|
|
375
|
+
success: true,
|
|
376
|
+
data: result,
|
|
377
|
+
message,
|
|
378
|
+
meta: { tool: 'autosnippet_task' },
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ═══ Decisions ═══════════════════════════════════════
|
|
383
|
+
|
|
384
|
+
async function _recordDecision(svc, args) {
|
|
385
|
+
if (!args.title) {
|
|
386
|
+
return envelope({ success: false, message: 'title is required', meta: { tool: 'autosnippet_task' } });
|
|
387
|
+
}
|
|
388
|
+
if (!args.description) {
|
|
389
|
+
return envelope({ success: false, message: 'description is required', meta: { tool: 'autosnippet_task' } });
|
|
390
|
+
}
|
|
391
|
+
const { task, isDuplicate } = await svc.recordDecision({
|
|
392
|
+
title: args.title,
|
|
393
|
+
description: args.description,
|
|
394
|
+
rationale: args.rationale || '',
|
|
395
|
+
tags: args.tags || [],
|
|
396
|
+
relatedTaskId: args.relatedTaskId || null,
|
|
397
|
+
});
|
|
398
|
+
return envelope({
|
|
399
|
+
success: true,
|
|
400
|
+
data: task.toJSON(),
|
|
401
|
+
message: isDuplicate
|
|
402
|
+
? `⚠ Decision already recorded: ${task.id}`
|
|
403
|
+
: `✅ Decision pinned: ${task.id} — "${args.title}"`,
|
|
404
|
+
meta: { tool: 'autosnippet_task' },
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function _reviseDecision(svc, args) {
|
|
409
|
+
if (!args.id) {
|
|
410
|
+
return envelope({ success: false, message: 'id of old decision is required', meta: { tool: 'autosnippet_task' } });
|
|
411
|
+
}
|
|
412
|
+
if (!args.title) {
|
|
413
|
+
return envelope({ success: false, message: 'title of new decision is required', meta: { tool: 'autosnippet_task' } });
|
|
414
|
+
}
|
|
415
|
+
if (!args.description) {
|
|
416
|
+
return envelope({ success: false, message: 'description of new decision is required', meta: { tool: 'autosnippet_task' } });
|
|
417
|
+
}
|
|
418
|
+
const result = await svc.reviseDecision({
|
|
419
|
+
oldDecisionId: args.id,
|
|
420
|
+
title: args.title,
|
|
421
|
+
description: args.description,
|
|
422
|
+
rationale: args.rationale || '',
|
|
423
|
+
reason: args.reason || '',
|
|
424
|
+
});
|
|
425
|
+
return envelope({
|
|
426
|
+
success: true,
|
|
427
|
+
data: {
|
|
428
|
+
newDecision: result.newDecision.toJSON(),
|
|
429
|
+
superseded: result.oldDecisionId,
|
|
430
|
+
},
|
|
431
|
+
message: `✅ Decision revised: ${result.oldDecisionId} → ${result.newDecision.id}`,
|
|
432
|
+
meta: { tool: 'autosnippet_task' },
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function _unpinDecision(svc, args) {
|
|
437
|
+
if (!args.id) {
|
|
438
|
+
return envelope({ success: false, message: 'id is required', meta: { tool: 'autosnippet_task' } });
|
|
439
|
+
}
|
|
440
|
+
const task = await svc.unpinDecision(args.id, args.reason || '');
|
|
441
|
+
return envelope({
|
|
442
|
+
success: true,
|
|
443
|
+
data: task.toJSON(),
|
|
444
|
+
message: `Decision ${args.id} unpinned and closed`,
|
|
445
|
+
meta: { tool: 'autosnippet_task' },
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function _listDecisions(svc) {
|
|
450
|
+
const decisions = await svc.list({ status: 'pinned', taskType: 'decision' }, { limit: 50 });
|
|
451
|
+
return envelope({
|
|
452
|
+
success: true,
|
|
453
|
+
data: decisions.map((d) => d.toJSON()),
|
|
454
|
+
message: `${decisions.length} active decision(s)`,
|
|
455
|
+
meta: { tool: 'autosnippet_task' },
|
|
456
|
+
});
|
|
457
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP 工具定义 — V3 整合版 (18 agent + 4 admin = 22 工具)
|
|
3
3
|
*
|
|
4
|
-
* 从 39 → 22 工具(参数路由合并同类工具 + 外部 Agent 冷启动新架构 + TaskGraph
|
|
5
|
-
* TaskGraph
|
|
4
|
+
* 从 39 → 22 工具(参数路由合并同类工具 + 外部 Agent 冷启动新架构 + TaskGraph 统一入口)。
|
|
5
|
+
* TaskGraph: autosnippet_task (统一入口,含 prime/decision/task CRUD)
|
|
6
6
|
* 每个工具声明增加 tier 字段(agent / admin)。
|
|
7
7
|
* tools.js 只包含 JSON Schema 声明 + Gateway 映射,不含业务逻辑。
|
|
8
8
|
*
|
|
@@ -45,16 +45,7 @@ export const TOOL_GATEWAY_MAP = {
|
|
|
45
45
|
autosnippet_submit_knowledge: { action: 'knowledge:create', resource: 'knowledge' },
|
|
46
46
|
autosnippet_submit_knowledge_batch: { action: 'knowledge:create', resource: 'knowledge' },
|
|
47
47
|
autosnippet_save_document: { action: 'knowledge:create', resource: 'knowledge' },
|
|
48
|
-
//
|
|
49
|
-
autosnippet_decide: {
|
|
50
|
-
resolver: (args) =>
|
|
51
|
-
({
|
|
52
|
-
record: { action: 'task:create', resource: 'tasks' },
|
|
53
|
-
revise: { action: 'task:update', resource: 'tasks' },
|
|
54
|
-
unpin: { action: 'task:update', resource: 'tasks' },
|
|
55
|
-
})[args?.operation] || null, // list 只读
|
|
56
|
-
},
|
|
57
|
-
// task 写操作(create/claim/close/fail/defer/decompose/dep_add)
|
|
48
|
+
// task 写操作(create/claim/close/fail/defer/decompose/dep_add + decision 写操作)
|
|
58
49
|
autosnippet_task: {
|
|
59
50
|
resolver: (args) =>
|
|
60
51
|
({
|
|
@@ -66,7 +57,10 @@ export const TOOL_GATEWAY_MAP = {
|
|
|
66
57
|
progress: { action: 'task:update', resource: 'tasks' },
|
|
67
58
|
decompose: { action: 'task:create', resource: 'tasks' },
|
|
68
59
|
dep_add: { action: 'task:update', resource: 'tasks' },
|
|
69
|
-
|
|
60
|
+
record_decision: { action: 'task:create', resource: 'tasks' },
|
|
61
|
+
revise_decision: { action: 'task:update', resource: 'tasks' },
|
|
62
|
+
unpin_decision: { action: 'task:update', resource: 'tasks' },
|
|
63
|
+
})[args?.operation] || null, // prime/ready/show/list/blocked/dep_tree/stats/list_decisions 只读
|
|
70
64
|
},
|
|
71
65
|
// admin 工具
|
|
72
66
|
autosnippet_enrich_candidates: { action: 'knowledge:update', resource: 'knowledge' },
|
|
@@ -216,32 +210,27 @@ export const TOOLS = [
|
|
|
216
210
|
},
|
|
217
211
|
},
|
|
218
212
|
|
|
219
|
-
// 6. Guard
|
|
213
|
+
// 6. Guard 检查(统一入口)
|
|
220
214
|
{
|
|
221
215
|
name: 'autosnippet_guard',
|
|
222
216
|
tier: 'agent',
|
|
223
|
-
description:
|
|
217
|
+
description:
|
|
218
|
+
'代码规范检查 & 质量门禁。\n' +
|
|
219
|
+
'• 无参数 → 自动从 git diff 检测增量文件并检查(编码后推荐用法)\n' +
|
|
220
|
+
'• files: ["path/to/file.m", ...] → 检查指定文件\n' +
|
|
221
|
+
'• code: "..." → 单文件内联检查\n' +
|
|
222
|
+
'每个 violation 内联 recipe 修复指南(doClause + coreCode),直接按指示修复后再次调用。',
|
|
224
223
|
inputSchema: {
|
|
225
224
|
type: 'object',
|
|
226
225
|
properties: {
|
|
227
|
-
code: { type: 'string', description: '待检查代码(单文件模式,与 files 二选一)' },
|
|
228
|
-
language: { type: 'string', description: '编程语言' },
|
|
229
|
-
filePath: { type: 'string', description: '文件路径(单文件模式)' },
|
|
230
226
|
files: {
|
|
231
227
|
type: 'array',
|
|
232
|
-
items: {
|
|
233
|
-
|
|
234
|
-
properties: { path: { type: 'string' }, content: { type: 'string' } },
|
|
235
|
-
required: ['path'],
|
|
236
|
-
},
|
|
237
|
-
description: '文件列表(批量模式,与 code 二选一)',
|
|
238
|
-
},
|
|
239
|
-
scope: {
|
|
240
|
-
type: 'string',
|
|
241
|
-
enum: ['file', 'target', 'project'],
|
|
242
|
-
default: 'project',
|
|
243
|
-
description: '审计范围(批量模式)',
|
|
228
|
+
items: { type: 'string' },
|
|
229
|
+
description: '待检查文件路径列表(string[])。省略则自动从 git diff 检测增量文件。',
|
|
244
230
|
},
|
|
231
|
+
code: { type: 'string', description: '待检查代码(单文件模式,与 files 二选一)' },
|
|
232
|
+
language: { type: 'string', description: '编程语言(单文件模式)' },
|
|
233
|
+
filePath: { type: 'string', description: '文件路径提示(单文件模式,用于语言检测)' },
|
|
245
234
|
},
|
|
246
235
|
required: [],
|
|
247
236
|
},
|
|
@@ -538,68 +527,26 @@ export const TOOLS = [
|
|
|
538
527
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
539
528
|
},
|
|
540
529
|
|
|
541
|
-
// 13.
|
|
542
|
-
{
|
|
543
|
-
name: 'autosnippet_ready',
|
|
544
|
-
tier: 'agent',
|
|
545
|
-
description:
|
|
546
|
-
'Session entry point. Call FIRST at conversation start to load project context, active decisions, and available tasks.\n' +
|
|
547
|
-
'Returns: in-progress tasks, ready tasks (with knowledge context), pinned decisions, and stats.',
|
|
548
|
-
inputSchema: {
|
|
549
|
-
type: 'object',
|
|
550
|
-
properties: {
|
|
551
|
-
limit: { type: 'number', default: 10, description: 'Max ready tasks to return' },
|
|
552
|
-
withKnowledge: { type: 'boolean', default: true, description: 'Attach knowledge context to ready tasks' },
|
|
553
|
-
},
|
|
554
|
-
required: [],
|
|
555
|
-
},
|
|
556
|
-
},
|
|
557
|
-
|
|
558
|
-
// 14. autosnippet_decide — 决策管理
|
|
559
|
-
{
|
|
560
|
-
name: 'autosnippet_decide',
|
|
561
|
-
tier: 'agent',
|
|
562
|
-
description:
|
|
563
|
-
'Decision management. Record, revise or unpin project decisions that persist across conversations.\n' +
|
|
564
|
-
'Call record when you and user reach agreement. Decisions are shown in every autosnippet_ready call.',
|
|
565
|
-
inputSchema: {
|
|
566
|
-
type: 'object',
|
|
567
|
-
properties: {
|
|
568
|
-
operation: {
|
|
569
|
-
type: 'string',
|
|
570
|
-
enum: ['record', 'revise', 'unpin', 'list'],
|
|
571
|
-
description: 'record=pin new decision, revise=supersede old, unpin=close, list=show active',
|
|
572
|
-
},
|
|
573
|
-
title: { type: 'string', description: 'Decision title (record/revise)' },
|
|
574
|
-
description: { type: 'string', description: 'Decision description (record/revise)' },
|
|
575
|
-
rationale: { type: 'string', description: 'Why this decision (record/revise)' },
|
|
576
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'Classification tags (record)' },
|
|
577
|
-
relatedTaskId: { type: 'string', description: 'Related task ID (record)' },
|
|
578
|
-
id: { type: 'string', description: 'Decision ID (revise/unpin)' },
|
|
579
|
-
reason: { type: 'string', description: 'Reason for revision/unpin' },
|
|
580
|
-
},
|
|
581
|
-
required: ['operation'],
|
|
582
|
-
},
|
|
583
|
-
},
|
|
584
|
-
|
|
585
|
-
// 15. autosnippet_task — 任务 CRUD
|
|
530
|
+
// 13. autosnippet_task — 统一任务管理(含 prime/decision/CRUD)
|
|
586
531
|
{
|
|
587
532
|
name: 'autosnippet_task',
|
|
588
533
|
tier: 'agent',
|
|
589
534
|
description:
|
|
590
|
-
'
|
|
591
|
-
'
|
|
535
|
+
'Unified task & decision management. Includes session context (prime), task CRUD, and decision persistence.\n' +
|
|
536
|
+
'Call prime FIRST at every conversation start to load decisions + tasks + knowledge context.',
|
|
592
537
|
inputSchema: {
|
|
593
538
|
type: 'object',
|
|
594
539
|
properties: {
|
|
595
540
|
operation: {
|
|
596
541
|
type: 'string',
|
|
597
542
|
enum: [
|
|
598
|
-
'
|
|
543
|
+
'prime', 'ready',
|
|
544
|
+
'create', 'claim', 'close', 'fail', 'defer',
|
|
599
545
|
'progress', 'show', 'list', 'stats',
|
|
600
546
|
'blocked', 'decompose', 'dep_add', 'dep_tree',
|
|
547
|
+
'record_decision', 'revise_decision', 'unpin_decision', 'list_decisions',
|
|
601
548
|
],
|
|
602
|
-
description: '
|
|
549
|
+
description: 'prime=session entry (CALL FIRST) | ready=ready tasks | record_decision/revise_decision/unpin_decision/list_decisions=decision management | create/claim/close/fail/defer/progress/decompose=task CRUD',
|
|
603
550
|
},
|
|
604
551
|
title: { type: 'string', description: 'Task title (create)' },
|
|
605
552
|
description: { type: 'string', description: 'Task description (create/progress)' },
|
|
@@ -612,8 +559,12 @@ export const TOOLS = [
|
|
|
612
559
|
description: 'Task type (create)',
|
|
613
560
|
},
|
|
614
561
|
parentId: { type: 'string', description: 'Parent task ID (create subtask)' },
|
|
615
|
-
id: { type: 'string', description: 'Task ID (claim/close/fail/defer/show/dep_add/dep_tree/progress)' },
|
|
616
|
-
reason: { type: 'string', description: 'Reason (close/fail/defer)' },
|
|
562
|
+
id: { type: 'string', description: 'Task ID (claim/close/fail/defer/show/dep_add/dep_tree/progress/revise_decision/unpin_decision)' },
|
|
563
|
+
reason: { type: 'string', description: 'Reason (close/fail/defer/unpin_decision)' },
|
|
564
|
+
rationale: { type: 'string', description: 'Why this decision (record_decision/revise_decision)' },
|
|
565
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Classification tags (record_decision)' },
|
|
566
|
+
relatedTaskId: { type: 'string', description: 'Related task ID (record_decision)' },
|
|
567
|
+
|
|
617
568
|
dependsOn: { type: 'string', description: 'Dependency target task ID (dep_add)' },
|
|
618
569
|
depType: {
|
|
619
570
|
type: 'string',
|
package/lib/http/routes/task.js
CHANGED
|
@@ -208,6 +208,57 @@ async function _prime(svc) {
|
|
|
208
208
|
const decisionTitles = (result.decisions || []).map((d) => d.title).join('; ');
|
|
209
209
|
const statsLine = `${result.inProgress.length} in-progress, ${result.ready.length} ready, ${result.stats.total} total`;
|
|
210
210
|
|
|
211
|
+
// ── Behavioral Rules Reminder (synced with MCP handler) ──
|
|
212
|
+
result._taskRules = {
|
|
213
|
+
reminder: [
|
|
214
|
+
'📋 TASK RULES (MANDATORY):',
|
|
215
|
+
'🔑 YOU are the task operator — user speaks naturally, you translate to task operations. NEVER tell user to run task commands.',
|
|
216
|
+
'• MUST prime on EVERY message BEFORE anything else',
|
|
217
|
+
'• MUST create task for non-trivial work (≥2 files OR ≥10 lines)',
|
|
218
|
+
'• MUST claim before coding, close when done with meaningful reason',
|
|
219
|
+
'• MUST handle unfinished tasks before starting new work (ask user: Continue/Defer/Abandon)',
|
|
220
|
+
'• NEVER skip prime, NEVER start new work with open in_progress tasks',
|
|
221
|
+
'• NEVER leave tasks in in_progress when session ends — close or defer ALL',
|
|
222
|
+
'• When in doubt → create a task. When idle → ready()',
|
|
223
|
+
'• Session end → close all tasks, defer incomplete, verify zero in_progress',
|
|
224
|
+
].join('\n'),
|
|
225
|
+
translationHint: [
|
|
226
|
+
'User Says → You Run:',
|
|
227
|
+
'"fix bug"/"implement" → create→claim→code→close',
|
|
228
|
+
'"continue" → resume in-progress→close',
|
|
229
|
+
'"pause" → defer | "abandon" → fail | "break down" → decompose',
|
|
230
|
+
'"what\'s next" → ready() | "agreed" → record_decision',
|
|
231
|
+
'Quick question → No task. Just answer.',
|
|
232
|
+
].join('\n'),
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// ── Resume Prompt: 有 inProgress 任务时,提示 Agent 让用户选择 ──
|
|
236
|
+
if (result.inProgress.length > 0) {
|
|
237
|
+
const taskList = result.inProgress.map((t) => {
|
|
238
|
+
const age = t.updatedAt
|
|
239
|
+
? `${Math.floor((Date.now() / 1000 - t.updatedAt) / 86400)}d ago`
|
|
240
|
+
: '';
|
|
241
|
+
return `• **${t.id}** — ${t.title}${age ? ` (${age})` : ''}`;
|
|
242
|
+
}).join('\n');
|
|
243
|
+
|
|
244
|
+
result._resumePrompt = {
|
|
245
|
+
instruction: [
|
|
246
|
+
'There are unfinished tasks. You MUST present these options to the user BEFORE doing anything else:',
|
|
247
|
+
'',
|
|
248
|
+
'**Unfinished tasks:**',
|
|
249
|
+
taskList,
|
|
250
|
+
'',
|
|
251
|
+
'Ask the user to choose:',
|
|
252
|
+
'1. **Continue** — resume the unfinished task(s)',
|
|
253
|
+
'2. **Defer** — pause it and work on something else',
|
|
254
|
+
'3. **Abandon** — close/fail it and start fresh',
|
|
255
|
+
'',
|
|
256
|
+
'Wait for the user\'s answer. Do NOT auto-resume.',
|
|
257
|
+
].join('\n'),
|
|
258
|
+
taskIds: result.inProgress.map((t) => t.id),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
211
262
|
let message;
|
|
212
263
|
if (decisionCount > 0) {
|
|
213
264
|
const stalePart = staleCount > 0 ? ` ${staleCount} stale.` : '';
|
|
@@ -216,6 +267,10 @@ async function _prime(svc) {
|
|
|
216
267
|
message = `${statsLine}.`;
|
|
217
268
|
}
|
|
218
269
|
|
|
270
|
+
if (result.inProgress.length > 0) {
|
|
271
|
+
message += ` ⏸️ ${result.inProgress.length} unfinished task(s) — ask user before resuming.`;
|
|
272
|
+
}
|
|
273
|
+
|
|
219
274
|
return {
|
|
220
275
|
success: true,
|
|
221
276
|
data: result,
|
|
@@ -74,7 +74,7 @@ const ANALYST_TOOLS = [
|
|
|
74
74
|
];
|
|
75
75
|
|
|
76
76
|
// ──────────────────────────────────────────────────────────────────
|
|
77
|
-
// Analyst 预算 —
|
|
77
|
+
// Analyst 预算 — 使用 analyst 策略(自由探索,无阶段约束)
|
|
78
78
|
// ──────────────────────────────────────────────────────────────────
|
|
79
79
|
|
|
80
80
|
const ANALYST_BUDGET = {
|
|
@@ -283,7 +283,7 @@ export class AnalystAgent {
|
|
|
283
283
|
dimConfig,
|
|
284
284
|
projectInfo,
|
|
285
285
|
options.dimensionContext,
|
|
286
|
-
options.
|
|
286
|
+
options.memoryCoordinator?.getSessionStore(), // v5.0: SessionStore 提供跨维度上下文
|
|
287
287
|
options.semanticMemory, // v4.1: ProjectSemanticMemory 历史记忆
|
|
288
288
|
options.codeEntityGraph // Phase E: CodeEntityGraph 代码实体图谱
|
|
289
289
|
);
|
|
@@ -308,24 +308,24 @@ export class AnalystAgent {
|
|
|
308
308
|
budget: ANALYST_BUDGET,
|
|
309
309
|
systemPromptOverride: ANALYST_SYSTEM_PROMPT,
|
|
310
310
|
allowedTools: ANALYST_TOOLS,
|
|
311
|
-
|
|
311
|
+
strategy: 'analyst',
|
|
312
312
|
temperature: 0.4,
|
|
313
313
|
dimensionMeta: {
|
|
314
314
|
id: dimId,
|
|
315
315
|
outputType: 'analysis',
|
|
316
316
|
allowedKnowledgeTypes: dimConfig.allowedKnowledgeTypes || [],
|
|
317
317
|
},
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
episodicMemory: options.episodicMemory || undefined,
|
|
321
|
-
toolResultCache: options.toolResultCache || undefined,
|
|
318
|
+
// v5.0: 统一 MemoryCoordinator
|
|
319
|
+
memoryCoordinator: options.memoryCoordinator || undefined,
|
|
322
320
|
});
|
|
323
321
|
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
const
|
|
328
|
-
|
|
322
|
+
// v5.0: 当有 MemoryCoordinator 时使用 ActiveContext 的 distill 结果
|
|
323
|
+
// 构建 AnalysisArtifact (包含 evidenceMap/findings/negativeSignals)
|
|
324
|
+
// 使用显式 scopeId 确保并行执行安全
|
|
325
|
+
const analystScopeId = `${dimId}:analyst`;
|
|
326
|
+
const ac = options.memoryCoordinator?.getActiveContext(analystScopeId);
|
|
327
|
+
const report = ac
|
|
328
|
+
? buildAnalysisArtifact(result, dimId, this.#projectGraph, ac)
|
|
329
329
|
: buildAnalysisReport(result, dimId, this.#projectGraph);
|
|
330
330
|
|
|
331
331
|
// 附加推理链数据(如果 ChatAgent 返回了 ReasoningTrace)
|