speccore 5.69.7 → 5.70.0
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/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +0 -1
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +48 -35
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +53 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pr.d.ts.map +1 -1
- package/dist/commands/pr.js +5 -0
- package/dist/commands/pr.js.map +1 -1
- package/dist/commands/welcome.js +1 -1
- package/dist/core/ask-config.d.ts +54 -0
- package/dist/core/ask-config.d.ts.map +1 -0
- package/dist/core/ask-config.js +145 -0
- package/dist/core/ask-config.js.map +1 -0
- package/dist/core/ask-context.d.ts +61 -0
- package/dist/core/ask-context.d.ts.map +1 -0
- package/dist/core/ask-context.js +187 -0
- package/dist/core/ask-context.js.map +1 -0
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +163 -64
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/ask-host-ai.d.ts.map +1 -1
- package/dist/core/ask-host-ai.js +6 -0
- package/dist/core/ask-host-ai.js.map +1 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +4 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/doc-validator.js +2 -2
- package/dist/core/doc-validator.js.map +1 -1
- package/dist/core/error-feedback.d.ts.map +1 -1
- package/dist/core/error-feedback.js.map +1 -1
- package/dist/core/intent-cache.d.ts +55 -0
- package/dist/core/intent-cache.d.ts.map +1 -0
- package/dist/core/intent-cache.js +162 -0
- package/dist/core/intent-cache.js.map +1 -0
- package/dist/core/prompt-builder.js +2 -2
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/schemas/context.schema.d.ts.map +1 -1
- package/dist/core/schemas/context.schema.js +1 -0
- package/dist/core/schemas/context.schema.js.map +1 -1
- package/dist/core/schemas/iteration.schema.d.ts.map +1 -1
- package/dist/core/schemas/iteration.schema.js +1 -0
- package/dist/core/schemas/iteration.schema.js.map +1 -1
- package/dist/core/schemas/platform.schema.d.ts.map +1 -1
- package/dist/core/schemas/platform.schema.js +1 -0
- package/dist/core/schemas/platform.schema.js.map +1 -1
- package/dist/core/schemas/task.schema.d.ts.map +1 -1
- package/dist/core/schemas/task.schema.js +1 -0
- package/dist/core/schemas/task.schema.js.map +1 -1
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +2 -1
- package/dist/core/state.js.map +1 -1
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +4 -0
- package/dist/core/transaction.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ask-context — Rich Context 构建器
|
|
4
|
+
*
|
|
5
|
+
* 为宿主AI / LLM 提供决策所需的完整上下文:
|
|
6
|
+
* 1. 本地引擎候选意图(让AI做选择题而非填空题)
|
|
7
|
+
* 2. 项目阶段与生命周期状态
|
|
8
|
+
* 3. 当前迭代/任务上下文
|
|
9
|
+
* 4. 最近命令历史(时间序列行为模式)
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.buildAskContext = buildAskContext;
|
|
13
|
+
exports.formatContextForHostAi = formatContextForHostAi;
|
|
14
|
+
const path_1 = require("path");
|
|
15
|
+
const context_1 = require("./context");
|
|
16
|
+
const logger_1 = require("../utils/logger");
|
|
17
|
+
// ═══════════════════════════════════════════════════════════
|
|
18
|
+
// Rich Context 构建入口
|
|
19
|
+
// ═══════════════════════════════════════════════════════════
|
|
20
|
+
/**
|
|
21
|
+
* 构建完整的 AskContext,供宿主AI / LLM 做语义判断
|
|
22
|
+
*/
|
|
23
|
+
async function buildAskContext(input, localResults) {
|
|
24
|
+
const candidates = localResults.slice(0, 4).map(r => ({
|
|
25
|
+
intent: r.intent,
|
|
26
|
+
command: r.command,
|
|
27
|
+
confidence: r.confidence,
|
|
28
|
+
matchedTriggers: r.matchedTriggers.slice(0, 3),
|
|
29
|
+
extractedParams: r.extractedParams,
|
|
30
|
+
}));
|
|
31
|
+
const projectContext = await buildProjectContext();
|
|
32
|
+
return {
|
|
33
|
+
userInput: input,
|
|
34
|
+
localConfidence: localResults[0]?.confidence || 0,
|
|
35
|
+
localCandidates: candidates,
|
|
36
|
+
projectContext,
|
|
37
|
+
availableCommands: getAvailableCommandsForPhase(projectContext.phase),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// ═══════════════════════════════════════════════════════════
|
|
41
|
+
// 项目上下文构建
|
|
42
|
+
// ═══════════════════════════════════════════════════════════
|
|
43
|
+
async function buildProjectContext() {
|
|
44
|
+
const ctx = await (0, context_1.loadContext)();
|
|
45
|
+
const iteration = ctx.currentIteration || (await (0, context_1.detectActiveIteration)());
|
|
46
|
+
return {
|
|
47
|
+
phase: detectProjectPhase(),
|
|
48
|
+
currentIteration: iteration,
|
|
49
|
+
currentTask: ctx.currentTask || '',
|
|
50
|
+
recentCommands: (ctx.history || []).slice(-5).map(h => ({
|
|
51
|
+
command: h.command,
|
|
52
|
+
timestamp: h.timestamp,
|
|
53
|
+
iteration: h.iteration,
|
|
54
|
+
task: h.task,
|
|
55
|
+
})),
|
|
56
|
+
iterationStats: {
|
|
57
|
+
pending: ctx.pendingTasks || 0,
|
|
58
|
+
inProgress: ctx.inProgressTasks || 0,
|
|
59
|
+
completed: ctx.completedTasks || 0,
|
|
60
|
+
blocked: ctx.blockedTasks || 0,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ═══════════════════════════════════════════════════════════
|
|
65
|
+
// 项目阶段检测
|
|
66
|
+
// ═══════════════════════════════════════════════════════════
|
|
67
|
+
/**
|
|
68
|
+
* 根据当前目录结构和上下文推断项目阶段
|
|
69
|
+
*/
|
|
70
|
+
function detectProjectPhase() {
|
|
71
|
+
// 基于文件存在性推断阶段
|
|
72
|
+
const cwd = process.cwd();
|
|
73
|
+
// 检查是否有迭代目录
|
|
74
|
+
try {
|
|
75
|
+
const { readdirSync } = require('fs');
|
|
76
|
+
const dirs = readdirSync(cwd, { withFileTypes: true })
|
|
77
|
+
.filter((d) => d.isDirectory())
|
|
78
|
+
.map((d) => d.name);
|
|
79
|
+
const hasIterations = dirs.some((d) => d.startsWith('Iteration-'));
|
|
80
|
+
const hasSpeccore = dirs.includes('.speccore');
|
|
81
|
+
if (!hasSpeccore)
|
|
82
|
+
return 'init';
|
|
83
|
+
if (!hasIterations)
|
|
84
|
+
return 'analyze';
|
|
85
|
+
// 检查最新迭代的内容
|
|
86
|
+
const iterDirs = dirs.filter((d) => d.startsWith('Iteration-')).sort();
|
|
87
|
+
const latestIter = iterDirs[iterDirs.length - 1];
|
|
88
|
+
if (!latestIter)
|
|
89
|
+
return 'analyze';
|
|
90
|
+
const iterPath = (0, path_1.join)(cwd, latestIter);
|
|
91
|
+
const { existsSync } = require('fs');
|
|
92
|
+
const hasTasks = existsSync((0, path_1.join)(iterPath, '030-tasks'));
|
|
93
|
+
const hasSpecs = existsSync((0, path_1.join)(iterPath, '020-specs'));
|
|
94
|
+
const hasRequirements = existsSync((0, path_1.join)(iterPath, '010-requirements'));
|
|
95
|
+
if (!hasRequirements)
|
|
96
|
+
return 'init';
|
|
97
|
+
if (!hasSpecs)
|
|
98
|
+
return 'analyze';
|
|
99
|
+
if (!hasTasks)
|
|
100
|
+
return 'split';
|
|
101
|
+
// 检查是否有执行中的任务
|
|
102
|
+
if (hasTasks) {
|
|
103
|
+
try {
|
|
104
|
+
const taskDirs = readdirSync((0, path_1.join)(iterPath, '030-tasks'), { withFileTypes: true })
|
|
105
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('Task-'));
|
|
106
|
+
if (taskDirs.length === 0)
|
|
107
|
+
return 'plan';
|
|
108
|
+
}
|
|
109
|
+
catch { }
|
|
110
|
+
}
|
|
111
|
+
return 'execute';
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
logger_1.logger.debug(`阶段检测失败: ${e.message}`);
|
|
115
|
+
return 'unknown';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ═══════════════════════════════════════════════════════════
|
|
119
|
+
// 阶段化命令推荐
|
|
120
|
+
// ═══════════════════════════════════════════════════════════
|
|
121
|
+
/**
|
|
122
|
+
* 根据项目阶段返回当前最相关的命令列表
|
|
123
|
+
* 用于缩小宿主AI的决策范围
|
|
124
|
+
*/
|
|
125
|
+
function getAvailableCommandsForPhase(phase) {
|
|
126
|
+
const base = ['help', 'dashboard', 'context'];
|
|
127
|
+
switch (phase) {
|
|
128
|
+
case 'init':
|
|
129
|
+
return [...base, 'init', 'iteration create', 'doc2spec'];
|
|
130
|
+
case 'analyze':
|
|
131
|
+
return [...base, 'analyze', 'doc2spec', 'split', 'validate'];
|
|
132
|
+
case 'split':
|
|
133
|
+
return [...base, 'split', 'task new', 'plan', 'analyze'];
|
|
134
|
+
case 'plan':
|
|
135
|
+
return [...base, 'plan', 'execute', 'task new', 'validate'];
|
|
136
|
+
case 'execute':
|
|
137
|
+
return [...base, 'execute', 'plan', 'pr', 'done', 'change', 'sync'];
|
|
138
|
+
case 'done':
|
|
139
|
+
return [...base, 'done', 'pr', 'sync', 'spec2doc'];
|
|
140
|
+
default:
|
|
141
|
+
return base;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// ═══════════════════════════════════════════════════════════
|
|
145
|
+
// Context 序列化(用于文件协议 / stdout 标记)
|
|
146
|
+
// ═══════════════════════════════════════════════════════════
|
|
147
|
+
/**
|
|
148
|
+
* 将 AskContext 序列化为宿主AI可读的结构化文本
|
|
149
|
+
*/
|
|
150
|
+
function formatContextForHostAi(context) {
|
|
151
|
+
const lines = [];
|
|
152
|
+
lines.push('## 用户输入');
|
|
153
|
+
lines.push(`"${context.userInput}"`);
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('## 本地引擎候选(按置信度排序)');
|
|
156
|
+
if (context.localCandidates.length === 0) {
|
|
157
|
+
lines.push('本地引擎未识别到匹配意图。');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
for (const c of context.localCandidates) {
|
|
161
|
+
const params = Object.entries(c.extractedParams)
|
|
162
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
163
|
+
.join(', ');
|
|
164
|
+
lines.push(`- ${c.command} (${c.confidence}%) — 触发: ${c.matchedTriggers.join(', ')}${params ? ` | 参数: ${params}` : ''}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
lines.push('');
|
|
168
|
+
lines.push('## 项目上下文');
|
|
169
|
+
const pc = context.projectContext;
|
|
170
|
+
lines.push(`- 当前阶段: ${pc.phase}`);
|
|
171
|
+
lines.push(`- 活跃迭代: ${pc.currentIteration || '无'}`);
|
|
172
|
+
lines.push(`- 当前任务: ${pc.currentTask || '无'}`);
|
|
173
|
+
lines.push(`- 任务统计: 待处理${pc.iterationStats.pending} / 进行中${pc.iterationStats.inProgress} / 已完成${pc.iterationStats.completed} / 阻塞${pc.iterationStats.blocked}`);
|
|
174
|
+
if (pc.recentCommands.length > 0) {
|
|
175
|
+
lines.push(`- 最近命令: ${pc.recentCommands.map(c => c.command).join(' → ')}`);
|
|
176
|
+
}
|
|
177
|
+
lines.push('');
|
|
178
|
+
lines.push('## 当前阶段推荐命令');
|
|
179
|
+
lines.push(context.availableCommands.join(', '));
|
|
180
|
+
lines.push('');
|
|
181
|
+
lines.push('## 你的任务');
|
|
182
|
+
lines.push('根据用户输入、本地引擎候选和项目上下文,判断最佳意图。');
|
|
183
|
+
lines.push('如果本地候选已足够明确,直接选择;如果模糊,结合项目阶段推断。');
|
|
184
|
+
lines.push('返回格式: {"intent": "...", "command": "...", "confidence": 95, "reasoning": "..."}');
|
|
185
|
+
return lines.join('\n');
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=ask-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-context.js","sourceRoot":"","sources":["../../src/core/ask-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask-engine.d.ts","sourceRoot":"","sources":["../../src/core/ask-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"ask-engine.d.ts","sourceRoot":"","sources":["../../src/core/ask-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,aAAa;AACb,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;AAE/E,aAAa;AACb,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,kBAAkB;AAClB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kBAAkB;AAClB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,aAAa;AACb,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACjE;AAMD,QAAA,MAAM,UAAU,EAAE,gBAAgB,EAiDjC,CAAC;AAMF,QAAA,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CA4B7C,CAAC;AAMF,iBAAiB;AACjB,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CA0CnD;AA8cD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iBAAiB;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAyD1G;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS;IACT,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,iBAAiB;IACjB,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/D,qBAAqB;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzE;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4JhF;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAsHjE;AAqID,uCAAuC;AACvC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA4BjD;AAED,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/core/ask-engine.js
CHANGED
|
@@ -47,6 +47,9 @@ const logger_1 = require("../utils/logger");
|
|
|
47
47
|
const intent_recognition_1 = require("./intent-recognition");
|
|
48
48
|
const ask_llm_1 = require("./ask-llm");
|
|
49
49
|
const ask_host_ai_1 = require("./ask-host-ai");
|
|
50
|
+
const ask_config_1 = require("./ask-config");
|
|
51
|
+
const intent_cache_1 = require("./intent-cache");
|
|
52
|
+
const ask_context_1 = require("./ask-context");
|
|
50
53
|
// ============================================================
|
|
51
54
|
// 命令知识库
|
|
52
55
|
// ============================================================
|
|
@@ -81,7 +84,7 @@ const COMMAND_KB = [
|
|
|
81
84
|
usage: 'speccore dev [--auto] [--from <phase>] [--to <phase>]', examples: ['speccore dev --auto', 'speccore dev --from analyze --to execute'], related: ['execute', 'plan'], triggers: ['dev', '流水线', '自动', '级联'] },
|
|
82
85
|
{ name: 'task', aliases: ['tk'], description: '任务管理:创建/列表/状态。子命令: new, list, status',
|
|
83
86
|
usage: 'speccore task new --name <name> [--id <id>] | speccore task list | speccore task status', examples: ['speccore task new --name "用户登录"', 'speccore task list'], related: ['plan', 'execute'], triggers: ['task', '任务列表', '查看任务', '列出任务'] },
|
|
84
|
-
{ name: 'schedule', aliases: ['sc'], description: '[
|
|
87
|
+
{ name: 'schedule', aliases: ['sc'], description: '[已废弃] 定时调度功能已废弃,请勿使用',
|
|
85
88
|
usage: 'speccore schedule create --at "HH:mm" | speccore schedule list | speccore schedule cancel --id <id>',
|
|
86
89
|
examples: ['speccore schedule list', 'speccore schedule retry --id sch-xxx'],
|
|
87
90
|
related: ['plan', 'execute', 'task'], triggers: ['调度', '定时', 'schedule', '重调度', 'retry', '守护进程', 'daemon', '队列'] },
|
|
@@ -288,7 +291,7 @@ function handleGuide(input) {
|
|
|
288
291
|
else if (/新功能|feature|登录|注册|支付|创建.*功能|做.*功能/i.test(input)) {
|
|
289
292
|
matchedWorkflow = WORKFLOWS['new feature'];
|
|
290
293
|
workflowName = '新功能开发全流程';
|
|
291
|
-
|
|
294
|
+
return null;
|
|
292
295
|
}
|
|
293
296
|
else if (/批量|分批|batch|队列/i.test(input)) {
|
|
294
297
|
matchedWorkflow = WORKFLOWS['batch execute'];
|
|
@@ -296,11 +299,11 @@ function handleGuide(input) {
|
|
|
296
299
|
}
|
|
297
300
|
else if (/创建.*迭代创建.*迭代/i.test(input)) {
|
|
298
301
|
// 不应该进 guide 模式——让调用方降级到 match
|
|
299
|
-
|
|
302
|
+
return null;
|
|
300
303
|
}
|
|
301
304
|
else {
|
|
302
|
-
// 无匹配工作流 →
|
|
303
|
-
|
|
305
|
+
// 无匹配工作流 → 返回 null,由 askEngine 降级到 handleMatch
|
|
306
|
+
return null;
|
|
304
307
|
}
|
|
305
308
|
const steps = matchedWorkflow.map(s => ` ${s.order}. speccore ${s.command}${s.args ? ' ' + s.args : ''}` +
|
|
306
309
|
`\n → ${s.explanation}`).join('\n\n');
|
|
@@ -333,10 +336,11 @@ function handleGuide(input) {
|
|
|
333
336
|
async function handleMatch(input) {
|
|
334
337
|
// 优先用 KB 精确匹配
|
|
335
338
|
const kbMatch = matchCommandInKB(input);
|
|
339
|
+
const config = await (0, ask_config_1.loadAskConfig)();
|
|
336
340
|
const results = await (0, intent_recognition_1.recognizeIntent)(input);
|
|
337
341
|
const best = results[0];
|
|
338
342
|
// 如果 KB 有匹配且置信度高于意图识别,用 KB(但要整合意图识别的参数)
|
|
339
|
-
if (kbMatch && (!best || best.confidence <
|
|
343
|
+
if (kbMatch && (!best || best.confidence < 60)) {
|
|
340
344
|
const params = best?.extractedParams || {};
|
|
341
345
|
let fullCommand = `speccore ${kbMatch.name}`;
|
|
342
346
|
const paramNotes = [];
|
|
@@ -365,7 +369,7 @@ async function handleMatch(input) {
|
|
|
365
369
|
return { mode: 'match', summary: '未识别到匹配命令', detail: '我无法完全理解你的意图。试试:\n speccore help — 查看命令列表\n 或更详细地描述你想做什么', commands: [] };
|
|
366
370
|
}
|
|
367
371
|
// ── 低置信度拒绝 ──
|
|
368
|
-
if (best.confidence <
|
|
372
|
+
if (best.confidence < config.routing.lowThreshold) {
|
|
369
373
|
return {
|
|
370
374
|
mode: 'match',
|
|
371
375
|
summary: '置信度过低',
|
|
@@ -394,11 +398,11 @@ async function handleMatch(input) {
|
|
|
394
398
|
let fullCommand;
|
|
395
399
|
const paramNotes = [];
|
|
396
400
|
// task-create / iteration-create 需要子命令
|
|
397
|
-
if (best.
|
|
401
|
+
if (best.command === 'task-create') {
|
|
398
402
|
const name = params.name || params.desc || input.slice(0, 30);
|
|
399
403
|
fullCommand = `speccore task new -n "${name}"`;
|
|
400
404
|
}
|
|
401
|
-
else if (best.
|
|
405
|
+
else if (best.command === 'iteration-create') {
|
|
402
406
|
const name = params.name || input.slice(0, 20);
|
|
403
407
|
fullCommand = `speccore iteration create -n "${name}"`;
|
|
404
408
|
}
|
|
@@ -453,7 +457,7 @@ async function handleMatch(input) {
|
|
|
453
457
|
summary: `匹配到: ${best.intent} (${best.confidence}%) → ${fullCommand}`,
|
|
454
458
|
detail,
|
|
455
459
|
commands: [best.command],
|
|
456
|
-
autoExec: best.confidence >=
|
|
460
|
+
autoExec: best.confidence >= config.routing.highThreshold ? {
|
|
457
461
|
command: fullCommand.replace(/^speccore /, '').split(' ')[0], // 主命令
|
|
458
462
|
args: fullCommand.replace(/^speccore [a-z-]+ /, ''), // 子命令 + 参数
|
|
459
463
|
confirm: true,
|
|
@@ -573,8 +577,16 @@ function handlePipeline(input) {
|
|
|
573
577
|
} : undefined,
|
|
574
578
|
};
|
|
575
579
|
}
|
|
576
|
-
// 默认返回 guide
|
|
577
|
-
|
|
580
|
+
// 默认返回 guide(降级到 match 若 guide 无匹配)
|
|
581
|
+
const guideResult = handleGuide(input);
|
|
582
|
+
if (guideResult)
|
|
583
|
+
return guideResult;
|
|
584
|
+
return {
|
|
585
|
+
mode: 'match',
|
|
586
|
+
summary: '未匹配到编排模式',
|
|
587
|
+
detail: '无法识别为 Pipeline 模式,请尝试更具体的描述。',
|
|
588
|
+
commands: [],
|
|
589
|
+
};
|
|
578
590
|
}
|
|
579
591
|
function buildPipelineDetail(steps, input) {
|
|
580
592
|
// fill template placeholders
|
|
@@ -833,7 +845,7 @@ async function synthesizeIntent(input) {
|
|
|
833
845
|
// b) 置信度低于阈值
|
|
834
846
|
// c) 参数严重缺失(如无任务名就创建任务)
|
|
835
847
|
// 否则 AI 自主补全后直接执行
|
|
836
|
-
const isProblematic = gaps.length > 0 || confidence <
|
|
848
|
+
const isProblematic = gaps.length > 0 || confidence < 60;
|
|
837
849
|
// 关键参数缺失 → 提问
|
|
838
850
|
if (commands.includes('task') && !parsed.name && !parsed.type) {
|
|
839
851
|
questions.push('请描述你要创建的任务类型和名称(如:创建一个登录功能的bug修复任务)');
|
|
@@ -863,8 +875,9 @@ async function synthesizeIntent(input) {
|
|
|
863
875
|
};
|
|
864
876
|
}
|
|
865
877
|
async function askEngine(input) {
|
|
866
|
-
//
|
|
867
|
-
//
|
|
878
|
+
// ═══════════════════════════════════════════════════════════
|
|
879
|
+
// 第零层: 确定性操作直接路由(零成本,最高优先级)
|
|
880
|
+
// ═══════════════════════════════════════════════════════════
|
|
868
881
|
if (/切换.*[到至].*迭代|上下文.*切换|切换到/.test(input)) {
|
|
869
882
|
const iterMatch = input.match(/Iteration[- ]?\S+|Q\d+|sample/i);
|
|
870
883
|
const raw = iterMatch ? iterMatch[0] : '';
|
|
@@ -879,64 +892,150 @@ async function askEngine(input) {
|
|
|
879
892
|
};
|
|
880
893
|
}
|
|
881
894
|
}
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
895
|
+
// ═══════════════════════════════════════════════════════════
|
|
896
|
+
// 加载统一配置(环境变量 > ask.json > 默认值)
|
|
897
|
+
// ═══════════════════════════════════════════════════════════
|
|
898
|
+
const config = await (0, ask_config_1.loadAskConfig)();
|
|
899
|
+
// ═══════════════════════════════════════════════════════════
|
|
900
|
+
// 第一层: 意图缓存(零成本,高频意图越用越快)
|
|
901
|
+
// ═══════════════════════════════════════════════════════════
|
|
902
|
+
if (config.routing.cacheEnabled) {
|
|
903
|
+
const cached = await (0, intent_cache_1.getCachedIntent)(input);
|
|
904
|
+
if (cached) {
|
|
905
|
+
logger_1.logger.info(`💾 缓存命中: "${input.slice(0, 30)}..."`);
|
|
906
|
+
return cached;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
// ═══════════════════════════════════════════════════════════
|
|
910
|
+
// 第二层: 本地意图引擎(关键词+正则+上下文)
|
|
911
|
+
// ═══════════════════════════════════════════════════════════
|
|
912
|
+
const mode = classifyMode(input);
|
|
913
|
+
let localResult;
|
|
914
|
+
let localCandidates = [];
|
|
915
|
+
switch (mode) {
|
|
916
|
+
case 'explain':
|
|
917
|
+
localResult = handleExplain(input);
|
|
918
|
+
break;
|
|
919
|
+
case 'guide': {
|
|
920
|
+
const guide = handleGuide(input);
|
|
921
|
+
localResult = guide || await handleMatch(input);
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
case 'pipeline':
|
|
925
|
+
localResult = handlePipeline(input);
|
|
926
|
+
break;
|
|
927
|
+
default:
|
|
928
|
+
localCandidates = await (0, intent_recognition_1.recognizeIntent)(input);
|
|
929
|
+
localResult = await handleMatch(input);
|
|
930
|
+
}
|
|
931
|
+
// 计算本地置信度(explain/guide/pipeline 视为高置信度)
|
|
932
|
+
const localConfidence = localCandidates[0]?.confidence ||
|
|
933
|
+
(localResult.autoExec ? 85 : (mode === 'explain' || mode === 'pipeline') ? 90 : 55);
|
|
934
|
+
// --rules / forceHostAi 强制所有请求走 AI
|
|
935
|
+
const forceHostAi = input.includes('--rules') || config.rules.forceHostAi;
|
|
936
|
+
// ═══════════════════════════════════════════════════════════
|
|
937
|
+
// 第三层: 三段式动态路由策略
|
|
938
|
+
// ═══════════════════════════════════════════════════════════
|
|
939
|
+
//
|
|
940
|
+
// ┌─────────────────────────────────────────────────────┐
|
|
941
|
+
// │ ≥ highThreshold (70) │ 本地直接执行,不打扰 AI │
|
|
942
|
+
// │ lowThreshold~high │ 双路并行,取更优结果 │
|
|
943
|
+
// │ < lowThreshold (45) │ 直接交给 AI,本地只提参数 │
|
|
944
|
+
// └─────────────────────────────────────────────────────┘
|
|
945
|
+
// ── 段1: 高分区 ── 本地引擎直接执行,零AI成本 ──
|
|
946
|
+
if (!forceHostAi && localConfidence >= config.routing.highThreshold) {
|
|
947
|
+
if (config.routing.cacheEnabled)
|
|
948
|
+
await (0, intent_cache_1.cacheIntent)(input, localResult, 'local');
|
|
949
|
+
return localResult;
|
|
950
|
+
}
|
|
951
|
+
// ── 段2: 中分区 ── 双路并行,取更优结果 ──
|
|
952
|
+
if (!forceHostAi && localConfidence >= config.routing.lowThreshold) {
|
|
953
|
+
// 本地结果已就绪,同时触发宿主AI
|
|
954
|
+
const hostPromise = tryHostAiEnhanced(input, localCandidates);
|
|
955
|
+
const hostResult = await hostPromise;
|
|
956
|
+
if (hostResult && hostResult.commands.length > 0) {
|
|
957
|
+
// AI 返回有效结果 → 优先AI(语义理解更精准)
|
|
958
|
+
if (config.routing.cacheEnabled)
|
|
959
|
+
await (0, intent_cache_1.cacheIntent)(input, hostResult, 'host-ai');
|
|
960
|
+
return hostResult;
|
|
961
|
+
}
|
|
962
|
+
// AI 不可用或失败 → 回退本地
|
|
963
|
+
if (config.routing.cacheEnabled)
|
|
964
|
+
await (0, intent_cache_1.cacheIntent)(input, localResult, 'local');
|
|
965
|
+
return localResult;
|
|
966
|
+
}
|
|
967
|
+
// ── 段3: 低分区 ── 直接交给AI,本地只负责提取参数 ──
|
|
968
|
+
// 此时本地引擎置信度不足,优先AI语义判断
|
|
969
|
+
if (forceHostAi || config.routing.autoHostAi) {
|
|
970
|
+
if (mode === 'match' || mode === 'ambiguous') {
|
|
971
|
+
const hostResult = await tryHostAiEnhanced(input, localCandidates);
|
|
972
|
+
if (hostResult && hostResult.commands.length > 0) {
|
|
973
|
+
if (config.routing.cacheEnabled)
|
|
974
|
+
await (0, intent_cache_1.cacheIntent)(input, hostResult, 'host-ai');
|
|
975
|
+
return hostResult;
|
|
893
976
|
}
|
|
894
|
-
|
|
895
|
-
|
|
977
|
+
// 自有LLM冗余(用户配置了provider时启用)
|
|
978
|
+
const llmResult = await tryLlmProviders(input, config);
|
|
979
|
+
if (llmResult) {
|
|
980
|
+
if (config.routing.cacheEnabled)
|
|
981
|
+
await (0, intent_cache_1.cacheIntent)(input, llmResult, 'llm');
|
|
982
|
+
return llmResult;
|
|
896
983
|
}
|
|
897
984
|
}
|
|
898
|
-
// 兜底: 关键词匹配
|
|
899
|
-
const mode = classifyMode(input);
|
|
900
|
-
switch (mode) {
|
|
901
|
-
case 'explain': return handleExplain(input);
|
|
902
|
-
case 'guide': return handleGuide(input);
|
|
903
|
-
case 'pipeline': return handlePipeline(input);
|
|
904
|
-
default: return handleMatch(input);
|
|
905
|
-
}
|
|
906
985
|
}
|
|
907
|
-
// ──
|
|
986
|
+
// ── 兜底 ── 所有AI路径都失败,返回本地结果
|
|
987
|
+
return localResult;
|
|
988
|
+
}
|
|
989
|
+
// ═══════════════════════════════════════════════════════════
|
|
990
|
+
// 宿主AI增强(传入Rich Context)
|
|
991
|
+
// ═══════════════════════════════════════════════════════════
|
|
992
|
+
async function tryHostAiEnhanced(input, candidates) {
|
|
993
|
+
const context = await (0, ask_context_1.buildAskContext)(input, candidates);
|
|
908
994
|
try {
|
|
909
|
-
const hostResult = await (0, ask_host_ai_1.tryHostAi)('ask', input
|
|
995
|
+
const hostResult = await (0, ask_host_ai_1.tryHostAi)('ask', input, {
|
|
996
|
+
...context,
|
|
997
|
+
formattedContext: (0, ask_context_1.formatContextForHostAi)(context),
|
|
998
|
+
});
|
|
910
999
|
if (hostResult) {
|
|
911
|
-
logger_1.logger.info(`🤖 宿主
|
|
912
|
-
hostResult._source = 'host';
|
|
1000
|
+
logger_1.logger.info(`🤖 宿主AI增强成功: ${hostResult.summary || hostResult.mode}`);
|
|
913
1001
|
return hostResult;
|
|
914
1002
|
}
|
|
915
1003
|
}
|
|
916
|
-
catch (e) {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1004
|
+
catch (e) {
|
|
1005
|
+
logger_1.logger.debug(`宿主AI增强失败: ${e.message}`);
|
|
1006
|
+
}
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
// ═══════════════════════════════════════════════════════════
|
|
1010
|
+
// 多LLM冗余路由(用户配置了provider时启用,默认禁用)
|
|
1011
|
+
// ═══════════════════════════════════════════════════════════
|
|
1012
|
+
async function tryLlmProviders(input, config) {
|
|
1013
|
+
const enabledProviders = (config.llmProviders || [])
|
|
1014
|
+
.filter(p => p.enabled)
|
|
1015
|
+
.sort((a, b) => a.priority - b.priority);
|
|
1016
|
+
if (enabledProviders.length === 0)
|
|
1017
|
+
return null;
|
|
1018
|
+
for (const provider of enabledProviders) {
|
|
1019
|
+
try {
|
|
1020
|
+
// 注入provider配置到环境变量(临时)
|
|
1021
|
+
if (provider.endpoint)
|
|
1022
|
+
process.env.SPECCORE_LLM_ENDPOINT = provider.endpoint;
|
|
1023
|
+
if (provider.apiKey)
|
|
1024
|
+
process.env.SPECCORE_LLM_KEY = provider.apiKey;
|
|
1025
|
+
if (provider.model)
|
|
1026
|
+
process.env.SPECCORE_LLM_MODEL = provider.model;
|
|
1027
|
+
const llmResult = await (0, ask_llm_1.askWithLlm)(input);
|
|
1028
|
+
if (llmResult && llmResult.commands.length > 0) {
|
|
1029
|
+
logger_1.logger.info(`🧠 ${provider.name} 响应成功: ${llmResult.mode}`);
|
|
1030
|
+
llmResult._source = 'llm';
|
|
1031
|
+
return llmResult;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
catch (e) {
|
|
1035
|
+
logger_1.logger.warn(`${provider.name} 不可用: ${e.message}`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return null;
|
|
940
1039
|
}
|
|
941
1040
|
/** 用规则引擎补充 LLM 结果的内容 */
|
|
942
1041
|
function enrichWithRules(llmResult, input) {
|