@sanqianx/claude-ai-workbench 0.11.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.
Files changed (90) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +129 -0
  3. package/apps/agent-terminal/app.css +567 -0
  4. package/apps/agent-terminal/app.js +2392 -0
  5. package/apps/agent-terminal/index.html +158 -0
  6. package/apps/ai-profile-settings-host/index.html +25 -0
  7. package/apps/ai-profile-settings-host/page.css +1 -0
  8. package/apps/ai-profile-settings-host/page.js +113 -0
  9. package/apps/minimal-test-host/README.md +15 -0
  10. package/apps/minimal-test-host/host.css +2 -0
  11. package/apps/minimal-test-host/host.js +35 -0
  12. package/apps/minimal-test-host/index.html +58 -0
  13. package/apps/minimal-test-host/optional-mock/fault-injection.js +15 -0
  14. package/docs/sdk/FILE_LAYOUT.md +31 -0
  15. package/docs/sdk/INTEGRATION.md +126 -0
  16. package/docs/sdk/README.md +21 -0
  17. package/docs/sdk/openapi.yaml +760 -0
  18. package/package.json +51 -0
  19. package/packages/client/README.md +11 -0
  20. package/packages/client/index.d.ts +81 -0
  21. package/packages/client/index.js +198 -0
  22. package/packages/client/package.json +29 -0
  23. package/packages/contracts/README.md +9 -0
  24. package/packages/contracts/index.d.ts +252 -0
  25. package/packages/contracts/index.js +295 -0
  26. package/packages/contracts/package.json +24 -0
  27. package/packages/core/README.md +8 -0
  28. package/packages/core/index.d.ts +76 -0
  29. package/packages/core/index.js +214 -0
  30. package/packages/core/package.json +29 -0
  31. package/packages/server/README.md +17 -0
  32. package/packages/server/bin/agent-terminal-server.js +36 -0
  33. package/packages/server/bin/claude-workbench-server.js +46 -0
  34. package/packages/server/bin/claude-workbench.js +225 -0
  35. package/packages/server/index.d.ts +145 -0
  36. package/packages/server/index.js +183 -0
  37. package/packages/server/lib/agent-discovery.js +112 -0
  38. package/packages/server/lib/agent-registry.js +98 -0
  39. package/packages/server/lib/attachment-store.js +78 -0
  40. package/packages/server/lib/bridges/app-server-bridge.js +372 -0
  41. package/packages/server/lib/bridges/opencode-server-bridge.js +490 -0
  42. package/packages/server/lib/capture-settings-store.js +34 -0
  43. package/packages/server/lib/context-resolver.js +40 -0
  44. package/packages/server/lib/drivers/cli-json-runner.js +960 -0
  45. package/packages/server/lib/drivers/codex-driver.js +1413 -0
  46. package/packages/server/lib/drivers/opencode-driver.js +1527 -0
  47. package/packages/server/lib/drivers/zcode-driver.js +1167 -0
  48. package/packages/server/lib/event-stream.js +35 -0
  49. package/packages/server/lib/folder-picker.js +623 -0
  50. package/packages/server/lib/http-handler.js +254 -0
  51. package/packages/server/lib/json-file.js +19 -0
  52. package/packages/server/lib/model-detection.js +295 -0
  53. package/packages/server/lib/profile-store.js +149 -0
  54. package/packages/server/lib/project-store.js +102 -0
  55. package/packages/server/lib/session-archive-store.js +53 -0
  56. package/packages/server/lib/system-doctor.js +119 -0
  57. package/packages/server/lib/ui-preferences-store.js +48 -0
  58. package/packages/server/lib/workbench-service.js +620 -0
  59. package/packages/server/package.json +34 -0
  60. package/packages/ui/README.md +30 -0
  61. package/packages/ui/ai-profile-settings.d.ts +45 -0
  62. package/packages/ui/ai-profile-settings.js +283 -0
  63. package/packages/ui/claude-terminal.d.ts +31 -0
  64. package/packages/ui/claude-terminal.js +343 -0
  65. package/packages/ui/index.d.ts +18 -0
  66. package/packages/ui/index.js +51 -0
  67. package/packages/ui/package.json +42 -0
  68. package/packages/ui/theme.css +13 -0
  69. package/src/assets/claude-prompts.json +21 -0
  70. package/src/backend/http/ai-profile-routes.js +61 -0
  71. package/src/backend/http/claude-routes.js +170 -0
  72. package/src/backend/http/node-helpers.js +45 -0
  73. package/src/backend/index.js +21 -0
  74. package/src/backend/lib/ai-adapter.js +144 -0
  75. package/src/backend/lib/ai-profile-store.js +259 -0
  76. package/src/backend/lib/ai-vendor-presets.js +83 -0
  77. package/src/backend/lib/ai-workspace.js +140 -0
  78. package/src/backend/lib/automation-config.js +253 -0
  79. package/src/backend/lib/claude-cli-runner.js +1749 -0
  80. package/src/backend/lib/data-dir.js +169 -0
  81. package/src/backend/lib/llm-client.js +176 -0
  82. package/src/backend/lib/model-context-windows.js +82 -0
  83. package/src/backend/lib/prompt-registry.js +166 -0
  84. package/src/backend/lib/runtime-config.js +121 -0
  85. package/src/index.js +7 -0
  86. package/src/sdk-wave-a.ts +1 -0
  87. package/src/sdk-wave-b.ts +1 -0
  88. package/src/sdk-wave-c.ts +1 -0
  89. package/src/sdk-wave-d.ts +1 -0
  90. package/src/sdk-wave-e.ts +1 -0
