@sanqianx/claude-ai-workbench 0.13.2 → 0.13.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 (122) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +156 -156
  3. package/apps/agent-terminal/app.css +652 -613
  4. package/apps/agent-terminal/app.js +143 -67
  5. package/apps/agent-terminal/conversation-composer.js +44 -0
  6. package/apps/agent-terminal/index.html +172 -170
  7. package/apps/agent-terminal/panel-host-proof.html +152 -152
  8. package/apps/agent-terminal/panel-proof-content.html +41 -41
  9. package/apps/agent-terminal/right-workspace-dock.css +189 -159
  10. package/apps/agent-terminal/right-workspace-dock.js +652 -521
  11. package/apps/agent-terminal/user-question-card.js +68 -0
  12. package/apps/ai-profile-settings-host/index.html +25 -25
  13. package/apps/ai-profile-settings-host/page.css +1 -1
  14. package/apps/ai-profile-settings-host/page.js +113 -113
  15. package/apps/minimal-test-host/README.md +15 -15
  16. package/apps/minimal-test-host/host.css +2 -2
  17. package/apps/minimal-test-host/host.js +35 -35
  18. package/apps/minimal-test-host/index.html +58 -58
  19. package/apps/minimal-test-host/optional-mock/fault-injection.js +15 -15
  20. package/docs/sdk/FILE_LAYOUT.md +31 -31
  21. package/docs/sdk/INTEGRATION.md +126 -126
  22. package/docs/sdk/README.md +21 -21
  23. package/docs/sdk/SESSION_EVENTS.md +15 -13
  24. package/docs/sdk/openapi.yaml +16 -1
  25. package/package.json +1 -1
  26. package/packages/client/README.md +29 -29
  27. package/packages/client/index.d.ts +122 -122
  28. package/packages/client/index.js +260 -260
  29. package/packages/client/package.json +2 -2
  30. package/packages/contracts/README.md +9 -9
  31. package/packages/contracts/index.d.ts +519 -517
  32. package/packages/contracts/index.js +312 -312
  33. package/packages/contracts/package.json +1 -1
  34. package/packages/core/README.md +8 -8
  35. package/packages/core/index.d.ts +76 -76
  36. package/packages/core/index.js +222 -222
  37. package/packages/core/package.json +2 -2
  38. package/packages/server/README.md +17 -17
  39. package/packages/server/bin/agent-terminal-server.js +42 -42
  40. package/packages/server/bin/claude-workbench.js +225 -225
  41. package/packages/server/index.d.ts +171 -171
  42. package/packages/server/index.js +230 -230
  43. package/packages/server/lib/agent-discovery.js +133 -133
  44. package/packages/server/lib/agent-registry.js +143 -143
  45. package/packages/server/lib/agent-spawn-env.js +17 -17
  46. package/packages/server/lib/attachment-store.js +78 -78
  47. package/packages/server/lib/bridges/app-server-bridge.js +373 -373
  48. package/packages/server/lib/bridges/opencode-server-bridge.js +506 -506
  49. package/packages/server/lib/capture-settings-store.js +35 -35
  50. package/packages/server/lib/claude-history-recovery.js +87 -87
  51. package/packages/server/lib/cli-session-recovery.js +28 -28
  52. package/packages/server/lib/context-resolver.js +55 -55
  53. package/packages/server/lib/conversation-capture.js +51 -51
  54. package/packages/server/lib/delegation-phase-store.js +357 -357
  55. package/packages/server/lib/delegation-store.js +474 -468
  56. package/packages/server/lib/diagnostics.js +32 -32
  57. package/packages/server/lib/drivers/cli-json-runner.js +974 -974
  58. package/packages/server/lib/drivers/codex-driver.js +1461 -1442
  59. package/packages/server/lib/drivers/opencode-driver.js +1554 -1554
  60. package/packages/server/lib/drivers/zcode-driver.js +1250 -1250
  61. package/packages/server/lib/drivers/zcode-profile-runtime.js +71 -71
  62. package/packages/server/lib/event-stream.js +138 -138
  63. package/packages/server/lib/folder-picker.js +623 -623
  64. package/packages/server/lib/http-handler.js +339 -339
  65. package/packages/server/lib/json-file.js +26 -26
  66. package/packages/server/lib/knowledge-context.js +33 -33
  67. package/packages/server/lib/model-detection.js +295 -295
  68. package/packages/server/lib/parent-context.js +44 -0
  69. package/packages/server/lib/profile-store.js +283 -283
  70. package/packages/server/lib/project-store.js +102 -102
  71. package/packages/server/lib/runtime-loader.js +120 -120
  72. package/packages/server/lib/session-archive-store.js +53 -53
  73. package/packages/server/lib/session-history.js +612 -612
  74. package/packages/server/lib/system-doctor.js +125 -125
  75. package/packages/server/lib/ui-preferences-store.js +48 -48
  76. package/packages/server/lib/user-questions.js +67 -0
  77. package/packages/server/lib/workbench-service.js +1696 -1675
  78. package/packages/server/package.json +2 -2
  79. package/packages/ui/README.md +30 -30
  80. package/packages/ui/ai-profile-settings.d.ts +45 -45
  81. package/packages/ui/ai-profile-settings.js +283 -283
  82. package/packages/ui/claude-terminal.d.ts +31 -31
  83. package/packages/ui/claude-terminal.js +343 -343
  84. package/packages/ui/index.d.ts +18 -18
  85. package/packages/ui/index.js +51 -51
  86. package/packages/ui/package.json +2 -2
  87. package/packages/ui/theme.css +13 -13
  88. package/src/assets/claude-prompts.json +21 -21
  89. package/src/backend/http/ai-profile-routes.js +61 -61
  90. package/src/backend/http/claude-routes.js +170 -170
  91. package/src/backend/http/node-helpers.js +45 -45
  92. package/src/backend/index.js +21 -21
  93. package/src/backend/lib/ai-adapter.js +144 -144
  94. package/src/backend/lib/ai-profile-store.js +259 -259
  95. package/src/backend/lib/ai-vendor-presets.js +83 -83
  96. package/src/backend/lib/ai-workspace.js +140 -140
  97. package/src/backend/lib/automation-config.js +253 -253
  98. package/src/backend/lib/claude-cli-runner.js +2408 -2392
  99. package/src/backend/lib/data-dir.js +169 -169
  100. package/src/backend/lib/llm-client.js +176 -176
  101. package/src/backend/lib/model-context-windows.js +82 -82
  102. package/src/backend/lib/native-task-control.js +217 -217
  103. package/src/backend/lib/native-task-history-index.js +475 -475
  104. package/src/backend/lib/native-task-history-reader.js +526 -526
  105. package/src/backend/lib/native-task-messages.js +457 -457
  106. package/src/backend/lib/native-task-registry.js +325 -325
  107. package/src/backend/lib/native-task-runtime.js +242 -242
  108. package/src/backend/lib/native-task-trace.js +440 -440
  109. package/src/backend/lib/native-task-transcript.js +393 -393
  110. package/src/backend/lib/prompt-registry.js +166 -166
  111. package/src/backend/lib/runtime-config.js +121 -121
  112. package/src/backend/lib/sdk-session-stream.js +143 -143
  113. package/src/backend/lib/sdk-turn-stream.js +16 -16
  114. package/src/backend/lib/store-scope.js +131 -131
  115. package/src/backend/lib/task-identity.js +104 -104
  116. package/src/backend/lib/task-state-projection.js +58 -58
  117. package/src/index.js +7 -7
  118. package/src/sdk-wave-a.ts +1 -1
  119. package/src/sdk-wave-b.ts +1 -1
  120. package/src/sdk-wave-c.ts +1 -1
  121. package/src/sdk-wave-d.ts +1 -1
  122. package/src/sdk-wave-e.ts +1 -1
