@winspan/claude-forge 0.1.2 → 0.1.4

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.
Files changed (156) hide show
  1. package/dist/autopilot/index.d.ts +0 -1
  2. package/dist/autopilot/index.d.ts.map +1 -1
  3. package/dist/autopilot/index.js +0 -1
  4. package/dist/autopilot/index.js.map +1 -1
  5. package/dist/autopilot/intent-engine.d.ts +4 -1
  6. package/dist/autopilot/intent-engine.d.ts.map +1 -1
  7. package/dist/autopilot/intent-engine.js +22 -4
  8. package/dist/autopilot/intent-engine.js.map +1 -1
  9. package/dist/autopilot/quality-gate.d.ts +7 -0
  10. package/dist/autopilot/quality-gate.d.ts.map +1 -1
  11. package/dist/autopilot/quality-gate.js +8 -0
  12. package/dist/autopilot/quality-gate.js.map +1 -1
  13. package/dist/claudemd/index.d.ts +1 -1
  14. package/dist/claudemd/index.d.ts.map +1 -1
  15. package/dist/claudemd/index.js +30 -4
  16. package/dist/claudemd/index.js.map +1 -1
  17. package/dist/claudemd/prompts.d.ts +4 -1
  18. package/dist/claudemd/prompts.d.ts.map +1 -1
  19. package/dist/claudemd/prompts.js +41 -5
  20. package/dist/claudemd/prompts.js.map +1 -1
  21. package/dist/cli/commands/init.d.ts +33 -0
  22. package/dist/cli/commands/init.d.ts.map +1 -1
  23. package/dist/cli/commands/init.js +508 -3
  24. package/dist/cli/commands/init.js.map +1 -1
  25. package/dist/cli/index.js +3 -1
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/cli/tui.d.ts.map +1 -1
  28. package/dist/cli/tui.js +4 -0
  29. package/dist/cli/tui.js.map +1 -1
  30. package/dist/config/defaults.d.ts.map +1 -1
  31. package/dist/config/defaults.js +0 -2
  32. package/dist/config/defaults.js.map +1 -1
  33. package/dist/config/schema.d.ts +0 -2
  34. package/dist/config/schema.d.ts.map +1 -1
  35. package/dist/config/schema.js +0 -4
  36. package/dist/config/schema.js.map +1 -1
  37. package/dist/convention/official-sync.d.ts +21 -0
  38. package/dist/convention/official-sync.d.ts.map +1 -0
  39. package/dist/convention/official-sync.js +196 -0
  40. package/dist/convention/official-sync.js.map +1 -0
  41. package/dist/daemon/engine-registry/init-autopilot.d.ts +0 -2
  42. package/dist/daemon/engine-registry/init-autopilot.d.ts.map +1 -1
  43. package/dist/daemon/engine-registry/init-autopilot.js +2 -6
  44. package/dist/daemon/engine-registry/init-autopilot.js.map +1 -1
  45. package/dist/daemon/engine-registry.d.ts.map +1 -1
  46. package/dist/daemon/engine-registry.js +4 -2
  47. package/dist/daemon/engine-registry.js.map +1 -1
  48. package/dist/daemon/handler-context.d.ts +0 -2
  49. package/dist/daemon/handler-context.d.ts.map +1 -1
  50. package/dist/daemon/handlers/post-tool-use-handler.d.ts.map +1 -1
  51. package/dist/daemon/handlers/post-tool-use-handler.js +7 -0
  52. package/dist/daemon/handlers/post-tool-use-handler.js.map +1 -1
  53. package/dist/daemon/handlers/pre-tool-use-handler.d.ts.map +1 -1
  54. package/dist/daemon/handlers/pre-tool-use-handler.js +15 -15
  55. package/dist/daemon/handlers/pre-tool-use-handler.js.map +1 -1
  56. package/dist/daemon/handlers/stop-handler.d.ts.map +1 -1
  57. package/dist/daemon/handlers/stop-handler.js +23 -9
  58. package/dist/daemon/handlers/stop-handler.js.map +1 -1
  59. package/dist/daemon/handlers/user-prompt-handler.d.ts +9 -8
  60. package/dist/daemon/handlers/user-prompt-handler.d.ts.map +1 -1
  61. package/dist/daemon/handlers/user-prompt-handler.js +75 -48
  62. package/dist/daemon/handlers/user-prompt-handler.js.map +1 -1
  63. package/dist/daemon/index.d.ts.map +1 -1
  64. package/dist/daemon/index.js +25 -15
  65. package/dist/daemon/index.js.map +1 -1
  66. package/dist/daemon/lifecycle.d.ts +6 -0
  67. package/dist/daemon/lifecycle.d.ts.map +1 -1
  68. package/dist/daemon/lifecycle.js +28 -0
  69. package/dist/daemon/lifecycle.js.map +1 -1
  70. package/dist/daemon/server.d.ts +2 -1
  71. package/dist/daemon/server.d.ts.map +1 -1
  72. package/dist/daemon/server.js +20 -1
  73. package/dist/daemon/server.js.map +1 -1
  74. package/dist/distill/index.d.ts.map +1 -1
  75. package/dist/distill/index.js +4 -0
  76. package/dist/distill/index.js.map +1 -1
  77. package/dist/distill/writer.d.ts +22 -0
  78. package/dist/distill/writer.d.ts.map +1 -1
  79. package/dist/distill/writer.js +77 -0
  80. package/dist/distill/writer.js.map +1 -1
  81. package/dist/hooks/check-context-limit.sh +0 -0
  82. package/dist/hooks/notification.sh +3 -1
  83. package/dist/hooks/post-tool-use.sh +3 -1
  84. package/dist/hooks/pre-tool-use.sh +3 -1
  85. package/dist/hooks/stop.sh +3 -1
  86. package/dist/hooks/user-prompt-submit.sh +3 -1
  87. package/dist/pipeline/phase-manager.d.ts +4 -0
  88. package/dist/pipeline/phase-manager.d.ts.map +1 -1
  89. package/dist/pipeline/phase-manager.js +52 -10
  90. package/dist/pipeline/phase-manager.js.map +1 -1
  91. package/dist/skill-registry/evolver.d.ts.map +1 -1
  92. package/dist/skill-registry/evolver.js +4 -2
  93. package/dist/skill-registry/evolver.js.map +1 -1
  94. package/package.json +10 -4
  95. package/dist/cli/commands/console.d.ts +0 -3
  96. package/dist/cli/commands/console.d.ts.map +0 -1
  97. package/dist/cli/commands/console.js +0 -11
  98. package/dist/cli/commands/console.js.map +0 -1
  99. package/dist/cli/interceptor.d.ts +0 -5
  100. package/dist/cli/interceptor.d.ts.map +0 -1
  101. package/dist/cli/interceptor.js +0 -55
  102. package/dist/cli/interceptor.js.map +0 -1
  103. package/dist/hooks/hooks/check-context-limit.sh +0 -97
  104. package/dist/hooks/hooks/notification.sh +0 -33
  105. package/dist/hooks/hooks/post-tool-use.sh +0 -49
  106. package/dist/hooks/hooks/pre-tool-use.sh +0 -55
  107. package/dist/hooks/hooks/stop.sh +0 -29
  108. package/dist/hooks/hooks/user-prompt-submit.sh +0 -72
  109. package/dist/skill-registry/recommender.d.ts +0 -25
  110. package/dist/skill-registry/recommender.d.ts.map +0 -1
  111. package/dist/skill-registry/recommender.js +0 -91
  112. package/dist/skill-registry/recommender.js.map +0 -1
  113. package/dist/tests/claude-api.test.d.ts +0 -2
  114. package/dist/tests/claude-api.test.d.ts.map +0 -1
  115. package/dist/tests/claude-api.test.js +0 -96
  116. package/dist/tests/claude-api.test.js.map +0 -1
  117. package/dist/tests/conversational-config-handler.test.d.ts +0 -2
  118. package/dist/tests/conversational-config-handler.test.d.ts.map +0 -1
  119. package/dist/tests/conversational-config-handler.test.js +0 -107
  120. package/dist/tests/conversational-config-handler.test.js.map +0 -1
  121. package/dist/tests/doc-sync.test.d.ts +0 -2
  122. package/dist/tests/doc-sync.test.d.ts.map +0 -1
  123. package/dist/tests/doc-sync.test.js +0 -148
  124. package/dist/tests/doc-sync.test.js.map +0 -1
  125. package/dist/tests/orchestration-config.test.d.ts +0 -2
  126. package/dist/tests/orchestration-config.test.d.ts.map +0 -1
  127. package/dist/tests/orchestration-config.test.js +0 -122
  128. package/dist/tests/orchestration-config.test.js.map +0 -1
  129. package/dist/tests/pipeline-options.test.d.ts +0 -2
  130. package/dist/tests/pipeline-options.test.d.ts.map +0 -1
  131. package/dist/tests/pipeline-options.test.js +0 -68
  132. package/dist/tests/pipeline-options.test.js.map +0 -1
  133. package/dist/tests/resume-context-gen.test.d.ts +0 -2
  134. package/dist/tests/resume-context-gen.test.d.ts.map +0 -1
  135. package/dist/tests/resume-context-gen.test.js +0 -93
  136. package/dist/tests/resume-context-gen.test.js.map +0 -1
  137. package/dist/tests/sqlite.test.d.ts +0 -2
  138. package/dist/tests/sqlite.test.d.ts.map +0 -1
  139. package/dist/tests/sqlite.test.js +0 -154
  140. package/dist/tests/sqlite.test.js.map +0 -1
  141. package/dist/tests/tracker.test.d.ts +0 -2
  142. package/dist/tests/tracker.test.d.ts.map +0 -1
  143. package/dist/tests/tracker.test.js +0 -75
  144. package/dist/tests/tracker.test.js.map +0 -1
  145. package/dist/tests/tui.test.d.ts +0 -2
  146. package/dist/tests/tui.test.d.ts.map +0 -1
  147. package/dist/tests/tui.test.js +0 -39
  148. package/dist/tests/tui.test.js.map +0 -1
  149. package/dist/tests/user-prompt-handler.test.d.ts +0 -2
  150. package/dist/tests/user-prompt-handler.test.d.ts.map +0 -1
  151. package/dist/tests/user-prompt-handler.test.js +0 -182
  152. package/dist/tests/user-prompt-handler.test.js.map +0 -1
  153. package/dist/tui/index.d.ts +0 -2
  154. package/dist/tui/index.d.ts.map +0 -1
  155. package/dist/tui/index.js +0 -486
  156. package/dist/tui/index.js.map +0 -1
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Claude Forge - Stop Hook
3
-
4
- SOCKET_PATH="${CLAUDE_FORGE_SOCKET:-$HOME/.claude-forge/daemon.sock}"
5
-
6
- # 读取 stdin
7
- INPUT=$(cat 2>/dev/null || echo '{}')
8
-
9
- # 提取 cwd
10
- PROJECT_PATH=$(echo "$INPUT" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("cwd",""))' 2>/dev/null || echo '')
11
- PROJECT_PATH="${PROJECT_PATH:-${PWD}}"
12
-
13
- # 构造事件 JSON(通过环境变量传参,避免 shell 注入)
14
- EVENT=$(FORGE_PROJECT_PATH="$PROJECT_PATH" \
15
- python3 -c "
16
- import json, os
17
- from datetime import datetime
18
- print(json.dumps({
19
- 'hook_type': 'Stop',
20
- 'timestamp': datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
21
- 'session_id': os.environ.get('CLAUDE_CODE_SESSION_ID', 'cli'),
22
- 'project_path': os.environ.get('FORGE_PROJECT_PATH', ''),
23
- 'tool_name': 'stop',
24
- 'tool_input': {},
25
- }))
26
- " 2>/dev/null)
27
-
28
- echo "$EVENT" | nc -U -w 1 "$SOCKET_PATH" 2>/dev/null || true
29
- exit 0
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Claude Forge - UserPromptSubmit Hook
3
- # 拦截用户输入,触发意图分析和自动 Pipeline 编排
4
-
5
- SOCKET_PATH="${CLAUDE_FORGE_SOCKET:-$HOME/.claude-forge/daemon.sock}"
6
-
7
- # daemon 未运行,快速跳过
8
- if [ ! -S "$SOCKET_PATH" ]; then
9
- exit 0
10
- fi
11
-
12
- # 读取 stdin(Claude Code 传入的 JSON)
13
- INPUT=$(cat)
14
-
15
- # 提取字段
16
- USER_PROMPT=$(echo "$INPUT" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("prompt",""))' 2>/dev/null || echo '')
17
- PROJECT_PATH=$(echo "$INPUT" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("cwd",""))' 2>/dev/null || echo '')
18
- PROJECT_PATH="${PROJECT_PATH:-${PWD}}"
19
-
20
- # 空 prompt 跳过
21
- if [ -z "$USER_PROMPT" ]; then
22
- exit 0
23
- fi
24
-
25
- # 安全地构造事件 JSON(通过环境变量传参,避免 shell 注入)
26
- EVENT=$(FORGE_USER_PROMPT="$USER_PROMPT" FORGE_PROJECT_PATH="$PROJECT_PATH" \
27
- python3 -c "
28
- import json, os
29
- from datetime import datetime
30
- print(json.dumps({
31
- 'hook_type': 'UserPromptSubmit',
32
- 'timestamp': datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
33
- 'session_id': os.environ.get('CLAUDE_CODE_SESSION_ID', 'cli'),
34
- 'project_path': os.environ.get('FORGE_PROJECT_PATH', ''),
35
- 'tool_name': 'UserPrompt',
36
- 'tool_input': {'user_prompt': os.environ.get('FORGE_USER_PROMPT', '')},
37
- }))
38
- " 2>/dev/null)
39
-
40
- if [ -z "$EVENT" ]; then
41
- exit 0
42
- fi
43
-
44
- # 发送事件并等待响应(10 秒超时,AI 分析可能需要更多时间)
45
- RESPONSE=$(echo "$EVENT" | nc -U -w 10 "$SOCKET_PATH" 2>/dev/null)
46
-
47
- if [ -n "$RESPONSE" ]; then
48
- HAS_CONTEXT=$(echo "$RESPONSE" | python3 -c 'import sys,json; d=json.load(sys.stdin); print("yes" if d.get("additionalContext") else "no")' 2>/dev/null || echo 'no')
49
- if [ "$HAS_CONTEXT" = "yes" ]; then
50
- # stderr 输出到终端,让用户实时看到决策过程
51
- # 用 fd3 保存真实 stderr,python3 错误丢弃,面板输出保留
52
- exec 3>&2
53
- echo "$RESPONSE" | python3 -c '
54
- import sys, json, os
55
- d = json.load(sys.stdin)
56
- ctx = d.get("additionalContext", "")
57
- lines = []
58
- for line in ctx.split("\n"):
59
- if line.startswith("请在回复"):
60
- break
61
- if line.strip():
62
- lines.append(line)
63
- if lines:
64
- os.write(3, ("\033[36m" + "\n".join(lines) + "\033[0m\n").encode())
65
- ' 2>/dev/null
66
- exec 3>&-
67
- # stdout 返回给 Claude Code
68
- echo "$RESPONSE"
69
- fi
70
- fi
71
-
72
- exit 0
@@ -1,25 +0,0 @@
1
- import type { ForgeEvent } from '../types/index.js';
2
- import { SkillRegistry } from './registry.js';
3
- export declare class SkillRecommender {
4
- private registry;
5
- private sessionRecommended;
6
- constructor(registry: SkillRegistry);
7
- /**
8
- * 根据工具调用事件推荐 Skill
9
- * 返回 additionalContext 或 null
10
- */
11
- recommend(event: ForgeEvent): string | null;
12
- /**
13
- * 检测工具调用中是否使用了已注册的 Skill
14
- */
15
- private detectSkillUsage;
16
- /**
17
- * 根据工具调用内容匹配可能有用的 Skill
18
- */
19
- private matchSkills;
20
- /**
21
- * 会话结束时重置推荐记录
22
- */
23
- resetSession(): void;
24
- }
25
- //# sourceMappingURL=recommender.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"recommender.d.ts","sourceRoot":"","sources":["../../src/skill-registry/recommender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,kBAAkB,CAAqB;gBAEnC,QAAQ,EAAE,aAAa;IAInC;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAyB3C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;OAEG;IACH,OAAO,CAAC,WAAW;IAkCnB;;OAEG;IACH,YAAY,IAAI,IAAI;CAGrB"}
@@ -1,91 +0,0 @@
1
- export class SkillRecommender {
2
- registry;
3
- sessionRecommended = new Set();
4
- constructor(registry) {
5
- this.registry = registry;
6
- }
7
- /**
8
- * 根据工具调用事件推荐 Skill
9
- * 返回 additionalContext 或 null
10
- */
11
- recommend(event) {
12
- const inputStr = event.tool_input ? JSON.stringify(event.tool_input) : '';
13
- const toolName = event.tool_name || '';
14
- // 检测 Skill 调用(Bash 中包含 skill 名或 lark-cli 等)
15
- this.detectSkillUsage(event);
16
- // 根据上下文推荐(只在每个 session 推荐一次)
17
- const recommendations = this.matchSkills(toolName, inputStr);
18
- const newRecs = recommendations.filter(r => !this.sessionRecommended.has(r.name));
19
- if (newRecs.length === 0)
20
- return null;
21
- // 标记已推荐
22
- for (const r of newRecs) {
23
- this.sessionRecommended.add(r.name);
24
- }
25
- const recList = newRecs
26
- .map(r => `- **${r.name}**: ${r.description.substring(0, 80)}`)
27
- .join('\n');
28
- return `[Forge 能力推荐] 检测到相关操作,以下 Skill 可能有帮助:\n${recList}`;
29
- }
30
- /**
31
- * 检测工具调用中是否使用了已注册的 Skill
32
- */
33
- detectSkillUsage(event) {
34
- if (event.tool_name !== 'Bash' && event.tool_name !== 'Skill')
35
- return;
36
- const inputStr = event.tool_input ? JSON.stringify(event.tool_input) : '';
37
- // 检测 lark-cli 调用
38
- const larkMatch = inputStr.match(/lark-cli\s+(\w+)/);
39
- if (larkMatch) {
40
- const skillName = `lark-${larkMatch[1]}`;
41
- this.registry.recordUsage(skillName, true);
42
- }
43
- // 检测 Skill 工具调用
44
- if (event.tool_name === 'Skill') {
45
- const skillField = event.tool_input?.skill;
46
- if (skillField) {
47
- this.registry.recordUsage(skillField, true);
48
- }
49
- }
50
- }
51
- /**
52
- * 根据工具调用内容匹配可能有用的 Skill
53
- */
54
- matchSkills(toolName, inputStr) {
55
- const matches = [];
56
- const allSkills = this.registry.getAll();
57
- const input = inputStr.toLowerCase();
58
- // 关键词 → Skill 映射
59
- const KEYWORD_MAP = [
60
- { keywords: ['飞书', 'feishu', 'lark'], skills: ['lark-shared'] },
61
- { keywords: ['日历', 'calendar', '日程', '会议'], skills: ['lark-calendar'] },
62
- { keywords: ['消息', 'message', '群聊', 'chat'], skills: ['lark-im'] },
63
- { keywords: ['文档', 'document', 'doc'], skills: ['lark-doc'] },
64
- { keywords: ['表格', 'sheet', 'spreadsheet'], skills: ['lark-sheets'] },
65
- { keywords: ['多维表格', 'bitable', 'base'], skills: ['lark-base'] },
66
- { keywords: ['任务', 'task', '待办', 'todo'], skills: ['lark-task'] },
67
- { keywords: ['邮件', 'email', 'mail'], skills: ['lark-mail'] },
68
- { keywords: ['知识库', 'wiki'], skills: ['lark-wiki'] },
69
- { keywords: ['云空间', 'drive', '文件管理'], skills: ['lark-drive'] },
70
- { keywords: ['通讯录', 'contact', '员工', '搜索人'], skills: ['lark-contact'] },
71
- ];
72
- for (const mapping of KEYWORD_MAP) {
73
- if (mapping.keywords.some(kw => input.includes(kw))) {
74
- for (const skillName of mapping.skills) {
75
- const skill = this.registry.get(skillName);
76
- if (skill && !matches.some(m => m.name === skill.name)) {
77
- matches.push(skill);
78
- }
79
- }
80
- }
81
- }
82
- return matches;
83
- }
84
- /**
85
- * 会话结束时重置推荐记录
86
- */
87
- resetSession() {
88
- this.sessionRecommended.clear();
89
- }
90
- }
91
- //# sourceMappingURL=recommender.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"recommender.js","sourceRoot":"","sources":["../../src/skill-registry/recommender.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=claude-api.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claude-api.test.d.ts","sourceRoot":"","sources":["../../src/tests/claude-api.test.ts"],"names":[],"mappings":""}
@@ -1,96 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- const mockCreate = vi.hoisted(() => vi.fn());
3
- vi.mock('@anthropic-ai/sdk', () => ({
4
- default: vi.fn(function () {
5
- return { messages: { create: mockCreate } };
6
- }),
7
- }));
8
- import { ClaudeAPI, ApiTimeoutError, CircuitOpenError } from '../utils/claude-api.js';
9
- function makeApi() {
10
- process.env.ANTHROPIC_API_KEY = 'test-key';
11
- return new ClaudeAPI(undefined, 'claude-test');
12
- }
13
- function okResponse(text = 'ok') {
14
- return Promise.resolve({ content: [{ type: 'text', text }] });
15
- }
16
- describe('ClaudeAPI', () => {
17
- beforeEach(() => {
18
- process.env.ANTHROPIC_API_KEY = 'test-key';
19
- mockCreate.mockReset();
20
- });
21
- afterEach(() => {
22
- delete process.env.ANTHROPIC_API_KEY;
23
- });
24
- // ── 正常调用 ──────────────────────────────────────────────────────────────
25
- it('成功调用返回文本内容', async () => {
26
- mockCreate.mockReturnValueOnce(okResponse('hello'));
27
- expect(await makeApi().complete('prompt')).toBe('hello');
28
- });
29
- it('content 为空时返回空字符串', async () => {
30
- mockCreate.mockReturnValueOnce(Promise.resolve({ content: [] }));
31
- expect(await makeApi().complete('prompt')).toBe('');
32
- });
33
- // ── 重试逻辑 ──────────────────────────────────────────────────────────────
34
- it('网络错误后重试并最终成功', async () => {
35
- mockCreate
36
- .mockRejectedValueOnce(new Error('network'))
37
- .mockReturnValueOnce(okResponse('ok'));
38
- const api = makeApi();
39
- expect(await api.complete('prompt', 'test', 1, 30_000)).toBe('ok');
40
- expect(mockCreate).toHaveBeenCalledTimes(2);
41
- expect(api.getStats().totalRetries).toBe(1);
42
- });
43
- it('4xx 错误不重试', async () => {
44
- mockCreate.mockRejectedValue(Object.assign(new Error('bad request'), { status: 400 }));
45
- const api = makeApi();
46
- await expect(api.complete('prompt', 'test', 2, 30_000)).rejects.toThrow('bad request');
47
- expect(mockCreate).toHaveBeenCalledTimes(1);
48
- });
49
- it('超时后抛出 ApiTimeoutError 且不重试', async () => {
50
- mockCreate.mockReturnValue(new Promise(() => { })); // never resolves
51
- const api = makeApi();
52
- await expect(api.complete('prompt', 'test', 2, 10)).rejects.toBeInstanceOf(ApiTimeoutError);
53
- expect(mockCreate).toHaveBeenCalledTimes(1);
54
- });
55
- // ── 熔断器 ────────────────────────────────────────────────────────────────
56
- it('连续失败 5 次后触发熔断', async () => {
57
- mockCreate.mockRejectedValue(new Error('server error'));
58
- const api = makeApi();
59
- for (let i = 0; i < 5; i++) {
60
- await expect(api.complete('prompt', 'test', 0, 30_000)).rejects.toThrow();
61
- }
62
- await expect(api.complete('prompt', 'test', 0, 30_000)).rejects.toBeInstanceOf(CircuitOpenError);
63
- expect(api.getStats().circuitBreakerTrips).toBe(1);
64
- });
65
- it('成功后重置熔断计数', async () => {
66
- mockCreate
67
- .mockRejectedValueOnce(new Error('fail'))
68
- .mockReturnValueOnce(okResponse())
69
- .mockRejectedValue(new Error('fail'));
70
- const api = makeApi();
71
- await expect(api.complete('prompt', 'test', 0, 30_000)).rejects.toThrow();
72
- await api.complete('prompt', 'test', 0, 30_000);
73
- for (let i = 0; i < 4; i++) {
74
- await expect(api.complete('prompt', 'test', 0, 30_000)).rejects.toThrow('fail');
75
- }
76
- expect(api.getStats().circuitBreakerTrips).toBe(0);
77
- });
78
- // ── 统计 ──────────────────────────────────────────────────────────────────
79
- it('formatStats 包含调用数', async () => {
80
- mockCreate.mockReturnValue(okResponse());
81
- const api = makeApi();
82
- await api.complete('prompt', 'distill');
83
- const stats = api.formatStats();
84
- expect(stats).toContain('API calls: 1');
85
- expect(stats).toContain('[distill]');
86
- });
87
- it('getStats 返回快照,修改不影响内部状态', async () => {
88
- mockCreate.mockReturnValue(okResponse());
89
- const api = makeApi();
90
- await api.complete('prompt', 'x');
91
- const snap = api.getStats();
92
- snap.totalCalls = 999;
93
- expect(api.getStats().totalCalls).toBe(1);
94
- });
95
- });
96
- //# sourceMappingURL=claude-api.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claude-api.test.js","sourceRoot":"","sources":["../../src/tests/claude-api.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=conversational-config-handler.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conversational-config-handler.test.d.ts","sourceRoot":"","sources":["../../src/tests/conversational-config-handler.test.ts"],"names":[],"mappings":""}
@@ -1,107 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- const { mockModulesConfigManager, mockClaudeMdEngine } = vi.hoisted(() => {
3
- const mockModulesConfigRead = vi.fn().mockReturnValue({});
4
- const mockModulesConfigWrite = vi.fn();
5
- const mockClaudeMdGenerate = vi.fn().mockResolvedValue(true);
6
- const mockClaudeMdRefine = vi.fn().mockResolvedValue(true);
7
- const mockModulesConfigManager = vi.fn(function () {
8
- return { read: mockModulesConfigRead, write: mockModulesConfigWrite };
9
- });
10
- const mockClaudeMdEngine = vi.fn(function () {
11
- return { generate: mockClaudeMdGenerate, refine: mockClaudeMdRefine };
12
- });
13
- return { mockModulesConfigManager, mockClaudeMdEngine };
14
- });
15
- vi.mock('../claudemd/modules-config.js', () => ({
16
- ModulesConfigManager: mockModulesConfigManager,
17
- }));
18
- vi.mock('../claudemd/index.js', () => ({
19
- ClaudeMdEngine: mockClaudeMdEngine,
20
- }));
21
- vi.mock('fs', async (importOriginal) => {
22
- const actual = await importOriginal();
23
- const mocked = {
24
- ...actual,
25
- existsSync: vi.fn().mockReturnValue(false),
26
- unlinkSync: vi.fn(),
27
- mkdirSync: vi.fn(),
28
- writeFileSync: vi.fn(),
29
- readFileSync: vi.fn().mockReturnValue(''),
30
- };
31
- return { ...mocked, default: mocked };
32
- });
33
- import { ConversationalConfigHandler } from '../daemon/handlers/conversational-config-handler.js';
34
- function makeCtx(overrides = {}) {
35
- return {
36
- storage: { queryEvents: vi.fn().mockReturnValue([]) },
37
- config: {},
38
- trigger: null,
39
- resumeEngine: null,
40
- orchestration: null,
41
- skillRegistry: {},
42
- pipelineEngine: null,
43
- intentEngine: null,
44
- knowledgeEngine: null,
45
- qualityGate: null,
46
- profileManager: null,
47
- sharedApi: { complete: vi.fn() },
48
- patternEngine: null,
49
- conventionManager: { getActivePrompt: vi.fn().mockReturnValue(null), getActiveConventions: vi.fn().mockReturnValue([]) },
50
- checkedProjects: new Set(),
51
- ...overrides,
52
- };
53
- }
54
- describe('ConversationalConfigHandler', () => {
55
- let handler;
56
- beforeEach(() => {
57
- vi.clearAllMocks();
58
- handler = new ConversationalConfigHandler(makeCtx());
59
- });
60
- // ── 无匹配 ────────────────────────────────────────────────────────────────
61
- it('无意图时返回 undefined', async () => {
62
- expect(await handler.handle('帮我写一个排序算法', '/proj')).toBeUndefined();
63
- });
64
- // ── 人设意图 ──────────────────────────────────────────────────────────────
65
- it('检测"设置人设为"意图', async () => {
66
- const result = await handler.handle('设置人设为:资深 TypeScript 工程师', '/proj');
67
- expect(result).toBeDefined();
68
- expect(result).toContain('已设置人设');
69
- });
70
- it('检测"重置人设"意图(无已有人设)', async () => {
71
- const result = await handler.handle('重置人设', '/proj');
72
- expect(result).toBeDefined();
73
- expect(result).toContain('人设');
74
- });
75
- it('人设内容为空时不抛出', async () => {
76
- await expect(handler.handle('设置人设为:', '/proj')).resolves.toBeDefined();
77
- });
78
- // ── CLAUDE.md 意图 ────────��───────────────────────────────────────────────
79
- it('检测"生成 CLAUDE.md"意图', async () => {
80
- const result = await handler.handle('生成 CLAUDE.md', '/proj');
81
- expect(result).toBeDefined();
82
- expect(result).toContain('CLAUDE.md');
83
- });
84
- it('检测"强制生成 CLAUDE.md"意图', async () => {
85
- const result = await handler.handle('强制生成 CLAUDE.md', '/proj');
86
- expect(result).toBeDefined();
87
- expect(result).toContain('CLAUDE.md');
88
- });
89
- it('检测"细化 CLAUDE.md"意图', async () => {
90
- const result = await handler.handle('细化 CLAUDE.md', '/proj');
91
- expect(result).toBeDefined();
92
- });
93
- it('sharedApi 为 null 时 CLAUDE.md 操作返回不可用提示', async () => {
94
- handler = new ConversationalConfigHandler(makeCtx({ sharedApi: null }));
95
- const result = await handler.handle('生成 CLAUDE.md', '/proj');
96
- expect(result).toContain('不可用');
97
- });
98
- // ── 技能意图 ──────────────────────────────────────────────────────────────
99
- it('skillRegistry 为 null 时返回不可用提示', async () => {
100
- handler = new ConversationalConfigHandler(makeCtx({
101
- skillRegistry: null,
102
- }));
103
- const result = await handler.handle('添加技能 sk:内容', '/proj');
104
- expect(result).toContain('不可用');
105
- });
106
- });
107
- //# sourceMappingURL=conversational-config-handler.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conversational-config-handler.test.js","sourceRoot":"","sources":["../../src/tests/conversational-config-handler.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=doc-sync.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"doc-sync.test.d.ts","sourceRoot":"","sources":["../../src/tests/doc-sync.test.ts"],"names":[],"mappings":""}
@@ -1,148 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- // ESM 下必须在 import 前用 vi.mock hoist 拦截 fs
3
- vi.mock('fs', async (importOriginal) => {
4
- const actual = await importOriginal();
5
- return {
6
- ...actual,
7
- readdirSync: vi.fn(),
8
- readFileSync: vi.fn(),
9
- writeFileSync: vi.fn(),
10
- };
11
- });
12
- import * as fs from 'fs';
13
- import * as path from 'path';
14
- import { DocSyncEngine, extractChangedFiles, findProjectDocs } from '../doc-sync/index.js';
15
- // ── helpers ──────────────────────────────────────────────────────────────────
16
- function makeEvent(overrides = {}) {
17
- return {
18
- event_id: 'e1',
19
- session_id: 's1',
20
- project_path: '/proj',
21
- timestamp: '2026-01-01T00:00:00Z',
22
- hook_type: 'PostToolUse',
23
- distilled: false,
24
- ...overrides,
25
- };
26
- }
27
- const mockReaddirSync = vi.mocked(fs.readdirSync);
28
- const mockReadFileSync = vi.mocked(fs.readFileSync);
29
- const mockWriteFileSync = vi.mocked(fs.writeFileSync);
30
- // ── extractChangedFiles ───────────────────────────────────────────────────────
31
- describe('extractChangedFiles', () => {
32
- it('提取 Write 操作的 file_path', () => {
33
- const events = [
34
- makeEvent({ tool_name: 'Write', tool_input: { file_path: '/proj/src/foo.ts' } }),
35
- makeEvent({ tool_name: 'Read', tool_input: { file_path: '/proj/src/bar.ts' } }),
36
- ];
37
- expect(extractChangedFiles(events)).toEqual(['/proj/src/foo.ts']);
38
- });
39
- it('提取 Edit 操作', () => {
40
- const events = [makeEvent({ tool_name: 'Edit', tool_input: { file_path: '/proj/a.ts' } })];
41
- expect(extractChangedFiles(events)).toEqual(['/proj/a.ts']);
42
- });
43
- it('去重相同路径', () => {
44
- const events = [
45
- makeEvent({ tool_name: 'Write', tool_input: { file_path: '/proj/a.ts' } }),
46
- makeEvent({ tool_name: 'Edit', tool_input: { file_path: '/proj/a.ts' } }),
47
- ];
48
- expect(extractChangedFiles(events)).toHaveLength(1);
49
- });
50
- it('无写操作时返回空数组', () => {
51
- const events = [
52
- makeEvent({ tool_name: 'Read', tool_input: { file_path: '/proj/a.ts' } }),
53
- makeEvent({ hook_type: 'Stop' }),
54
- ];
55
- expect(extractChangedFiles(events)).toEqual([]);
56
- });
57
- });
58
- // ── findProjectDocs ───────────────────────────────────────────────────────────
59
- describe('findProjectDocs', () => {
60
- beforeEach(() => mockReaddirSync.mockReset());
61
- it('返回 .md 文件并排除 node_modules', () => {
62
- mockReaddirSync.mockImplementation((dir) => {
63
- if (dir === '/proj') {
64
- return [
65
- { name: 'README.md', isDirectory: () => false, isFile: () => true },
66
- { name: 'node_modules', isDirectory: () => true, isFile: () => false },
67
- { name: 'src', isDirectory: () => true, isFile: () => false },
68
- ];
69
- }
70
- if (dir === '/proj/src') {
71
- return [
72
- { name: 'DESIGN.md', isDirectory: () => false, isFile: () => true },
73
- { name: 'index.ts', isDirectory: () => false, isFile: () => true },
74
- ];
75
- }
76
- return [];
77
- });
78
- const docs = findProjectDocs('/proj');
79
- expect(docs).toContain(path.join('/proj', 'README.md'));
80
- expect(docs).toContain(path.join('/proj', 'src', 'DESIGN.md'));
81
- expect(docs.some(d => d.includes('node_modules'))).toBe(false);
82
- });
83
- });
84
- // ── DocSyncEngine ─────────────────────────────────────────────────────────────
85
- describe('DocSyncEngine', () => {
86
- let mockApi;
87
- let engine;
88
- beforeEach(() => {
89
- mockReaddirSync.mockReset();
90
- mockReadFileSync.mockReset();
91
- mockWriteFileSync.mockReset();
92
- mockApi = { complete: vi.fn() };
93
- engine = new DocSyncEngine(mockApi);
94
- });
95
- it('无写操作时不调用 API', async () => {
96
- mockReaddirSync.mockReturnValue([]);
97
- const events = [makeEvent({ tool_name: 'Read', tool_input: { file_path: '/proj/a.ts' } })];
98
- await engine.sync('s1', '/proj', events);
99
- expect(mockApi.complete).not.toHaveBeenCalled();
100
- });
101
- it('AI 返回空内容时不写文件', async () => {
102
- mockReaddirSync.mockImplementation((dir) => {
103
- if (dir === '/proj') {
104
- return [{ name: 'README.md', isDirectory: () => false, isFile: () => true }];
105
- }
106
- return [];
107
- });
108
- const events = [makeEvent({ tool_name: 'Write', tool_input: { file_path: '/proj/src/foo.ts' } })];
109
- mockApi.complete.mockResolvedValueOnce(''); // detect → 无受影响文档
110
- await engine.sync('s1', '/proj', events);
111
- expect(mockWriteFileSync).not.toHaveBeenCalled();
112
- });
113
- it('AI 返回受影响文档后执行更新', async () => {
114
- mockReaddirSync.mockImplementation((dir) => {
115
- if (dir === '/proj') {
116
- return [{ name: 'docs', isDirectory: () => true, isFile: () => false }];
117
- }
118
- if (dir === '/proj/docs') {
119
- return [{ name: 'plan.md', isDirectory: () => false, isFile: () => true }];
120
- }
121
- return [];
122
- });
123
- mockReadFileSync.mockReturnValue('# 旧内容');
124
- const events = [makeEvent({ tool_name: 'Write', tool_input: { file_path: '/proj/src/foo.ts' } })];
125
- mockApi.complete.mockResolvedValueOnce('docs/plan.md'); // detect
126
- mockApi.complete.mockResolvedValueOnce('# 新内容'); // update
127
- await engine.sync('s1', '/proj', events);
128
- expect(mockWriteFileSync).toHaveBeenCalledWith(path.join('/proj', 'docs', 'plan.md'), '# 新内容', 'utf-8');
129
- });
130
- it('AI 返回与现有内容相同时不写文件', async () => {
131
- mockReaddirSync.mockImplementation((dir) => {
132
- if (dir === '/proj') {
133
- return [{ name: 'docs', isDirectory: () => true, isFile: () => false }];
134
- }
135
- if (dir === '/proj/docs') {
136
- return [{ name: 'plan.md', isDirectory: () => false, isFile: () => true }];
137
- }
138
- return [];
139
- });
140
- mockReadFileSync.mockReturnValue('# 内容不变');
141
- const events = [makeEvent({ tool_name: 'Write', tool_input: { file_path: '/proj/src/foo.ts' } })];
142
- mockApi.complete.mockResolvedValueOnce('docs/plan.md');
143
- mockApi.complete.mockResolvedValueOnce('# 内容不变'); // 与原文相同
144
- await engine.sync('s1', '/proj', events);
145
- expect(mockWriteFileSync).not.toHaveBeenCalled();
146
- });
147
- });
148
- //# sourceMappingURL=doc-sync.test.js.map