@@ -0,0 +1,253 @@
1
+ const path = require('path');
2
+
3
+ const KNOWLEDGE_MODES = new Set(['requestApproval', 'autoApply', 'directWriteKb']);
4
+ const PERMISSION_MODES = new Set(['plan', 'default', 'acceptEdits', 'auto', 'bypassPermissions']);
5
+
6
+ const DEFAULT_HOOK_PROMPT_TEMPLATE = `请根据以下 Git 提交更新当前项目知识库。
7
+
8
+ 项目:{{displayName}} / {{projectSlug}}
9
+ 知识库路径:{{kbPath}}
10
+ 源码路径:{{repoPath}}
11
+ 分支:{{branch}}
12
+ 提交:{{commitHash}}
13
+ 标题:{{commitSubject}}
14
+ 作者:{{commitAuthor}}
15
+ 日期:{{commitDate}}
16
+
17
+ 变更文件:
18
+ {{changedFiles}}
19
+
20
+ Diff 摘要:
21
+ {{diffSummary}}
22
+
23
+ 当前知识库模式:{{knowledgeMode}}
24
+ Claude 工具权限模式:{{permissionMode}}
25
+
26
+ 执行边界:
27
+ - 只处理当前提交对知识库的影响。
28
+ - 只能读取源码项目,不能修改源码项目。
29
+ - 只能修改当前项目知识库目录内的内容。
30
+ - 不能修改其他项目知识库。
31
+ - Bash 只能用于只读检查,不能执行写操作或危险命令。
32
+
33
+ 知识库更新原则:
34
+ 一、语言要求:知识库内新增或修改的所有文本必须使用中文撰写;类名、函数名、API 端点、配置项、第三方库名称等专有名词可以保留英文原文。
35
+ 二、上下文要求:如果当前环境提供对话记忆、项目记忆或历史讨论上下文,请结合这些信息理解本次代码变更背后的意图、讨论背景和决策原因;如果没有可用记忆,则基于源码、diff 和现有知识库谨慎更新,不要编造背景。
36
+ 三、全面性要求:基于本次提交的变更文件及 Diff 摘要,系统性判断是否需要更新新增功能、行为变更、移除项、架构调整、配置变更、数据库迁移、使用方式和注意事项。
37
+ 四、结构化更新:知识库修改应是增量式的。保留历史信息,同时为本次提交新增可回溯的章节或条目,建议记录提交哈希、日期、作者和关键变更文件。若变更影响 README、API 参考、开发指南或模块说明,请同步更新相关章节。
38
+ 五、目标导向:更新后的知识库应让未来维护者无需重新翻阅大量代码,也能快速理解本次提交的实质影响,并能够正确使用或适配新代码。
39
+ 六、证据约束:只记录能从代码、diff、现有知识库或明确上下文中确认的信息;不确定的内容请标注待确认,不要写成事实。
40
+ `;
41
+
42
+ const DEFAULT_INIT_PROMPT_TEMPLATE = `请为以下项目初始化当前项目知识库。
43
+
44
+ 项目:{{displayName}} / {{projectSlug}}
45
+ 知识库路径:{{kbPath}}
46
+ 源码路径:{{repoPath}}
47
+ 分支:{{branch}}
48
+ 当前提交:{{commitHash}}
49
+ 远程仓库:{{remoteUrl}}
50
+ 主要语言:{{primaryLanguage}}
51
+ 项目标签:{{tags}}
52
+
53
+ 源码文件概览:
54
+ {{sourceOverview}}
55
+
56
+ 当前知识库模式:{{knowledgeMode}}
57
+ Claude 工具权限模式:{{permissionMode}}
58
+
59
+ 执行边界:
60
+ - 这是项目首次导入后的知识库初始化任务,不是某一次提交的增量更新。
61
+ - 可以读取源码项目来理解结构、入口、配置、脚本、依赖和重要模块。
62
+ - 不能修改源码项目。
63
+ - 只能修改当前项目知识库目录内的内容。
64
+ - 不能修改其他项目知识库。
65
+ - Bash 只能用于只读检查,不能执行写操作或危险命令。
66
+
67
+ 初始化目标:
68
+ 一、用中文建立一份可直接使用的项目知识库,专有名词可以保留英文。
69
+ 二、优先更新 README、ARCHITECTURE、modules、changes 等现有知识库结构;如果已有文件存在,请增量补全,不要无意义覆盖。
70
+ 三、总结项目用途、核心能力、运行方式、目录结构、关键模块、数据流、配置项、外部依赖、重要命令和常见维护任务。
71
+ 四、识别项目的技术栈、入口文件、构建/测试/发布流程,以及与 Git、Hook、自动化或 AI 模型相关的约束。
72
+ 五、为后续提交级知识库更新留下清晰结构,让未来维护者能按模块和变更历史继续补充。
73
+ 六、只写能从源码、配置、README、package/lock 文件、脚本或现有知识库中确认的信息;不确定内容请标注待确认。
74
+ `;
75
+
76
+ const READ_TOOLS = ['Read', 'Grep', 'Glob'];
77
+ const WRITE_TOOLS = ['Edit', 'Write', 'MultiEdit'];
78
+ const BASH_TOOL = 'Bash';
79
+
80
+ function normalizeKnowledgeMode(value) {
81
+ return KNOWLEDGE_MODES.has(value) ? value : 'requestApproval';
82
+ }
83
+
84
+ function normalizePermissionMode(value) {
85
+ return PERMISSION_MODES.has(value) ? value : 'default';
86
+ }
87
+
88
+ function normalizeAutomationConfig(input) {
89
+ const src = input && typeof input === 'object' ? input : {};
90
+ const maxQueueSizeRaw = Number(src.maxQueueSize);
91
+ const maxQueueSize = Number.isFinite(maxQueueSizeRaw) && maxQueueSizeRaw > 0
92
+ ? Math.floor(maxQueueSizeRaw)
93
+ : 10;
94
+ return {
95
+ enabled: src.enabled === true,
96
+ postCommitEnabled: src.postCommitEnabled === true,
97
+ knowledgeMode: normalizeKnowledgeMode(src.knowledgeMode),
98
+ allowReadOnlyBash: src.allowReadOnlyBash !== false,
99
+ maxQueueSize,
100
+ hookPromptTemplate: typeof src.hookPromptTemplate === 'string' && src.hookPromptTemplate.trim()
101
+ ? src.hookPromptTemplate
102
+ : DEFAULT_HOOK_PROMPT_TEMPLATE,
103
+ initPromptTemplate: typeof src.initPromptTemplate === 'string' && src.initPromptTemplate.trim()
104
+ ? src.initPromptTemplate
105
+ : DEFAULT_INIT_PROMPT_TEMPLATE,
106
+ };
107
+ }
108
+
109
+ function normalizeClaudeWorkbenchConfig(input) {
110
+ const src = input && typeof input === 'object' ? input : {};
111
+ return {
112
+ permissionMode: normalizePermissionMode(src.permissionMode),
113
+ };
114
+ }
115
+
116
+ const normalizeClaudeTerminalConfig = normalizeClaudeWorkbenchConfig;
117
+
118
+ function renderTemplate(template, vars) {
119
+ const source = typeof template === 'string' ? template : DEFAULT_HOOK_PROMPT_TEMPLATE;
120
+ const safeVars = vars && typeof vars === 'object' ? vars : {};
121
+ return source.replace(/\{\{\s*([A-Za-z][A-Za-z0-9_]*)\s*\}\}/g, (match, name) => {
122
+ if (!Object.prototype.hasOwnProperty.call(safeVars, name)) return match;
123
+ const value = safeVars[name];
124
+ return value == null ? '' : String(value);
125
+ });
126
+ }
127
+
128
+ function normalizePathForCompare(value) {
129
+ if (!value || typeof value !== 'string') return '';
130
+ return path.resolve(value).toLowerCase().replace(/[\\\/]+$/, '');
131
+ }
132
+
133
+ function pathsReferToSameLocation(a, b) {
134
+ const left = normalizePathForCompare(a);
135
+ const right = normalizePathForCompare(b);
136
+ return !!left && !!right && left === right;
137
+ }
138
+
139
+ function isInsidePath(root, target) {
140
+ if (!root || !target) return false;
141
+ const rootAbs = path.resolve(root);
142
+ const targetAbs = path.resolve(rootAbs, target);
143
+ const rel = path.relative(rootAbs, targetAbs);
144
+ return rel === '' || (!!rel && !rel.startsWith('..') && !path.isAbsolute(rel));
145
+ }
146
+
147
+ function commandText(input) {
148
+ if (typeof input === 'string') return input;
149
+ if (input && typeof input === 'object') {
150
+ return String(input.command || input.cmd || input.script || '');
151
+ }
152
+ return '';
153
+ }
154
+
155
+ function isReadOnlyBashCommand(input) {
156
+ const command = commandText(input).trim();
157
+ if (!command) return false;
158
+ const lowered = command.toLowerCase();
159
+ const denied = [
160
+ ' rm ', 'del ', 'rmdir ', 'remove-item', ' mv ', 'move ', 'ren ', 'rename-item',
161
+ 'copy ', ' cp ', 'set-content', 'add-content', 'out-file', 'new-item',
162
+ 'git reset', 'git checkout', 'git clean', 'git commit', 'git push',
163
+ 'npm install', 'pnpm install', 'yarn add', 'pip install',
164
+ '>', '>>', '| set-content', '| out-file',
165
+ ];
166
+ const padded = ` ${lowered}`;
167
+ if (denied.some(token => padded.includes(token))) return false;
168
+ const allowedPrefixes = [
169
+ 'git status', 'git log', 'git show', 'git diff',
170
+ 'ls', 'dir', 'get-childitem',
171
+ 'type', 'get-content',
172
+ 'findstr', 'select-string',
173
+ ];
174
+ return allowedPrefixes.some(prefix => lowered === prefix || lowered.startsWith(prefix + ' '));
175
+ }
176
+
177
+ function extractToolPaths(toolName, input) {
178
+ if (!input || typeof input !== 'object') return [];
179
+ const candidates = [];
180
+ for (const key of ['file_path', 'path', 'notebook_path']) {
181
+ if (typeof input[key] === 'string') candidates.push(input[key]);
182
+ }
183
+ if (toolName === 'MultiEdit' && Array.isArray(input.edits) && typeof input.file_path === 'string') {
184
+ candidates.push(input.file_path);
185
+ }
186
+ return candidates;
187
+ }
188
+
189
+ function buildAutomationToolPolicy({ automation, kbPath }) {
190
+ const cfg = normalizeAutomationConfig(automation);
191
+ const canWriteKb = cfg.knowledgeMode === 'autoApply' || cfg.knowledgeMode === 'directWriteKb';
192
+ const allowedTools = [...READ_TOOLS];
193
+ if (canWriteKb) allowedTools.push(...WRITE_TOOLS);
194
+ if (cfg.allowReadOnlyBash) allowedTools.push(BASH_TOOL);
195
+
196
+ return {
197
+ kind: 'kb-automation',
198
+ kbPath,
199
+ knowledgeMode: cfg.knowledgeMode,
200
+ allowReadOnlyBash: cfg.allowReadOnlyBash,
201
+ allowedTools,
202
+ canWriteKb,
203
+ };
204
+ }
205
+
206
+ function evaluateAutomationToolUse(policy, toolName, input) {
207
+ if (!policy || policy.kind !== 'kb-automation') {
208
+ return { behavior: 'ask', reason: 'no automation policy' };
209
+ }
210
+ if (!policy.allowedTools.includes(toolName)) {
211
+ return { behavior: 'deny', reason: `tool is not allowed: ${toolName}` };
212
+ }
213
+ if (READ_TOOLS.includes(toolName)) {
214
+ return { behavior: 'allow', reason: 'read tool allowed' };
215
+ }
216
+ if (toolName === BASH_TOOL) {
217
+ if (!policy.allowReadOnlyBash) return { behavior: 'deny', reason: 'bash disabled' };
218
+ return isReadOnlyBashCommand(input)
219
+ ? { behavior: 'allow', reason: 'read-only bash command allowed' }
220
+ : { behavior: 'deny', reason: 'bash command is not read-only allowlisted' };
221
+ }
222
+ if (WRITE_TOOLS.includes(toolName)) {
223
+ if (!policy.canWriteKb) return { behavior: 'deny', reason: 'knowledge mode is read-only' };
224
+ const paths = extractToolPaths(toolName, input);
225
+ if (!paths.length) return { behavior: 'deny', reason: 'write tool has no file path' };
226
+ for (const p of paths) {
227
+ if (!isInsidePath(policy.kbPath, p)) {
228
+ return { behavior: 'deny', reason: `write path outside current KB: ${p}` };
229
+ }
230
+ }
231
+ return { behavior: 'allow', reason: 'write path is inside current KB' };
232
+ }
233
+ return { behavior: 'deny', reason: `unhandled tool: ${toolName}` };
234
+ }
235
+
236
+ module.exports = {
237
+ KNOWLEDGE_MODES,
238
+ PERMISSION_MODES,
239
+ DEFAULT_HOOK_PROMPT_TEMPLATE,
240
+ DEFAULT_INIT_PROMPT_TEMPLATE,
241
+ normalizeKnowledgeMode,
242
+ normalizePermissionMode,
243
+ normalizeAutomationConfig,
244
+ normalizeClaudeWorkbenchConfig,
245
+ normalizeClaudeTerminalConfig,
246
+ renderTemplate,
247
+ normalizePathForCompare,
248
+ pathsReferToSameLocation,
249
+ isInsidePath,
250
+ isReadOnlyBashCommand,
251
+ buildAutomationToolPolicy,
252
+ evaluateAutomationToolUse,
253
+ };