@raolin2025/claude-code-node 2.8.14 → 2.8.15
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raolin2025/claude-code-node",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.15",
|
|
4
4
|
"description": "Node.js AI Code Agent CLI - Zero dependencies, pure JavaScript, security hardened, multi-channel notifications, Telegram & QQ Bot remote programming, rich media upload, multi-account management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/core/index.js",
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
buildSmallModelSystemPrompt,
|
|
19
19
|
SMALL_MODEL_SYSTEM_PROMPT,
|
|
20
20
|
isSmallModelEnabled,
|
|
21
|
+
buildMultiStepPlan,
|
|
22
|
+
buildCombinedGuidance,
|
|
21
23
|
} from '../core/small-model.js'
|
|
22
24
|
|
|
23
25
|
// ---- 敷衍输出检测 ----
|
|
@@ -145,3 +147,45 @@ test('小模型开关判断', () => {
|
|
|
145
147
|
assert.equal(isSmallModelEnabled({ smallModel: false }), false)
|
|
146
148
|
assert.equal(isSmallModelEnabled(undefined), false)
|
|
147
149
|
})
|
|
150
|
+
|
|
151
|
+
// ---- system prompt 注入 cwd ----
|
|
152
|
+
test('system prompt 强化:注入当前工作目录', () => {
|
|
153
|
+
const base = 'You are cc-node.'
|
|
154
|
+
const enhanced = buildSmallModelSystemPrompt(base, { cwd: 'D:\\workspace\\miniQMT-trader' })
|
|
155
|
+
assert.ok(enhanced.includes('当前工作目录'), '应包含工作目录说明')
|
|
156
|
+
assert.ok(enhanced.includes('miniQMT-trader'), '应包含实际 cwd 路径')
|
|
157
|
+
assert.ok(enhanced.includes('不要猜测其它绝对路径'), '应提示不要瞎猜路径')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// ---- 多步计划拆解 ----
|
|
161
|
+
test('多步计划:识别"按计划实现"任务', () => {
|
|
162
|
+
const r = buildMultiStepPlan('按计划实现第一阶段', { cwd: 'D:\\x' })
|
|
163
|
+
assert.ok(r && r.isMultiStep, '应识别为多步任务')
|
|
164
|
+
assert.ok(r.plan.includes('步骤 1'), '应有步骤 1')
|
|
165
|
+
assert.ok(r.plan.includes('Read'), '步骤 1 应用 Read')
|
|
166
|
+
assert.ok(r.plan.includes('DEVELOPMENT_PLAN.md'), '应引用计划文件')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('多步计划:提取指令里的计划文件名', () => {
|
|
170
|
+
const r = buildMultiStepPlan('实现DEV_PLAN.md里的第二阶段', { cwd: 'D:\\x' })
|
|
171
|
+
assert.ok(r && r.plan.includes('DEV_PLAN.md'), '应提取指令中的计划文件名')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('多步计划:非实现任务返回 null', () => {
|
|
175
|
+
assert.equal(buildMultiStepPlan('阅读DEVELOPMENT_PLAN.md', { enable: true }), null)
|
|
176
|
+
assert.equal(buildMultiStepPlan('你好', { enable: true }), null)
|
|
177
|
+
assert.equal(buildMultiStepPlan('按计划实现第一阶段', { enable: false }), null)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('多步计划:combined guidance 优先走多步拆解', () => {
|
|
181
|
+
const g = buildCombinedGuidance('按计划实现第一阶段', { cwd: 'D:\\x', enable: true })
|
|
182
|
+
assert.ok(g, '应生成引导')
|
|
183
|
+
assert.ok(g.includes('多步执行计划'), '应走多步计划')
|
|
184
|
+
assert.ok(g.includes('步骤 1'), '应包含步骤引导')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('多步计划:普通任务走简单意图引导', () => {
|
|
188
|
+
const g = buildCombinedGuidance('阅读DEVELOPMENT_PLAN.md文档', { cwd: 'D:\\x', enable: true })
|
|
189
|
+
assert.ok(g, '应生成引导')
|
|
190
|
+
assert.ok(g.includes('任务引导'), '应走简单意图引导')
|
|
191
|
+
})
|
package/src/core/query-engine.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
isSmallModelEnabled,
|
|
20
20
|
buildSmallModelSystemPrompt,
|
|
21
21
|
isFillerResponse,
|
|
22
|
-
|
|
22
|
+
buildCombinedGuidance,
|
|
23
23
|
selectRelevantTools,
|
|
24
24
|
RETRY_GUIDANCE,
|
|
25
25
|
} from './small-model.js'
|
|
@@ -228,11 +228,14 @@ export class QueryEngine {
|
|
|
228
228
|
// 发送前硬校验:工具结果可能已使上下文超窗,确保 ≤ 窗口(摘要优先 + 滑动窗口裁剪兜底)
|
|
229
229
|
this._ensureFitWindow()
|
|
230
230
|
|
|
231
|
-
//
|
|
231
|
+
// 小模型模式:首轮若识别到意图/多步计划,把引导【追加到首条 system 消息】。
|
|
232
232
|
// 注意:不能 push 一条新的 system 消息到中间——llama.cpp/Jinja 严格要求
|
|
233
233
|
// system 消息必须在对话开头,中间插 system 会报 "System message must be at the beginning" (500)。
|
|
234
234
|
if (smallModel && !intentGuided && this.state.messages.length > 0) {
|
|
235
|
-
const guidance =
|
|
235
|
+
const guidance = buildCombinedGuidance(userMessage.content, {
|
|
236
|
+
enable: true,
|
|
237
|
+
cwd: this.config.cwd,
|
|
238
|
+
})
|
|
236
239
|
if (guidance) {
|
|
237
240
|
// 找到第一条 system 消息,把引导拼接进去(保持 system 全在开头)
|
|
238
241
|
const sysIdx = this.state.messages.findIndex(m => m.role === 'system')
|
|
@@ -246,7 +249,7 @@ export class QueryEngine {
|
|
|
246
249
|
// 没有 system 消息 → 作为 user 引导插入(跟随在最新 user 之后作为新 user)
|
|
247
250
|
this.state.messages.push({ role: 'user', content: guidance })
|
|
248
251
|
}
|
|
249
|
-
if (this.config.verbose) console.error('[small-model]
|
|
252
|
+
if (this.config.verbose) console.error('[small-model] 已注入引导(并入首条 system):' + guidance.slice(0, 60) + '...')
|
|
250
253
|
}
|
|
251
254
|
intentGuided = true
|
|
252
255
|
}
|
|
@@ -315,10 +318,10 @@ export class QueryEngine {
|
|
|
315
318
|
_buildRequest(messages) {
|
|
316
319
|
const request = []
|
|
317
320
|
|
|
318
|
-
//
|
|
321
|
+
// 系统提示(小模型模式:追加强制工具调用引导 + 当前工作目录)
|
|
319
322
|
if (this.config.systemPrompt) {
|
|
320
323
|
const sysContent = isSmallModelEnabled(this.config)
|
|
321
|
-
? buildSmallModelSystemPrompt(this.config.systemPrompt)
|
|
324
|
+
? buildSmallModelSystemPrompt(this.config.systemPrompt, { cwd: this.config.cwd })
|
|
322
325
|
: this.config.systemPrompt
|
|
323
326
|
request.push({ role: 'system', content: sysContent })
|
|
324
327
|
}
|
package/src/core/small-model.js
CHANGED
|
@@ -97,10 +97,18 @@ export const SMALL_MODEL_SYSTEM_PROMPT = `
|
|
|
97
97
|
/**
|
|
98
98
|
* 生成小模型模式下的完整 system prompt(基础 prompt + 强化引导)
|
|
99
99
|
* @param {string} basePrompt — 基础 system prompt
|
|
100
|
+
* @param {object} [opts]
|
|
101
|
+
* @param {string} [opts.cwd] — 当前工作目录(注入给模型,避免它瞎猜文件路径)
|
|
100
102
|
* @returns {string}
|
|
101
103
|
*/
|
|
102
|
-
export function buildSmallModelSystemPrompt(basePrompt) {
|
|
103
|
-
|
|
104
|
+
export function buildSmallModelSystemPrompt(basePrompt, opts = {}) {
|
|
105
|
+
let prompt = basePrompt + SMALL_MODEL_SYSTEM_PROMPT
|
|
106
|
+
// 注入当前工作目录:小模型没有上下文意识,不知道用户在哪个项目目录,
|
|
107
|
+
// 不给 cwd 它会瞎猜绝对路径(如 /home/user/repos/...),导致 File not found。
|
|
108
|
+
if (opts.cwd) {
|
|
109
|
+
prompt += `\n\n## 当前工作目录\n用户当前的工作目录是:\`${opts.cwd}\`\n查找/读取/写入文件时,优先在此目录(或此目录的子目录)中定位文件,不要猜测其它绝对路径。\n`
|
|
110
|
+
}
|
|
111
|
+
return prompt
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
// ---- 重试追加强引导(层 A)----
|
|
@@ -118,6 +126,21 @@ export const RETRY_GUIDANCE = `[系统提示] 你刚才没有调用任何工具
|
|
|
118
126
|
// note: 给模型的动作说明
|
|
119
127
|
|
|
120
128
|
const INTENT_RULES = [
|
|
129
|
+
{
|
|
130
|
+
// 实现/开发任务:按计划实现、开始写代码、实现模块等 —— 多步复杂任务
|
|
131
|
+
// 这类任务 detectIntent 只给工具提示,真正的分步拆解由 buildMultiStepPlan 完成
|
|
132
|
+
pattern: /(按计划|依据计划|根据计划).*(实现|开发|完成|写代码|编码|落地)/i,
|
|
133
|
+
plan: ['Read(读计划)', 'Glob/Grep(看现状)', 'Write/Edit(实现)', 'Bash(验证)'],
|
|
134
|
+
note: '这是按计划实现代码的多步任务,需要先读计划再逐步实现。',
|
|
135
|
+
toolHint: 'Bash, Read, Edit, Write, Glob, Grep',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
// 实现/开发任务(不带"按计划"):实现模块/功能/代码
|
|
139
|
+
pattern: /(实现|开发|编写|写出|完成).*(模块|功能|代码|接口|函数|类|系统|第一阶段|阶段)/i,
|
|
140
|
+
plan: ['Read(读相关文件)', 'Glob/Grep(看现状)', 'Write/Edit(实现)', 'Bash(验证)'],
|
|
141
|
+
note: '这是实现代码的多步任务,先了解现状再实现并验证。',
|
|
142
|
+
toolHint: 'Bash, Read, Edit, Write, Glob, Grep',
|
|
143
|
+
},
|
|
121
144
|
{
|
|
122
145
|
// 读/查看/阅读文档或文件 → 读取任务(优先于写规则,避免"阅读XX文档"被误判为写)
|
|
123
146
|
pattern: /(读|阅读|查看|打开|展示|显示|看看|浏览).*(文档|文件|内容|md|readme|代码|计划)/i,
|
|
@@ -210,6 +233,96 @@ export function buildIntentGuidance(userInput, opts = {}) {
|
|
|
210
233
|
return `[任务引导] 根据用户指令,建议按此思路用工具推进:${intent.intent}。\n说明:${intent.note}\n可用工具:${intent.toolHint}`
|
|
211
234
|
}
|
|
212
235
|
|
|
236
|
+
// 常见计划文档文件名(供多步拆解时定位计划文件)
|
|
237
|
+
const PLAN_FILE_CANDIDATES = [
|
|
238
|
+
'DEVELOPMENT_PLAN.md',
|
|
239
|
+
'development_plan.md',
|
|
240
|
+
'DEV_PLAN.md',
|
|
241
|
+
'PLAN.md',
|
|
242
|
+
'plan.md',
|
|
243
|
+
'PROJECT_PLAN.md',
|
|
244
|
+
'ROADMAP.md',
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 框架深度拆解:为"按计划实现"类多步任务生成分步执行计划(层 B 深化)
|
|
249
|
+
*
|
|
250
|
+
* 背景:小模型(27B Q3)撑不住"按计划实现第一阶段"这类多步复杂任务——
|
|
251
|
+
* 它常常不读计划就瞎写,或干脆敷衍。框架必须【接管规划】,把任务拆成
|
|
252
|
+
* 明确、可执行的步骤注入给模型。
|
|
253
|
+
*
|
|
254
|
+
* 针对"按计划实现/开发"任务,生成强制分步引导,核心是:
|
|
255
|
+
* 第 1 步必须 Read 计划文件(定位要实现的模块/函数);
|
|
256
|
+
* 后续步骤按"看现状 → 实现 → 验证"推进。
|
|
257
|
+
*
|
|
258
|
+
* @param {string} userInput — 用户指令
|
|
259
|
+
* @param {object} [opts]
|
|
260
|
+
* @param {string} [opts.cwd] — 当前工作目录(用于给计划文件相对路径)
|
|
261
|
+
* @param {boolean} [opts.enable=true] — 是否启用
|
|
262
|
+
* @returns {{ isMultiStep: boolean, plan: string } | null}
|
|
263
|
+
* isMultiStep=true 表示识别为多步实现任务;plan 为注入给模型的分步引导
|
|
264
|
+
*/
|
|
265
|
+
export function buildMultiStepPlan(userInput, opts = {}) {
|
|
266
|
+
if (opts.enable === false) return null
|
|
267
|
+
if (!userInput) return null
|
|
268
|
+
const s = userInput
|
|
269
|
+
|
|
270
|
+
// 识别"按计划实现/开发"类任务
|
|
271
|
+
const isPlanImplement = /(按计划|依据计划|根据计划|按开发计划|按照计划).*(实现|开发|完成|写|落地|推进)/i.test(s)
|
|
272
|
+
|| /实现.*(计划|第一阶段|阶段|模块)/i.test(s)
|
|
273
|
+
|
|
274
|
+
if (!isPlanImplement) return null
|
|
275
|
+
|
|
276
|
+
// 定位计划文件:从指令里提取文件名,否则用常见候选
|
|
277
|
+
let planFile = ''
|
|
278
|
+
const m = s.match(/([\w./-]+\.md)/i)
|
|
279
|
+
if (m) {
|
|
280
|
+
planFile = m[1]
|
|
281
|
+
} else {
|
|
282
|
+
planFile = PLAN_FILE_CANDIDATES[0] // 默认 DEVELOPMENT_PLAN.md
|
|
283
|
+
}
|
|
284
|
+
// 若非绝对路径且给了 cwd,拼成相对提示
|
|
285
|
+
const planRef = planFile
|
|
286
|
+
|
|
287
|
+
const plan = `[多步执行计划] 这是一个按计划实现代码的任务,请【严格按以下步骤】用工具推进,不要跳过,不要一次做太多:
|
|
288
|
+
|
|
289
|
+
步骤 1(必做):用 Read 读取计划文件 \`${planRef}\`,从中找出用户要求实现的阶段/模块/函数(明确要做什么、涉及哪些文件、有哪些要求)。
|
|
290
|
+
步骤 2:用 Glob 查看项目当前文件结构(cwd 下),用 Grep 搜索计划里提到的模块/函数是否已有代码。
|
|
291
|
+
步骤 3:对每个需要实现的目标,先 Read 相关现有文件了解现状,再用 Write 创建新文件 / 用 Edit 修改已有文件来落地实现。
|
|
292
|
+
步骤 4:实现完成后,用 Bash 运行测试或编译命令验证(如有),并总结实现了什么。
|
|
293
|
+
|
|
294
|
+
严格遵守:第 1 步必须先 Read 计划文件;每一步完成后再进行下一步;不要在没有读计划的情况下直接写代码。`
|
|
295
|
+
|
|
296
|
+
return { isMultiStep: true, plan }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* 生成注入到 system 的完整小模型引导(意图引导 + 多步计划 + cwd)
|
|
301
|
+
*
|
|
302
|
+
* 把多个引导源合并成一段,追加到首条 system 消息末尾。
|
|
303
|
+
*
|
|
304
|
+
* @param {string} userInput — 用户指令
|
|
305
|
+
* @param {object} [opts]
|
|
306
|
+
* @param {string} [opts.cwd] — 当前工作目录
|
|
307
|
+
* @param {boolean} [opts.enable=true]
|
|
308
|
+
* @returns {string|null} 合并后的引导文本
|
|
309
|
+
*/
|
|
310
|
+
export function buildCombinedGuidance(userInput, opts = {}) {
|
|
311
|
+
if (opts.enable === false) return null
|
|
312
|
+
const parts = []
|
|
313
|
+
// 多步计划优先(复杂实现任务走深度拆解)
|
|
314
|
+
const multi = buildMultiStepPlan(userInput, opts)
|
|
315
|
+
if (multi) {
|
|
316
|
+
parts.push(multi.plan)
|
|
317
|
+
} else {
|
|
318
|
+
// 否则用简单意图引导
|
|
319
|
+
const g = buildIntentGuidance(userInput, opts)
|
|
320
|
+
if (g) parts.push(g)
|
|
321
|
+
}
|
|
322
|
+
if (parts.length === 0) return null
|
|
323
|
+
return parts.join('\n\n')
|
|
324
|
+
}
|
|
325
|
+
|
|
213
326
|
/**
|
|
214
327
|
* 精简工具列表(层 A)— 小模型一次看太多工具会混乱
|
|
215
328
|
*
|