@@ -1,166 +1,166 @@
1
- // Prompt Registry — reads/writes claude-prompts.json and renders templated prompts.
2
- //
3
- // File location: <KB_ROOT>/claude-prompts.json
4
- // Schema: { schema: 'claude-prompts/v1', prompts: { <key>: { description, model, permissionMode,
5
- // allowedTools, systemPrompt, userPrompt } } }
6
- //
7
- // Variable interpolation: {{KEY}} placeholders in systemPrompt/userPrompt are replaced
8
- // with values from the `vars` argument. Unknown placeholders are left untouched (so
9
- // incomplete prompts don't crash — they show the raw token for diagnosis).
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- const { getDataDir } = require('./data-dir');
15
- const { getWorkbenchConfig } = require('./runtime-config');
16
- const SCHEMA = 'claude-prompts/v1';
17
-
18
- const PLACEHOLDER_RE = /\{\{\s*([A-Z_][A-Z0-9_]*)\s*\}\}/g;
19
-
20
- function normalizePromptsConfig(cfg) {
21
- const normalized = cfg && typeof cfg === 'object' ? cfg : {};
22
- if (!normalized.prompts || typeof normalized.prompts !== 'object') normalized.prompts = {};
23
- if (!normalized.schema) normalized.schema = SCHEMA;
24
- return normalized;
25
- }
26
-
27
- function promptsPath() {
28
- const cfg = getWorkbenchConfig();
29
- return path.join(getDataDir(), cfg.promptsFileName || 'claude-prompts.json');
30
- }
31
-
32
- function bundledPromptsPath() {
33
- const cfg = getWorkbenchConfig();
34
- return cfg.bundledPromptsPath || path.resolve(__dirname, '..', '..', 'assets', 'claude-prompts.json');
35
- }
36
-
37
- function readBundledPrompts() {
38
- const file = bundledPromptsPath();
39
- if (!fs.existsSync(file)) return { schema: SCHEMA, prompts: {} };
40
- try {
41
- return normalizePromptsConfig(JSON.parse(fs.readFileSync(file, 'utf-8')));
42
- } catch {
43
- return { schema: SCHEMA, prompts: {} };
44
- }
45
- }
46
-
47
- function persistPrompts(cfg) {
48
- const file = promptsPath();
49
- fs.mkdirSync(path.dirname(file), { recursive: true });
50
- fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
51
- }
52
-
53
- function withBundledDefaults(cfg) {
54
- const current = normalizePromptsConfig(cfg);
55
- const bundled = readBundledPrompts();
56
- let changed = false;
57
- const prompts = { ...current.prompts };
58
- for (const [key, prompt] of Object.entries(bundled.prompts || {})) {
59
- if (!Object.prototype.hasOwnProperty.call(prompts, key)) {
60
- prompts[key] = prompt;
61
- changed = true;
62
- }
63
- }
64
- return {
65
- cfg: {
66
- schema: current.schema || bundled.schema || SCHEMA,
67
- prompts,
68
- },
69
- changed,
70
- };
71
- }
72
-
73
- function readPrompts() {
74
- const file = promptsPath();
75
- if (!fs.existsSync(file)) {
76
- const seeded = withBundledDefaults({ schema: SCHEMA, prompts: {} }).cfg;
77
- if (Object.keys(seeded.prompts).length) {
78
- persistPrompts(seeded);
79
- }
80
- return seeded;
81
- }
82
- try {
83
- const result = withBundledDefaults(JSON.parse(fs.readFileSync(file, 'utf-8')));
84
- if (result.changed) persistPrompts(result.cfg);
85
- return result.cfg;
86
- } catch (e) {
87
- throw new Error(`failed to parse ${file}: ${e.message}`);
88
- }
89
- }
90
-
91
- function writePrompts(cfg) {
92
- if (!cfg || typeof cfg !== 'object') throw new Error('prompts config must be an object');
93
- if (cfg.prompts && typeof cfg.prompts !== 'object') throw new Error('prompts field must be an object');
94
- const normalized = {
95
- schema: cfg.schema || SCHEMA,
96
- prompts: cfg.prompts || {},
97
- };
98
- for (const [key, val] of Object.entries(normalized.prompts)) {
99
- if (!key || typeof key !== 'string') throw new Error(`invalid prompt key: ${key}`);
100
- if (!val || typeof val !== 'object') throw new Error(`prompt "${key}" must be an object`);
101
- if (typeof val.userPrompt !== 'string' || !val.userPrompt) {
102
- throw new Error(`prompt "${key}" must have a non-empty userPrompt`);
103
- }
104
- }
105
- persistPrompts(normalized);
106
- return normalized;
107
- }
108
-
109
- function listPromptKeys() {
110
- return Object.keys(readPrompts().prompts);
111
- }
112
-
113
- function getPrompt(key) {
114
- const cfg = readPrompts();
115
- const p = cfg.prompts[key];
116
- if (!p) return null;
117
- return p;
118
- }
119
-
120
- function interpolate(text, vars) {
121
- if (typeof text !== 'string') return text;
122
- if (!vars || typeof vars !== 'object') return text;
123
- return text.replace(PLACEHOLDER_RE, (_, name) => {
124
- if (Object.prototype.hasOwnProperty.call(vars, name)) {
125
- const v = vars[name];
126
- return v == null ? '' : String(v);
127
- }
128
- return `{{${name}}}`;
129
- });
130
- }
131
-
132
- // Render the prompt for a given key, returning all fields needed to spawn claude.
133
- // Returns null if key doesn't exist.
134
- function renderPrompt(key, vars) {
135
- const p = getPrompt(key);
136
- if (!p) return null;
137
- const safeVars = vars || {};
138
- return {
139
- key,
140
- description: p.description || '',
141
- model: p.model || 'sonnet',
142
- permissionMode: p.permissionMode || 'bypassPermissions',
143
- allowedTools: Array.isArray(p.allowedTools) ? p.allowedTools.slice() : ['Read', 'Grep', 'Glob'],
144
- systemPrompt: interpolate(p.systemPrompt || '', safeVars),
145
- userPrompt: interpolate(p.userPrompt, safeVars),
146
- };
147
- }
148
-
149
- const exported = {
150
- promptsPath,
151
- bundledPromptsPath,
152
- SCHEMA,
153
- readPrompts,
154
- writePrompts,
155
- listPromptKeys,
156
- getPrompt,
157
- renderPrompt,
158
- interpolate,
159
- };
160
-
161
- Object.defineProperties(exported, {
162
- PROMPTS_PATH: { enumerable: true, get: promptsPath },
163
- BUNDLED_PROMPTS_PATH: { enumerable: true, get: bundledPromptsPath },
164
- });
165
-
166
- module.exports = exported;
1
+ // Prompt Registry — reads/writes claude-prompts.json and renders templated prompts.
2
+ //
3
+ // File location: <KB_ROOT>/claude-prompts.json
4
+ // Schema: { schema: 'claude-prompts/v1', prompts: { <key>: { description, model, permissionMode,
5
+ // allowedTools, systemPrompt, userPrompt } } }
6
+ //
7
+ // Variable interpolation: {{KEY}} placeholders in systemPrompt/userPrompt are replaced
8
+ // with values from the `vars` argument. Unknown placeholders are left untouched (so
9
+ // incomplete prompts don't crash — they show the raw token for diagnosis).
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ const { getDataDir } = require('./data-dir');
15
+ const { getWorkbenchConfig } = require('./runtime-config');
16
+ const SCHEMA = 'claude-prompts/v1';
17
+
18
+ const PLACEHOLDER_RE = /\{\{\s*([A-Z_][A-Z0-9_]*)\s*\}\}/g;
19
+
20
+ function normalizePromptsConfig(cfg) {
21
+ const normalized = cfg && typeof cfg === 'object' ? cfg : {};
22
+ if (!normalized.prompts || typeof normalized.prompts !== 'object') normalized.prompts = {};
23
+ if (!normalized.schema) normalized.schema = SCHEMA;
24
+ return normalized;
25
+ }
26
+
27
+ function promptsPath() {
28
+ const cfg = getWorkbenchConfig();
29
+ return path.join(getDataDir(), cfg.promptsFileName || 'claude-prompts.json');
30
+ }
31
+
32
+ function bundledPromptsPath() {
33
+ const cfg = getWorkbenchConfig();
34
+ return cfg.bundledPromptsPath || path.resolve(__dirname, '..', '..', 'assets', 'claude-prompts.json');
35
+ }
36
+
37
+ function readBundledPrompts() {
38
+ const file = bundledPromptsPath();
39
+ if (!fs.existsSync(file)) return { schema: SCHEMA, prompts: {} };
40
+ try {
41
+ return normalizePromptsConfig(JSON.parse(fs.readFileSync(file, 'utf-8')));
42
+ } catch {
43
+ return { schema: SCHEMA, prompts: {} };
44
+ }
45
+ }
46
+
47
+ function persistPrompts(cfg) {
48
+ const file = promptsPath();
49
+ fs.mkdirSync(path.dirname(file), { recursive: true });
50
+ fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
51
+ }
52
+
53
+ function withBundledDefaults(cfg) {
54
+ const current = normalizePromptsConfig(cfg);
55
+ const bundled = readBundledPrompts();
56
+ let changed = false;
57
+ const prompts = { ...current.prompts };
58
+ for (const [key, prompt] of Object.entries(bundled.prompts || {})) {
59
+ if (!Object.prototype.hasOwnProperty.call(prompts, key)) {
60
+ prompts[key] = prompt;
61
+ changed = true;
62
+ }
63
+ }
64
+ return {
65
+ cfg: {
66
+ schema: current.schema || bundled.schema || SCHEMA,
67
+ prompts,
68
+ },
69
+ changed,
70
+ };
71
+ }
72
+
73
+ function readPrompts() {
74
+ const file = promptsPath();
75
+ if (!fs.existsSync(file)) {
76
+ const seeded = withBundledDefaults({ schema: SCHEMA, prompts: {} }).cfg;
77
+ if (Object.keys(seeded.prompts).length) {
78
+ persistPrompts(seeded);
79
+ }
80
+ return seeded;
81
+ }
82
+ try {
83
+ const result = withBundledDefaults(JSON.parse(fs.readFileSync(file, 'utf-8')));
84
+ if (result.changed) persistPrompts(result.cfg);
85
+ return result.cfg;
86
+ } catch (e) {
87
+ throw new Error(`failed to parse ${file}: ${e.message}`);
88
+ }
89
+ }
90
+
91
+ function writePrompts(cfg) {
92
+ if (!cfg || typeof cfg !== 'object') throw new Error('prompts config must be an object');
93
+ if (cfg.prompts && typeof cfg.prompts !== 'object') throw new Error('prompts field must be an object');
94
+ const normalized = {
95
+ schema: cfg.schema || SCHEMA,
96
+ prompts: cfg.prompts || {},
97
+ };
98
+ for (const [key, val] of Object.entries(normalized.prompts)) {
99
+ if (!key || typeof key !== 'string') throw new Error(`invalid prompt key: ${key}`);
100
+ if (!val || typeof val !== 'object') throw new Error(`prompt "${key}" must be an object`);
101
+ if (typeof val.userPrompt !== 'string' || !val.userPrompt) {
102
+ throw new Error(`prompt "${key}" must have a non-empty userPrompt`);
103
+ }
104
+ }
105
+ persistPrompts(normalized);
106
+ return normalized;
107
+ }
108
+
109
+ function listPromptKeys() {
110
+ return Object.keys(readPrompts().prompts);
111
+ }
112
+
113
+ function getPrompt(key) {
114
+ const cfg = readPrompts();
115
+ const p = cfg.prompts[key];
116
+ if (!p) return null;
117
+ return p;
118
+ }
119
+
120
+ function interpolate(text, vars) {
121
+ if (typeof text !== 'string') return text;
122
+ if (!vars || typeof vars !== 'object') return text;
123
+ return text.replace(PLACEHOLDER_RE, (_, name) => {
124
+ if (Object.prototype.hasOwnProperty.call(vars, name)) {
125
+ const v = vars[name];
126
+ return v == null ? '' : String(v);
127
+ }
128
+ return `{{${name}}}`;
129
+ });
130
+ }
131
+
132
+ // Render the prompt for a given key, returning all fields needed to spawn claude.
133
+ // Returns null if key doesn't exist.
134
+ function renderPrompt(key, vars) {
135
+ const p = getPrompt(key);
136
+ if (!p) return null;
137
+ const safeVars = vars || {};
138
+ return {
139
+ key,
140
+ description: p.description || '',
141
+ model: p.model || 'sonnet',
142
+ permissionMode: p.permissionMode || 'bypassPermissions',
143
+ allowedTools: Array.isArray(p.allowedTools) ? p.allowedTools.slice() : ['Read', 'Grep', 'Glob'],
144
+ systemPrompt: interpolate(p.systemPrompt || '', safeVars),
145
+ userPrompt: interpolate(p.userPrompt, safeVars),
146
+ };
147
+ }
148
+
149
+ const exported = {
150
+ promptsPath,
151
+ bundledPromptsPath,
152
+ SCHEMA,
153
+ readPrompts,
154
+ writePrompts,
155
+ listPromptKeys,
156
+ getPrompt,
157
+ renderPrompt,
158
+ interpolate,
159
+ };
160
+
161
+ Object.defineProperties(exported, {
162
+ PROMPTS_PATH: { enumerable: true, get: promptsPath },
163
+ BUNDLED_PROMPTS_PATH: { enumerable: true, get: bundledPromptsPath },
164
+ });
165
+
166
+ module.exports = exported;
@@ -1,121 +1,121 @@
1
- const path = require('path');
2
- const os = require('os');
3
-
4
- const PRESETS = {
5
- workbench: {
6
- appName: 'claude-ai-workbench',
7
- defaultDataDirName: '.claude-ai-workbench',
8
- dataDirEnvVars: ['CLAUDE_AI_WORKBENCH_DATA_DIR', 'KB_DATA_DIR', 'DEV_TASK_RADAR_CLAUDE_DATA_DIR'],
9
- sessionDirName: 'claude-workbench',
10
- sessionSchema: 'claude-workbench-session/v1',
11
- legacySessionDirNames: ['claude-terminal'],
12
- legacySessionSchemas: ['devtask-radar-claude-session/v1'],
13
- suppressHooksEnv: '',
14
- fakeClaudeEnvVar: 'KB_AUTOMATION_FAKE_CLAUDE',
15
- staleActiveEnvVar: 'KB_STALE_ACTIVE_MS',
16
- },
17
- projectKnowledge: {
18
- appName: 'project-knowledge',
19
- defaultDataDirName: '.project-knowledge',
20
- dataDirEnvVars: ['KB_DATA_DIR'],
21
- sessionDirName: 'claude-workbench',
22
- sessionSchema: 'claude-workbench-session/v1',
23
- legacySessionDirNames: [],
24
- legacySessionSchemas: [],
25
- suppressHooksEnv: '',
26
- fakeClaudeEnvVar: 'KB_AUTOMATION_FAKE_CLAUDE',
27
- staleActiveEnvVar: 'KB_STALE_ACTIVE_MS',
28
- },
29
- devTaskRadar: {
30
- appName: 'devtask-radar',
31
- defaultDataDirName: '.devtask-radar',
32
- dataDirEnvVars: ['DEV_TASK_RADAR_CLAUDE_DATA_DIR', 'DEV_TASK_RADAR_ANALYSIS_DATA_DIR', 'KB_DATA_DIR'],
33
- sessionDirName: 'claude-terminal',
34
- sessionSchema: 'devtask-radar-claude-session/v1',
35
- legacySessionDirNames: ['claude-workbench'],
36
- legacySessionSchemas: ['claude-workbench-session/v1'],
37
- suppressHooksEnv: 'DEV_TASK_RADAR_SUPPRESS_HOOKS',
38
- fakeClaudeEnvVar: 'KB_AUTOMATION_FAKE_CLAUDE',
39
- staleActiveEnvVar: 'KB_STALE_ACTIVE_MS',
40
- },
41
- };
42
-
43
- const DEFAULTS = {
44
- ...PRESETS.workbench,
45
- aiRootDirName: '_ai',
46
- projectsFileName: 'projects.json',
47
- promptsFileName: 'claude-prompts.json',
48
- bundledPromptsPath: path.resolve(__dirname, '..', '..', 'assets', 'claude-prompts.json'),
49
- standardSubdirs: ['drafts', 'runs', 'context-packs', 'backups'],
50
- suppressHooksValue: '1',
51
- };
52
-
53
- let runtimeConfig = { ...DEFAULTS };
54
-
55
- function normalizeArray(value) {
56
- if (!value) return [];
57
- return Array.isArray(value) ? value.filter(Boolean).map(String) : [String(value)];
58
- }
59
-
60
- function mergeConfig(base, override = {}) {
61
- const next = { ...base, ...(override || {}) };
62
- next.dataDirEnvVars = normalizeArray(next.dataDirEnvVars);
63
- next.legacySessionDirNames = normalizeArray(next.legacySessionDirNames);
64
- next.legacySessionSchemas = normalizeArray(next.legacySessionSchemas);
65
- next.standardSubdirs = normalizeArray(next.standardSubdirs);
66
- return next;
67
- }
68
-
69
- function configureWorkbench(options = {}) {
70
- runtimeConfig = mergeConfig(runtimeConfig, options);
71
- return getWorkbenchConfig();
72
- }
73
-
74
- function configurePreset(name, options = {}) {
75
- const preset = PRESETS[name];
76
- if (!preset) throw new Error(`unknown Claude AI workbench preset: ${name}`);
77
- runtimeConfig = mergeConfig({ ...DEFAULTS, ...preset }, options);
78
- return getWorkbenchConfig();
79
- }
80
-
81
- function getWorkbenchConfig() {
82
- return mergeConfig({}, runtimeConfig);
83
- }
84
-
85
- function firstEnv(names) {
86
- for (const name of normalizeArray(names)) {
87
- const value = process.env[name];
88
- if (value) return value;
89
- }
90
- return '';
91
- }
92
-
93
- function resolveDataDir() {
94
- const cfg = getWorkbenchConfig();
95
- const explicit = cfg.dataDir || firstEnv(cfg.dataDirEnvVars);
96
- return explicit
97
- ? path.resolve(explicit)
98
- : path.join(os.homedir(), cfg.defaultDataDirName || '.claude-ai-workbench');
99
- }
100
-
101
- function sessionDirNames() {
102
- const cfg = getWorkbenchConfig();
103
- return [cfg.sessionDirName || 'claude-workbench', ...normalizeArray(cfg.legacySessionDirNames)]
104
- .filter((value, index, arr) => value && arr.indexOf(value) === index);
105
- }
106
-
107
- function sessionSchemas() {
108
- const cfg = getWorkbenchConfig();
109
- return [cfg.sessionSchema || 'claude-workbench-session/v1', ...normalizeArray(cfg.legacySessionSchemas)]
110
- .filter((value, index, arr) => value && arr.indexOf(value) === index);
111
- }
112
-
113
- module.exports = {
114
- PRESETS,
115
- configureWorkbench,
116
- configurePreset,
117
- getWorkbenchConfig,
118
- resolveDataDir,
119
- sessionDirNames,
120
- sessionSchemas,
121
- };
1
+ const path = require('path');
2
+ const os = require('os');
3
+
4
+ const PRESETS = {
5
+ workbench: {
6
+ appName: 'claude-ai-workbench',
7
+ defaultDataDirName: '.claude-ai-workbench',
8
+ dataDirEnvVars: ['CLAUDE_AI_WORKBENCH_DATA_DIR', 'KB_DATA_DIR', 'DEV_TASK_RADAR_CLAUDE_DATA_DIR'],
9
+ sessionDirName: 'claude-workbench',
10
+ sessionSchema: 'claude-workbench-session/v1',
11
+ legacySessionDirNames: ['claude-terminal'],
12
+ legacySessionSchemas: ['devtask-radar-claude-session/v1'],
13
+ suppressHooksEnv: '',
14
+ fakeClaudeEnvVar: 'KB_AUTOMATION_FAKE_CLAUDE',
15
+ staleActiveEnvVar: 'KB_STALE_ACTIVE_MS',
16
+ },
17
+ projectKnowledge: {
18
+ appName: 'project-knowledge',
19
+ defaultDataDirName: '.project-knowledge',
20
+ dataDirEnvVars: ['KB_DATA_DIR'],
21
+ sessionDirName: 'claude-workbench',
22
+ sessionSchema: 'claude-workbench-session/v1',
23
+ legacySessionDirNames: [],
24
+ legacySessionSchemas: [],
25
+ suppressHooksEnv: '',
26
+ fakeClaudeEnvVar: 'KB_AUTOMATION_FAKE_CLAUDE',
27
+ staleActiveEnvVar: 'KB_STALE_ACTIVE_MS',
28
+ },
29
+ devTaskRadar: {
30
+ appName: 'devtask-radar',
31
+ defaultDataDirName: '.devtask-radar',
32
+ dataDirEnvVars: ['DEV_TASK_RADAR_CLAUDE_DATA_DIR', 'DEV_TASK_RADAR_ANALYSIS_DATA_DIR', 'KB_DATA_DIR'],
33
+ sessionDirName: 'claude-terminal',
34
+ sessionSchema: 'devtask-radar-claude-session/v1',
35
+ legacySessionDirNames: ['claude-workbench'],
36
+ legacySessionSchemas: ['claude-workbench-session/v1'],
37
+ suppressHooksEnv: 'DEV_TASK_RADAR_SUPPRESS_HOOKS',
38
+ fakeClaudeEnvVar: 'KB_AUTOMATION_FAKE_CLAUDE',
39
+ staleActiveEnvVar: 'KB_STALE_ACTIVE_MS',
40
+ },
41
+ };
42
+
43
+ const DEFAULTS = {
44
+ ...PRESETS.workbench,
45
+ aiRootDirName: '_ai',
46
+ projectsFileName: 'projects.json',
47
+ promptsFileName: 'claude-prompts.json',
48
+ bundledPromptsPath: path.resolve(__dirname, '..', '..', 'assets', 'claude-prompts.json'),
49
+ standardSubdirs: ['drafts', 'runs', 'context-packs', 'backups'],
50
+ suppressHooksValue: '1',
51
+ };
52
+
53
+ let runtimeConfig = { ...DEFAULTS };
54
+
55
+ function normalizeArray(value) {
56
+ if (!value) return [];
57
+ return Array.isArray(value) ? value.filter(Boolean).map(String) : [String(value)];
58
+ }
59
+
60
+ function mergeConfig(base, override = {}) {
61
+ const next = { ...base, ...(override || {}) };
62
+ next.dataDirEnvVars = normalizeArray(next.dataDirEnvVars);
63
+ next.legacySessionDirNames = normalizeArray(next.legacySessionDirNames);
64
+ next.legacySessionSchemas = normalizeArray(next.legacySessionSchemas);
65
+ next.standardSubdirs = normalizeArray(next.standardSubdirs);
66
+ return next;
67
+ }
68
+
69
+ function configureWorkbench(options = {}) {
70
+ runtimeConfig = mergeConfig(runtimeConfig, options);
71
+ return getWorkbenchConfig();
72
+ }
73
+
74
+ function configurePreset(name, options = {}) {
75
+ const preset = PRESETS[name];
76
+ if (!preset) throw new Error(`unknown Claude AI workbench preset: ${name}`);
77
+ runtimeConfig = mergeConfig({ ...DEFAULTS, ...preset }, options);
78
+ return getWorkbenchConfig();
79
+ }
80
+
81
+ function getWorkbenchConfig() {
82
+ return mergeConfig({}, runtimeConfig);
83
+ }
84
+
85
+ function firstEnv(names) {
86
+ for (const name of normalizeArray(names)) {
87
+ const value = process.env[name];
88
+ if (value) return value;
89
+ }
90
+ return '';
91
+ }
92
+
93
+ function resolveDataDir() {
94
+ const cfg = getWorkbenchConfig();
95
+ const explicit = cfg.dataDir || firstEnv(cfg.dataDirEnvVars);
96
+ return explicit
97
+ ? path.resolve(explicit)
98
+ : path.join(os.homedir(), cfg.defaultDataDirName || '.claude-ai-workbench');
99
+ }
100
+
101
+ function sessionDirNames() {
102
+ const cfg = getWorkbenchConfig();
103
+ return [cfg.sessionDirName || 'claude-workbench', ...normalizeArray(cfg.legacySessionDirNames)]
104
+ .filter((value, index, arr) => value && arr.indexOf(value) === index);
105
+ }
106
+
107
+ function sessionSchemas() {
108
+ const cfg = getWorkbenchConfig();
109
+ return [cfg.sessionSchema || 'claude-workbench-session/v1', ...normalizeArray(cfg.legacySessionSchemas)]
110
+ .filter((value, index, arr) => value && arr.indexOf(value) === index);
111
+ }
112
+
113
+ module.exports = {
114
+ PRESETS,
115
+ configureWorkbench,
116
+ configurePreset,
117
+ getWorkbenchConfig,
118
+ resolveDataDir,
119
+ sessionDirNames,
120
+ sessionSchemas,
121
+ };