@emqo/claudebridge 0.3.0 → 0.3.1
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/adapters/base.d.ts +1 -1
- package/dist/adapters/base.js +32 -1
- package/dist/core/markdown.d.ts +2 -1
- package/dist/core/markdown.js +73 -38
- package/dist/skills/bridge.js +24 -0
- package/package.json +1 -1
package/dist/adapters/base.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export interface Adapter {
|
|
|
8
8
|
start(): Promise<void>;
|
|
9
9
|
stop(): void;
|
|
10
10
|
}
|
|
11
|
-
/** Split long text into chunks respecting newlines */
|
|
11
|
+
/** Split long text into chunks respecting newlines, with code-block-aware balancing */
|
|
12
12
|
export declare function chunkText(text: string, maxLen: number): string[];
|
package/dist/adapters/base.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/** Split long text into chunks respecting newlines */
|
|
1
|
+
/** Split long text into chunks respecting newlines, with code-block-aware balancing */
|
|
2
2
|
export function chunkText(text, maxLen) {
|
|
3
3
|
if (text.length <= maxLen)
|
|
4
4
|
return [text];
|
|
5
|
+
// Phase 1: split by newlines (existing logic)
|
|
5
6
|
const chunks = [];
|
|
6
7
|
let remaining = text;
|
|
7
8
|
while (remaining.length > 0) {
|
|
@@ -15,5 +16,35 @@ export function chunkText(text, maxLen) {
|
|
|
15
16
|
chunks.push(remaining.slice(0, cut));
|
|
16
17
|
remaining = remaining.slice(cut).replace(/^\n/, "");
|
|
17
18
|
}
|
|
19
|
+
// Phase 2: balance code fences across chunks
|
|
20
|
+
// Ensures each chunk is self-contained valid MarkdownV2
|
|
21
|
+
let inCode = false;
|
|
22
|
+
let lang = "";
|
|
23
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
24
|
+
const chunk = chunks[i];
|
|
25
|
+
const startsInCode = inCode;
|
|
26
|
+
// Scan fences in original chunk to determine end state and track language
|
|
27
|
+
const fenceRegex = /```(\w*)/g;
|
|
28
|
+
let toggleState = startsInCode;
|
|
29
|
+
let m;
|
|
30
|
+
while ((m = fenceRegex.exec(chunk)) !== null) {
|
|
31
|
+
// Opening fence (not in code) → track language
|
|
32
|
+
if (!toggleState && m[1]) {
|
|
33
|
+
lang = m[1];
|
|
34
|
+
}
|
|
35
|
+
toggleState = !toggleState;
|
|
36
|
+
}
|
|
37
|
+
inCode = toggleState;
|
|
38
|
+
// Apply fixes: reopen/close code blocks as needed
|
|
39
|
+
let fixed = chunk;
|
|
40
|
+
if (startsInCode) {
|
|
41
|
+
fixed = "```" + lang + "\n" + fixed;
|
|
42
|
+
}
|
|
43
|
+
if (inCode) {
|
|
44
|
+
fixed = fixed + "\n```";
|
|
45
|
+
// inCode stays true — next chunk's content is still logically inside code
|
|
46
|
+
}
|
|
47
|
+
chunks[i] = fixed;
|
|
48
|
+
}
|
|
18
49
|
return chunks;
|
|
19
50
|
}
|
package/dist/core/markdown.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export declare function escapeMarkdownV2(text: string): string;
|
|
3
3
|
/**
|
|
4
4
|
* Convert standard markdown to Telegram MarkdownV2.
|
|
5
|
-
*
|
|
5
|
+
* Uses iterative scanning for code blocks instead of regex split,
|
|
6
|
+
* properly handling unclosed blocks and multiple consecutive blocks.
|
|
6
7
|
*/
|
|
7
8
|
export declare function toTelegramMarkdown(text: string): string;
|
package/dist/core/markdown.js
CHANGED
|
@@ -3,49 +3,84 @@ export function escapeMarkdownV2(text) {
|
|
|
3
3
|
// Characters that must be escaped outside code blocks
|
|
4
4
|
return text.replace(/([_*\[\]()~`>#+\-=|{}.!\\])/g, "\\$1");
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Process inline markdown: inline code, links, bold, italic, strikethrough.
|
|
8
|
+
* Order: extract protected elements → escape → restore formatted elements → restore placeholders.
|
|
9
|
+
*/
|
|
10
|
+
function processInline(text) {
|
|
11
|
+
let s = text;
|
|
12
|
+
// 1. Extract inline code → placeholders (before escaping)
|
|
13
|
+
const inlineCodes = [];
|
|
14
|
+
s = s.replace(/`([^`]+)`/g, (_, code) => {
|
|
15
|
+
const escaped = code.replace(/([\\`])/g, "\\$1");
|
|
16
|
+
inlineCodes.push(`\`${escaped}\``);
|
|
17
|
+
return `\x00IC${inlineCodes.length - 1}\x00`;
|
|
18
|
+
});
|
|
19
|
+
// 2. Extract markdown links [text](url) → placeholders (before escaping)
|
|
20
|
+
// Handles one level of nested parens in URLs, e.g. wiki/Rust_(programming_language)
|
|
21
|
+
const links = [];
|
|
22
|
+
s = s.replace(/\[([^\]]+)\]\(([^()]*(?:\([^()]*\))*[^()]*)\)/g, (_, linkText, url) => {
|
|
23
|
+
const escapedText = escapeMarkdownV2(linkText);
|
|
24
|
+
// Inside MarkdownV2 link URL, only \ and ) need escaping
|
|
25
|
+
const escapedUrl = url.replace(/\\/g, "\\\\").replace(/\)/g, "\\)");
|
|
26
|
+
links.push(`[${escapedText}](${escapedUrl})`);
|
|
27
|
+
return `\x00LK${links.length - 1}\x00`;
|
|
28
|
+
});
|
|
29
|
+
// 3. Escape all remaining special chars
|
|
30
|
+
s = escapeMarkdownV2(s);
|
|
31
|
+
// 4. Restore bold **text** → *text* (MarkdownV2 single asterisk)
|
|
32
|
+
s = s.replace(/\\\*\\\*([\s\S]+?)\\\*\\\*/g, "*$1*");
|
|
33
|
+
// 5. Restore italic _text_ → _text_
|
|
34
|
+
s = s.replace(/\\_(.+?)\\_/g, "_$1_");
|
|
35
|
+
// 6. Restore strikethrough ~~text~~ → ~text~
|
|
36
|
+
s = s.replace(/\\~\\~(.+?)\\~\\~/g, "~$1~");
|
|
37
|
+
// 7. Restore link placeholders
|
|
38
|
+
s = s.replace(/\x00LK(\d+)\x00/g, (_, i) => links[Number(i)]);
|
|
39
|
+
// 8. Restore inline code placeholders
|
|
40
|
+
s = s.replace(/\x00IC(\d+)\x00/g, (_, i) => inlineCodes[Number(i)]);
|
|
41
|
+
return s;
|
|
42
|
+
}
|
|
6
43
|
/**
|
|
7
44
|
* Convert standard markdown to Telegram MarkdownV2.
|
|
8
|
-
*
|
|
45
|
+
* Uses iterative scanning for code blocks instead of regex split,
|
|
46
|
+
* properly handling unclosed blocks and multiple consecutive blocks.
|
|
9
47
|
*/
|
|
10
48
|
export function toTelegramMarkdown(text) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
parts.push(seg);
|
|
25
|
-
}
|
|
49
|
+
const result = [];
|
|
50
|
+
let pos = 0;
|
|
51
|
+
while (pos < text.length) {
|
|
52
|
+
// Find next code fence ```
|
|
53
|
+
const fenceStart = text.indexOf("```", pos);
|
|
54
|
+
if (fenceStart === -1) {
|
|
55
|
+
// No more code blocks — process rest as inline
|
|
56
|
+
result.push(processInline(text.slice(pos)));
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
// Process text before code block as inline
|
|
60
|
+
if (fenceStart > pos) {
|
|
61
|
+
result.push(processInline(text.slice(pos, fenceStart)));
|
|
26
62
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// Restore bold **text** → *text*
|
|
40
|
-
s = s.replace(/\\\*\\\*(.+?)\\\*\\\*/g, "*$1*");
|
|
41
|
-
// Restore italic _text_ (single underscore)
|
|
42
|
-
s = s.replace(/\\_(.+?)\\_/g, "_$1_");
|
|
43
|
-
// Restore links [text](url)
|
|
44
|
-
s = s.replace(/\\\[(.+?)\\\]\\\((.+?)\\\)/g, "[$1]($2)");
|
|
45
|
-
// Restore inline codes
|
|
46
|
-
s = s.replace(/\x00IC(\d+)\x00/g, (_, i) => inlineCodes[Number(i)]);
|
|
47
|
-
parts.push(s);
|
|
63
|
+
// Find closing ```
|
|
64
|
+
const afterOpen = fenceStart + 3;
|
|
65
|
+
const fenceEnd = text.indexOf("```", afterOpen);
|
|
66
|
+
if (fenceEnd === -1) {
|
|
67
|
+
// Unclosed code block — auto-close it
|
|
68
|
+
const raw = text.slice(afterOpen);
|
|
69
|
+
const langMatch = raw.match(/^(\w*)\n?/);
|
|
70
|
+
const lang = langMatch ? langMatch[1] : "";
|
|
71
|
+
const contentStart = langMatch ? langMatch[0].length : 0;
|
|
72
|
+
const code = raw.slice(contentStart).replace(/([\\`])/g, "\\$1");
|
|
73
|
+
result.push(`\`\`\`${lang}\n${code}\n\`\`\``);
|
|
74
|
+
break;
|
|
48
75
|
}
|
|
76
|
+
// Complete code block — extract lang and content, escape only \ and `
|
|
77
|
+
const raw = text.slice(afterOpen, fenceEnd);
|
|
78
|
+
const langMatch = raw.match(/^(\w*)\n?/);
|
|
79
|
+
const lang = langMatch ? langMatch[1] : "";
|
|
80
|
+
const contentStart = langMatch ? langMatch[0].length : 0;
|
|
81
|
+
const code = raw.slice(contentStart).replace(/([\\`])/g, "\\$1");
|
|
82
|
+
result.push(`\`\`\`${lang}\n${code}\`\`\``);
|
|
83
|
+
pos = fenceEnd + 3;
|
|
49
84
|
}
|
|
50
|
-
return
|
|
85
|
+
return result.join("");
|
|
51
86
|
}
|
package/dist/skills/bridge.js
CHANGED
|
@@ -40,6 +40,18 @@ export function generateSkillDoc(ctx) {
|
|
|
40
40
|
`- 用户要添加任务/待办 → 使用 task add`,
|
|
41
41
|
`- 命令输出 JSON,请用自然语言向用户回复结果,不要直接展示 JSON`,
|
|
42
42
|
`- 提醒会由 Bridge 定时器自动推送,你只需创建即可`,
|
|
43
|
+
``,
|
|
44
|
+
`### 高级用法:任务自动分解`,
|
|
45
|
+
`- 遇到复杂/大型任务时,必须拆分为多个 auto-task 分步执行`,
|
|
46
|
+
`- 每个 auto-task 应该是独立可完成的小任务(预算内可完成)`,
|
|
47
|
+
`- auto-task 按创建顺序排队,每60秒执行一个`,
|
|
48
|
+
`- 在 auto-task 执行中可以创建新的 auto-task(链式执行)`,
|
|
49
|
+
`- 每个 auto-task 的描述要足够详细,因为它会在全新会话中执行`,
|
|
50
|
+
`- 示例:用户说"优化整个项目" → 创建多个 auto-task:`,
|
|
51
|
+
` 1. "分析项目结构,列出所有需要优化的模块和具体改进点"`,
|
|
52
|
+
` 2. "优化模块A:[具体描述,包含文件路径和修改内容]"`,
|
|
53
|
+
` 3. "优化模块B:[具体描述]"`,
|
|
54
|
+
` 4. "运行测试验证所有修改,提交代码,生成优化报告"`,
|
|
43
55
|
].join("\n");
|
|
44
56
|
}
|
|
45
57
|
return [
|
|
@@ -76,5 +88,17 @@ export function generateSkillDoc(ctx) {
|
|
|
76
88
|
`- User wants to add a task/todo → use task add`,
|
|
77
89
|
`- Commands output JSON. Respond to the user in natural language, do not dump raw JSON.`,
|
|
78
90
|
`- Reminders are automatically pushed by Bridge timers — you only need to create them.`,
|
|
91
|
+
``,
|
|
92
|
+
`### Advanced: Auto-Task Decomposition`,
|
|
93
|
+
`- For complex/large tasks, decompose into multiple auto-tasks`,
|
|
94
|
+
`- Each auto-task should be independently completable within budget`,
|
|
95
|
+
`- Auto-tasks execute in FIFO order, one every 60 seconds`,
|
|
96
|
+
`- An auto-task can create new auto-tasks (chaining)`,
|
|
97
|
+
`- Each description must be detailed enough for a fresh session`,
|
|
98
|
+
`- Example: user says "optimize the project" → create:`,
|
|
99
|
+
` 1. "Analyze project structure, list modules needing optimization"`,
|
|
100
|
+
` 2. "Optimize module A: [specific file paths and changes]"`,
|
|
101
|
+
` 3. "Optimize module B: [specific description]"`,
|
|
102
|
+
` 4. "Run tests, commit changes, generate optimization report"`,
|
|
79
103
|
].join("\n");
|
|
80
104
|
}
|