chenbhao Claude Big Pickle commited on
Commit
642c567
·
1 Parent(s): 5994b8c

feat: unify build output paths and extract shared feature flags

Browse files

- Always build to dist/VersperClaw with symlink at project root
- Always enable all experimental features (dev mode, no release builds)
- Extract feature flag list to scripts/features.ts, shared by build & dev
- Create scripts/dev.ts to run source with --feature and --define flags
- Copy vendor/ to dist/vendor/ at build time for runtime resolution
- Remove redundant build:dev, build:dev:full, compile scripts

Co-Authored-By: Claude Big Pickle <noreply@anthropic.com>

Files changed (6) hide show
  1. .gitignore +0 -1
  2. README.md +1 -1
  3. package.json +1 -4
  4. scripts/build.ts +28 -107
  5. scripts/dev.ts +38 -0
  6. scripts/features.ts +44 -0
.gitignore CHANGED
@@ -1,7 +1,6 @@
1
  node_modules/
2
  packages/
3
  dist/
4
- cli
5
  .codex
6
  VersperClaw
7
 
 
1
  node_modules/
2
  packages/
3
  dist/
 
4
  .codex
5
  VersperClaw
6
 
README.md CHANGED
@@ -67,7 +67,7 @@
67
  curl -fsSL https://raw.githubusercontent.com/versperai/VersperClaw/main/install.sh | bash
68
 
69
  # source install
70
- git clone https://github.com/versperai/VersperClaw.git && cd VersperClaw && bun install && bun run build:dev:full && bun run friend:build && ./VersperClaw
71
 
72
  # if want global use bin file
73
  cp VersperClaw ~/.local/bin
 
67
  curl -fsSL https://raw.githubusercontent.com/versperai/VersperClaw/main/install.sh | bash
68
 
69
  # source install
70
+ git clone https://github.com/versperai/VersperClaw.git && cd VersperClaw && bun install && bun run build && bun run friend:build && ./VersperClaw
71
 
72
  # if want global use bin file
73
  cp VersperClaw ~/.local/bin
package.json CHANGED
@@ -17,10 +17,7 @@
17
  },
18
  "scripts": {
19
  "build": "bun run ./scripts/build.ts",
20
- "build:dev": "bun run ./scripts/build.ts --dev",
21
- "build:dev:full": "bun run ./scripts/build.ts --dev --feature-set=dev-full",
22
- "compile": "bun run ./scripts/build.ts --compile",
23
- "dev": "bun run ./src/entrypoints/cli.tsx",
24
  "friend:build": "cd src/components/friend/frontend && npx tauri build"
25
  },
26
  "dependencies": {
 
17
  },
18
  "scripts": {
19
  "build": "bun run ./scripts/build.ts",
20
+ "dev": "bun run ./scripts/dev.ts",
 
 
 
21
  "friend:build": "cd src/components/friend/frontend && npx tauri build"
22
  },
