deepccc 0.1.7 → 0.1.8
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/context.d.ts +5 -0
- package/dist/context.js +13 -2
- package/dist/file-tools.d.ts +6 -0
- package/dist/file-tools.js +17 -2
- package/dist/index.d.ts +6 -1
- package/dist/index.js +12 -5
- package/dist/skills.d.ts +7 -0
- package/dist/skills.js +17 -1
- package/package.json +1 -1
package/dist/context.d.ts
CHANGED
|
@@ -46,6 +46,11 @@ export declare function newBuiltinSessionId(now?: Date, suffix?: string): string
|
|
|
46
46
|
export declare function getBuiltinContextSession(sessionId: string, contextDir?: string): BuiltinContextSessionInfo | null;
|
|
47
47
|
export declare function listBuiltinContextSessions(contextDir?: string): BuiltinContextSessionInfo[];
|
|
48
48
|
export declare function latestBuiltinSessionForCwd(cwd: string, contextDir?: string): BuiltinContextSessionInfo | null;
|
|
49
|
+
/**
|
|
50
|
+
* 估算上下文的 token 数(用于决定何时压缩)。
|
|
51
|
+
* 按字符类型加权,比旧版 chars/3 更接近真实:CJK 字符 ≈ 1 token/字,
|
|
52
|
+
* 其他字符 ≈ 3.5 chars/token。避免中文长上下文被严重低估导致压缩过晚。
|
|
53
|
+
*/
|
|
49
54
|
export declare function estimateBuiltinContextTokens(summary: string, messages: readonly BuiltinContextMessage[]): number;
|
|
50
55
|
export declare function serializeMessagesForSummary(messages: readonly BuiltinContextMessage[]): string;
|
|
51
56
|
export declare function buildSummaryPrompt(plan: BuiltinCompactionPlan): string;
|
package/dist/context.js
CHANGED
|
@@ -121,9 +121,20 @@ export function latestBuiltinSessionForCwd(cwd, contextDir = DEFAULT_BUILTIN_CON
|
|
|
121
121
|
const legacySessionId = defaultBuiltinSessionId(cwd);
|
|
122
122
|
return listBuiltinContextSessions(contextDir).find((session) => session.cwd === cwd || session.sessionId === legacySessionId) ?? null;
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* 估算上下文的 token 数(用于决定何时压缩)。
|
|
126
|
+
* 按字符类型加权,比旧版 chars/3 更接近真实:CJK 字符 ≈ 1 token/字,
|
|
127
|
+
* 其他字符 ≈ 3.5 chars/token。避免中文长上下文被严重低估导致压缩过晚。
|
|
128
|
+
*/
|
|
124
129
|
export function estimateBuiltinContextTokens(summary, messages) {
|
|
125
|
-
const
|
|
126
|
-
|
|
130
|
+
const text = summary + messages.reduce((sum, m) => sum + `${m.role}\n${m.content}\n`, "");
|
|
131
|
+
let cjk = 0;
|
|
132
|
+
for (const ch of text) {
|
|
133
|
+
if (/[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\u3000-\u303F\uFF00-\uFFEF]/.test(ch))
|
|
134
|
+
cjk++;
|
|
135
|
+
}
|
|
136
|
+
const other = text.length - cjk;
|
|
137
|
+
return Math.ceil(cjk + other / 3.5);
|
|
127
138
|
}
|
|
128
139
|
export function serializeMessagesForSummary(messages) {
|
|
129
140
|
return messages
|
package/dist/file-tools.d.ts
CHANGED
|
@@ -132,6 +132,12 @@ export interface ApplyPatchFileChange {
|
|
|
132
132
|
export interface ApplyPatchOutput {
|
|
133
133
|
changedFiles: ApplyPatchFileChange[];
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* 把 `~` / `~/x`(含反斜杠 `~\\x`)展开为用户主目录绝对路径。
|
|
137
|
+
* `~user/x` 形式不展开(Node 无内置支持),保持原样交给后续解析。
|
|
138
|
+
* 供路径工具共用:技能索引里 `~/...` 路径可直接传给 read_file 等工具。
|
|
139
|
+
*/
|
|
140
|
+
export declare function expandHomePath(value: string): string;
|
|
135
141
|
export declare function readFileForTool(cwd: string, input: ReadFileInput): Promise<ReadFileOutput>;
|
|
136
142
|
export declare function listDirForTool(cwd: string, input?: ListDirInput): Promise<ListDirOutput>;
|
|
137
143
|
export declare function searchCodeForTool(cwd: string, input: SearchCodeInput, signal?: AbortSignal, runtimeOptions?: SearchCodeRuntimeOptions): Promise<SearchCodeOutput>;
|
package/dist/file-tools.js
CHANGED
|
@@ -3,7 +3,8 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
3
3
|
import { createReadStream } from "node:fs";
|
|
4
4
|
import { copyFile, mkdir, open, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
|
-
import {
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
8
|
import { createInterface } from "node:readline";
|
|
8
9
|
import { jsonSchema, tool } from "ai";
|
|
9
10
|
import { isDangerousCommand } from "./permissions.js";
|
|
@@ -22,11 +23,25 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
|
|
|
22
23
|
const MAX_COMMAND_TIMEOUT_MS = 900_000;
|
|
23
24
|
const requireFromHere = createRequire(import.meta.url);
|
|
24
25
|
const FALLBACK_SKIPPED_DIRECTORIES = new Set([".git", "node_modules"]);
|
|
26
|
+
/**
|
|
27
|
+
* 把 `~` / `~/x`(含反斜杠 `~\\x`)展开为用户主目录绝对路径。
|
|
28
|
+
* `~user/x` 形式不展开(Node 无内置支持),保持原样交给后续解析。
|
|
29
|
+
* 供路径工具共用:技能索引里 `~/...` 路径可直接传给 read_file 等工具。
|
|
30
|
+
*/
|
|
31
|
+
export function expandHomePath(value) {
|
|
32
|
+
if (value === "~")
|
|
33
|
+
return homedir();
|
|
34
|
+
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
35
|
+
return join(homedir(), value.slice(2));
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
25
39
|
function resolveToolPath(cwd, value) {
|
|
26
40
|
const raw = value?.trim();
|
|
27
41
|
if (!raw)
|
|
28
42
|
return resolve(cwd);
|
|
29
|
-
|
|
43
|
+
const expanded = expandHomePath(raw);
|
|
44
|
+
return isAbsolute(expanded) ? resolve(expanded) : resolve(cwd, expanded);
|
|
30
45
|
}
|
|
31
46
|
function toPositiveInt(value) {
|
|
32
47
|
if (value === undefined)
|
package/dist/index.d.ts
CHANGED
|
@@ -98,7 +98,12 @@ export declare class ChatSession {
|
|
|
98
98
|
/** 最近一次 chat() 使用的 system prompt(供 history 等读取) */
|
|
99
99
|
private systemPrompt;
|
|
100
100
|
constructor(overrides?: ChatSessionConfig, options?: ChatSessionOptions);
|
|
101
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* 组装系统提示词。顺序遵循“稳定性优先”原则(缓存命中友好):
|
|
103
|
+
* 固定规则 → 项目指令 → runtime 上下文 → 用户补充 → 技能索引(最后)。
|
|
104
|
+
* 技能索引是最易变的部分(热加载,任何 SKILL.md 变化都会改前缀),
|
|
105
|
+
* 放最后可以让前面的稳定内容尽量命中缓存,只丢尾段。
|
|
106
|
+
*/
|
|
102
107
|
private buildSystemPrompt;
|
|
103
108
|
chat(userMessage: string, signal?: AbortSignal): AsyncIterable<ChatEvent>;
|
|
104
109
|
/** 返回当前的会话历史(只读) */
|
package/dist/index.js
CHANGED
|
@@ -123,21 +123,26 @@ export class ChatSession {
|
|
|
123
123
|
});
|
|
124
124
|
this.permissionGate = new PermissionGate(options.permissionMode ?? "ask", options.permissionResolver);
|
|
125
125
|
}
|
|
126
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* 组装系统提示词。顺序遵循“稳定性优先”原则(缓存命中友好):
|
|
128
|
+
* 固定规则 → 项目指令 → runtime 上下文 → 用户补充 → 技能索引(最后)。
|
|
129
|
+
* 技能索引是最易变的部分(热加载,任何 SKILL.md 变化都会改前缀),
|
|
130
|
+
* 放最后可以让前面的稳定内容尽量命中缓存,只丢尾段。
|
|
131
|
+
*/
|
|
127
132
|
buildSystemPrompt(skills) {
|
|
128
133
|
const systemContent = [SYSTEM_PROMPT];
|
|
129
134
|
const projectInstructions = readProjectInstructionFiles(this.cwd);
|
|
130
135
|
if (projectInstructions) {
|
|
131
136
|
systemContent.push("", projectInstructions);
|
|
132
137
|
}
|
|
138
|
+
systemContent.push("", buildRuntimeWorkspacePrompt(this.cwd));
|
|
139
|
+
if (this.customSystemPrompt) {
|
|
140
|
+
systemContent.push("", this.customSystemPrompt);
|
|
141
|
+
}
|
|
133
142
|
const skillsPrompt = buildSkillsIndexPrompt(skills);
|
|
134
143
|
if (skillsPrompt) {
|
|
135
144
|
systemContent.push("", skillsPrompt);
|
|
136
145
|
}
|
|
137
|
-
if (this.customSystemPrompt) {
|
|
138
|
-
systemContent.push("", this.customSystemPrompt);
|
|
139
|
-
}
|
|
140
|
-
systemContent.push("", buildRuntimeWorkspacePrompt(this.cwd));
|
|
141
146
|
return systemContent.join("\n");
|
|
142
147
|
}
|
|
143
148
|
async *chat(userMessage, signal) {
|
|
@@ -291,6 +296,8 @@ export class ChatSession {
|
|
|
291
296
|
system: SUMMARY_SYSTEM_PROMPT,
|
|
292
297
|
messages: [{ role: "user", content: buildSummaryPrompt(plan) }],
|
|
293
298
|
abortSignal: signal,
|
|
299
|
+
// 温度 0:相同输入尽量产出相同摘要,避免压缩后上下文前缀随机漂移破坏缓存
|
|
300
|
+
temperature: 0,
|
|
294
301
|
});
|
|
295
302
|
if (!result.text.trim())
|
|
296
303
|
return 0;
|
package/dist/skills.d.ts
CHANGED
|
@@ -46,11 +46,18 @@ export declare function scanSkillsDirs(dirs: SkillDirSpec[]): Promise<BuiltinSki
|
|
|
46
46
|
* 其中 codex 全局包含 ~/.codex/skills(CLI 旧路径)与 ~/.agents/skills(标准全局目录)。
|
|
47
47
|
*/
|
|
48
48
|
export declare function buildDefaultSkillDirs(cwd: string): SkillDirSpec[];
|
|
49
|
+
/**
|
|
50
|
+
* 把技能绝对路径转成 prompt 展示形式:主目录下的路径缩写为 `~/...`(跨机器、
|
|
51
|
+
* 跨用户稳定,避免用户名/盘符差异导致 system 前缀变化破坏缓存命中);
|
|
52
|
+
* 分隔符统一为 `/`。主目录外的路径保持绝对路径(仅统一分隔符)。
|
|
53
|
+
*/
|
|
54
|
+
export declare function normalizeSkillPathForPrompt(skillPath: string): string;
|
|
49
55
|
/**
|
|
50
56
|
* 生成 skill 索引提示词(注入 system prompt)。
|
|
51
57
|
* 索引只含 name + description + 来源 + 路径,并指示模型在任务匹配时
|
|
52
58
|
* 先用 read_file 读取 SKILL.md 全文再执行;同时声明"创建技能"约定
|
|
53
59
|
* (模型在对话中应把新技能创建到 ~/.deepccc/skills 或项目 .deepccc/skills)。
|
|
60
|
+
* 路径经 normalizeSkillPathForPrompt 缩写,保持跨机器前缀稳定。
|
|
54
61
|
*/
|
|
55
62
|
export declare function buildSkillsIndexPrompt(skills: BuiltinSkill[]): string;
|
|
56
63
|
/** 生成 Codex 结构的新技能 SKILL.md 模板(供 CLI skill create / 对话创建约定共用) */
|
package/dist/skills.js
CHANGED
|
@@ -113,11 +113,27 @@ export function buildDefaultSkillDirs(cwd) {
|
|
|
113
113
|
{ dir: join(cwd, ".deepccc", "skills"), source: "deepccc", scope: "project" },
|
|
114
114
|
];
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* 把技能绝对路径转成 prompt 展示形式:主目录下的路径缩写为 `~/...`(跨机器、
|
|
118
|
+
* 跨用户稳定,避免用户名/盘符差异导致 system 前缀变化破坏缓存命中);
|
|
119
|
+
* 分隔符统一为 `/`。主目录外的路径保持绝对路径(仅统一分隔符)。
|
|
120
|
+
*/
|
|
121
|
+
export function normalizeSkillPathForPrompt(skillPath) {
|
|
122
|
+
const home = homedir().replace(/[\\/]+$/, "").replace(/\\/g, "/");
|
|
123
|
+
const normalized = skillPath.replace(/\\/g, "/");
|
|
124
|
+
if (normalized === home)
|
|
125
|
+
return "~";
|
|
126
|
+
if (normalized.startsWith(home + "/")) {
|
|
127
|
+
return "~" + normalized.slice(home.length);
|
|
128
|
+
}
|
|
129
|
+
return normalized;
|
|
130
|
+
}
|
|
116
131
|
/**
|
|
117
132
|
* 生成 skill 索引提示词(注入 system prompt)。
|
|
118
133
|
* 索引只含 name + description + 来源 + 路径,并指示模型在任务匹配时
|
|
119
134
|
* 先用 read_file 读取 SKILL.md 全文再执行;同时声明"创建技能"约定
|
|
120
135
|
* (模型在对话中应把新技能创建到 ~/.deepccc/skills 或项目 .deepccc/skills)。
|
|
136
|
+
* 路径经 normalizeSkillPathForPrompt 缩写,保持跨机器前缀稳定。
|
|
121
137
|
*/
|
|
122
138
|
export function buildSkillsIndexPrompt(skills) {
|
|
123
139
|
if (skills.length === 0)
|
|
@@ -128,7 +144,7 @@ export function buildSkillsIndexPrompt(skills) {
|
|
|
128
144
|
"On name conflicts: DeepCCC > Codex > Cursor > Claude wins; within one source, project scope wins over global.",
|
|
129
145
|
"When a user request matches a skill's description, first read its full SKILL.md with read_file, then follow the instructions in it exactly.",
|
|
130
146
|
"",
|
|
131
|
-
...skills.map((s) => `- **${s.name}** [${s.source}:${s.scope}] (\`${s.skillPath}\`): ${s.description || "(no description)"}`),
|
|
147
|
+
...skills.map((s) => `- **${s.name}** [${s.source}:${s.scope}] (\`${normalizeSkillPathForPrompt(s.skillPath)}\`): ${s.description || "(no description)"}`),
|
|
132
148
|
"",
|
|
133
149
|
"## Creating Skills",
|
|
134
150
|
"When the user asks to create a skill, create it as a Codex-style directory skill at",
|