23
  "dependencies": {
scripts/build.ts CHANGED
@@ -1,55 +1,14 @@
1
- import { chmodSync, cpSync, existsSync, mkdirSync } from 'fs'
2
- import { dirname, join } from 'path'
3
 
4
  const pkg = await Bun.file(new URL('../package.json', import.meta.url)).json() as {
5
  name: string
6
  version: string
7
  }
8
 
 
 
9
  const args = process.argv.slice(2)
10
- const compile = args.includes('--compile')
11
- const dev = args.includes('--dev')
12
-
13
- const fullExperimentalFeatures = [
14
- 'AGENT_MEMORY_SNAPSHOT',
15
- 'AGENT_TRIGGERS',
16
- 'AGENT_TRIGGERS_REMOTE',
17
- 'AWAY_SUMMARY',
18
- 'BASH_CLASSIFIER',
19
- 'BUDDY',
20
- 'BRIDGE_MODE',
21
- 'BUILTIN_EXPLORE_PLAN_AGENTS',
22
- 'CACHED_MICROCOMPACT',
23
- 'CCR_AUTO_CONNECT',
24
- 'CCR_MIRROR',
25
- 'CCR_REMOTE_SETUP',
26
- 'COMPACTION_REMINDERS',
27
- 'CONNECTOR_TEXT',
28
- 'EXTRACT_MEMORIES',
29
- 'HISTORY_PICKER',
30
- 'HOOK_PROMPTS',
31
- 'KAIROS_BRIEF',
32
- 'KAIROS_CHANNELS',
33
- 'LODESTONE',
34
- 'MCP_RICH_OUTPUT',
35
- 'MESSAGE_ACTIONS',
36
- 'NATIVE_CLIPBOARD_IMAGE',
37
- 'NEW_INIT',
38
- 'POWERSHELL_AUTO_MODE',
39
- 'PROMPT_CACHE_BREAK_DETECTION',
40
- 'QUICK_SEARCH',
41
- 'SHOT_STATS',
42
- 'TEAMMEM',
43
- 'TOKEN_BUDGET',
44
- 'TREE_SITTER_BASH',
45
- 'TREE_SITTER_BASH_SHADOW',
46
- 'TRANSCRIPT_CLASSIFIER',
47
- 'ULTRAPLAN',
48
- 'ULTRATHINK',
49
- 'UNATTENDED_RETRY',
50
- 'VERIFICATION_AGENT',
51
- 'VOICE_MODE',
52
- ] as const
53
 
54
  function runCommand(cmd: string[]): string | null {
55
  const proc = Bun.spawnSync({
@@ -58,11 +17,7 @@ function runCommand(cmd: string[]): string | null {
58
  stdout: 'pipe',
59
  stderr: 'pipe',
60
  })
61
-
62
- if (proc.exitCode !== 0) {
63
- return null
64
- }
65
-
66
  return new TextDecoder().decode(proc.stdout).trim() || null
67
  }
68
 
@@ -81,45 +36,22 @@ function getVersionChangelog(): string {
81
  )
82
  }
83
 
84
- const defaultFeatures = ['VOICE_MODE']
85
- const featureSet = new Set(defaultFeatures)
86
  for (let i = 0; i < args.length; i += 1) {
87
  const arg = args[i]
88
- if (arg === '--feature-set' && args[i + 1]) {
89
- if (args[i + 1] === 'dev-full') {
90
- for (const feature of fullExperimentalFeatures) {
91
- featureSet.add(feature)
92
- }
93
- }
94
- i += 1
95
- continue
96
- }
97
- if (arg === '--feature-set=dev-full') {
98
- for (const feature of fullExperimentalFeatures) {
99
- featureSet.add(feature)
100
- }
101
- continue
102
- }
103
  if (arg === '--feature' && args[i + 1]) {
104
  featureSet.add(args[i + 1]!)
105
  i += 1
106
- continue
107
- }
108
- if (arg.startsWith('--feature=')) {
109
  featureSet.add(arg.slice('--feature='.length))
110
  }
111
  }
112
  const features = [...featureSet]
113
 
114
- const outfile = compile
115
- ? dev
116
- ? './dist/VersperClaw'
117
- : './dist/cli'
118
- : dev
119
- ? './VersperClaw'
120
- : './cli'
121
 
122
- // ── Pre-step: build Friend VRM frontend ───────────────────────────────────────
123
  function buildFriendFrontend(): boolean {
124
  const frontendDir = join(process.cwd(), 'src', 'components', 'friend', 'frontend')
125
  const distIndex = join(frontendDir, 'dist', 'index.html')
@@ -146,18 +78,13 @@ if (!buildFriendFrontend()) {
146
  process.exit(1)
147
  }
148
 
149
- // ─────────────────────────────────────────────────────────────────────────────
150
 
151
  const buildTime = new Date().toISOString()
152
- const version = dev ? getDevVersion(pkg.version) : pkg.version
153
 
154
  mkdirSync(dirname(outfile), { recursive: true })
155
 
156
- // Merge defaultFeatures into features for consistent behavior
157
- for (const feature of defaultFeatures) {
158
- featureSet.add(feature)
159
- }
160
-
161
  const externals = [
162
  '@ant/*',
163
  'audio-capture-napi',
@@ -169,14 +96,8 @@ const externals = [
169
  const defines = {
170
  'process.env.USER_TYPE': JSON.stringify('external'),
171
  'process.env.CLAUDE_CODE_FORCE_FULL_LOGO': JSON.stringify('true'),
172
- ...(dev
173
- ? { 'process.env.NODE_ENV': JSON.stringify('development') }
174
- : {}),
175
- ...(dev
176
- ? {
177
- 'process.env.CLAUDE_CODE_EXPERIMENTAL_BUILD': JSON.stringify('true'),
178
- }
179
- : {}),
180
  'process.env.CLAUDE_CODE_VERIFY_PLAN': JSON.stringify('false'),
181
  'process.env.CCR_FORCE_BUNDLE': JSON.stringify('true'),
182
  'MACRO.VERSION': JSON.stringify(version),
@@ -187,9 +108,7 @@ const defines = {
187
  'MACRO.ISSUES_EXPLAINER': JSON.stringify(
188
  'This reconstructed source snapshot does not include Anthropic internal issue routing.',
189
  ),
190
- 'MACRO.VERSION_CHANGELOG': JSON.stringify(
191
- dev ? getVersionChangelog() : 'https://github.com/paoloanzn/claude-code',
192
- ),
193
  } as const
194
 
195
  const cmd = [
@@ -214,11 +133,9 @@ const cmd = [
214
  for (const external of externals) {
215
  cmd.push('--external', external)
216
  }
217
-
218
  for (const feature of features) {
219
  cmd.push(`--feature=${feature}`)
220
  }
221
-
222
  for (const [key, value] of Object.entries(defines)) {
223
  cmd.push('--define', `${key}=${value}`)
224
  }
@@ -238,15 +155,19 @@ if (existsSync(outfile)) {
238
  chmodSync(outfile, 0o755)
239
  }
240
 
241
- // Copy vendor audio-capture binaries to dist/ for runtime resolution
242
- if (!compile) {
243
- const distDir = dirname(outfile)
244
- const vendorDir = join(distDir, 'vendor')
245
- if (!existsSync(vendorDir)) {
246
- cpSync('vendor', vendorDir, { recursive: true })
247
- console.log(`Copied vendor/ → ${vendorDir}/`)
248
- }
249
  }
250
 
251
- console.log(`Built ${outfile}`)
 
 
 
 
252
 
 
 
 
1
+ import { chmodSync, cpSync, existsSync, mkdirSync, symlinkSync, unlinkSync } from 'fs'
2
+ import { dirname, join, relative } from 'path'
3
 
4
  const pkg = await Bun.file(new URL('../package.json', import.meta.url)).json() as {
5
  name: string
6
  version: string
7
  }
8
 
9
+ import { FULL_EXPERIMENTAL_FEATURES } from './features.ts'
10
+
11
  const args = process.argv.slice(2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  function runCommand(cmd: string[]): string | null {
14
  const proc = Bun.spawnSync({
 
17
  stdout: 'pipe',
18
  stderr: 'pipe',
19
  })
20
+ if (proc.exitCode !== 0) return null
 
 
 
 
21
  return new TextDecoder().decode(proc.stdout).trim() || null
22
  }
23
 
 
36
  )
37
  }
38
 
39
+ // Collect feature flags (always all + any extras from args)
40
+ const featureSet = new Set<string>(FULL_EXPERIMENTAL_FEATURES)
41
  for (let i = 0; i < args.length; i += 1) {
42
  const arg = args[i]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  if (arg === '--feature' && args[i + 1]) {
44
  featureSet.add(args[i + 1]!)
45
  i += 1
46
+ } else if (arg.startsWith('--feature=')) {
 
 
47
  featureSet.add(arg.slice('--feature='.length))
48
  }
49
  }
50
  const features = [...featureSet]
51
 
52
+ const outfile = join('dist', 'VersperClaw')
 
 
 
 
 
 
53
 
54
+ // ── Pre-step: build Friend VRM frontend ──────────────────────────────────
55
  function buildFriendFrontend(): boolean {
56
  const frontendDir = join(process.cwd(), 'src', 'components', 'friend', 'frontend')
57
  const distIndex = join(frontendDir, 'dist', 'index.html')
 
78
  process.exit(1)
79
  }
80
 
81
+ // ──────────────────────────────────────────────────────────────────────────
82
 
83
  const buildTime = new Date().toISOString()
84
+ const version = getDevVersion(pkg.version)
85
 
86
  mkdirSync(dirname(outfile), { recursive: true })
87
 
 
 
 
 
 
88
  const externals = [
89
  '@ant/*',
90
  'audio-capture-napi',
 
96
  const defines = {
97
  'process.env.USER_TYPE': JSON.stringify('external'),
98
  'process.env.CLAUDE_CODE_FORCE_FULL_LOGO': JSON.stringify('true'),
99
+ 'process.env.NODE_ENV': JSON.stringify('development'),
100
+ 'process.env.CLAUDE_CODE_EXPERIMENTAL_BUILD': JSON.stringify('true'),
 
 
 
 
 
 
101
  'process.env.CLAUDE_CODE_VERIFY_PLAN': JSON.stringify('false'),
102
  'process.env.CCR_FORCE_BUNDLE': JSON.stringify('true'),
103
  'MACRO.VERSION': JSON.stringify(version),
 
108
  'MACRO.ISSUES_EXPLAINER': JSON.stringify(
109
  'This reconstructed source snapshot does not include Anthropic internal issue routing.',
110
  ),
111
+ 'MACRO.VERSION_CHANGELOG': JSON.stringify(getVersionChangelog()),
 
 
112
  } as const
113
 
114
  const cmd = [
 
133
  for (const external of externals) {
134
  cmd.push('--external', external)
135
  }
 
136
  for (const feature of features) {
137
  cmd.push(`--feature=${feature}`)
138
  }
 
139
  for (const [key, value] of Object.entries(defines)) {
140
  cmd.push('--define', `${key}=${value}`)
141
  }
 
155
  chmodSync(outfile, 0o755)
156
  }
157
 
158
+ // Copy vendor/ to dist/vendor/ for runtime audio-capture resolution
159
+ const distDir = dirname(outfile)
160
+ const vendorDir = join(distDir, 'vendor')
161
+ if (!existsSync(vendorDir)) {
162
+ cpSync('vendor', vendorDir, { recursive: true })
163
+ console.log(`Copied vendor/ ${vendorDir}/`)
 
 
164
  }
165
 
166
+ // Create symlink at project root for convenient access
167
+ const symlink = join(process.cwd(), 'VersperClaw')
168
+ const symlinkTarget = relative(process.cwd(), outfile)
169
+ try { unlinkSync(symlink) } catch { /* ignore */ }
170
+ try { symlinkSync(symlinkTarget, symlink) } catch { /* ignore */ }
171
 
172
+ console.log(`Built ${outfile}`)
173
+ console.log(`Symlink ${symlink} → ${symlinkTarget}`)
scripts/dev.ts ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Dev runner: invokes bun run ./src/entrypoints/cli.tsx with all experimental
3
+ * feature flags and build-time defines enabled.
4
+ *
5
+ * Keeps the feature list in scripts/features.ts — one place to update.
6
+ */
7
+ import { spawnSync } from 'bun'
8
+ import { FULL_EXPERIMENTAL_FEATURES } from './features.ts'
9
+
10
+ const version = '0.0.0-dev'
11
+ const buildTime = new Date().toISOString()
12
+
13
+ const featureArgs = FULL_EXPERIMENTAL_FEATURES.flatMap(f => ['--feature', f])
14
+
15
+ const defineArgs = [
16
+ '--define', `MACRO.VERSION:${JSON.stringify(version)}`,
17
+ '--define', `MACRO.BUILD_TIME:${JSON.stringify(buildTime)}`,
18
+ '--define', `MACRO.PACKAGE_URL:${JSON.stringify('versperclaw-dev')}`,
19
+ '--define', `MACRO.NATIVE_PACKAGE_URL:undefined`,
20
+ '--define', `MACRO.FEEDBACK_CHANNEL:${JSON.stringify('github')}`,
21
+ '--define', `MACRO.ISSUES_EXPLAINER:${JSON.stringify('')}`,
22
+ '--define', `MACRO.VERSION_CHANGELOG:${JSON.stringify('')}`,
23
+ '--define', `process.env.USER_TYPE:${JSON.stringify('external')}`,
24
+ '--define', `process.env.CLAUDE_CODE_FORCE_FULL_LOGO:${JSON.stringify('true')}`,
25
+ '--define', `process.env.NODE_ENV:${JSON.stringify('development')}`,
26
+ '--define', `process.env.CLAUDE_CODE_VERIFY_PLAN:${JSON.stringify('false')}`,
27
+ '--define', `process.env.CCR_FORCE_BUNDLE:${JSON.stringify('true')}`,
28
+ ]
29
+
30
+ const proc = spawnSync({
31
+ cmd: ['bun', 'run', ...featureArgs, ...defineArgs, './src/entrypoints/cli.tsx', ...process.argv.slice(2)],
32
+ cwd: process.cwd(),
33
+ stdout: 'inherit',
34
+ stderr: 'inherit',
35
+ stdin: 'inherit',
36
+ })
37
+
38
+ process.exit(proc.exitCode ?? 1)
scripts/features.ts ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Complete list of all experimental feature flags.
3
+ * Shared between build.ts (compile-time) and dev.ts (bun run --feature).
4
+ */
5
+ export const FULL_EXPERIMENTAL_FEATURES: readonly string[] = [
6
+ 'AGENT_MEMORY_SNAPSHOT',
7
+ 'AGENT_TRIGGERS',
8
+ 'AGENT_TRIGGERS_REMOTE',
9
+ 'AWAY_SUMMARY',
10
+ 'BASH_CLASSIFIER',
11
+ 'BUDDY',
12
+ 'BRIDGE_MODE',
13
+ 'BUILTIN_EXPLORE_PLAN_AGENTS',
14
+ 'CACHED_MICROCOMPACT',
15
+ 'CCR_AUTO_CONNECT',
16
+ 'CCR_MIRROR',
17
+ 'CCR_REMOTE_SETUP',
18
+ 'COMPACTION_REMINDERS',
19
+ 'CONNECTOR_TEXT',
20
+ 'EXTRACT_MEMORIES',
21
+ 'HISTORY_PICKER',
22
+ 'HOOK_PROMPTS',
23
+ 'KAIROS_BRIEF',
24
+ 'KAIROS_CHANNELS',
25
+ 'LODESTONE',
26
+ 'MCP_RICH_OUTPUT',
27
+ 'MESSAGE_ACTIONS',
28
+ 'NATIVE_CLIPBOARD_IMAGE',
29
+ 'NEW_INIT',
30
+ 'POWERSHELL_AUTO_MODE',
31
+ 'PROMPT_CACHE_BREAK_DETECTION',
32
+ 'QUICK_SEARCH',
33
+ 'SHOT_STATS',
34
+ 'TEAMMEM',
35
+ 'TOKEN_BUDGET',
36
+ 'TREE_SITTER_BASH',
37
+ 'TREE_SITTER_BASH_SHADOW',
38
+ 'TRANSCRIPT_CLASSIFIER',
39
+ 'ULTRAPLAN',
40
+ 'ULTRATHINK',
41
+ 'UNATTENDED_RETRY',
42
+ 'VERIFICATION_AGENT',
43
+ 'VOICE_MODE',
44
+ ]