@fugood/bricks-ctor 2.25.0-beta.6 → 2.25.0-beta.60

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 (188) hide show
  1. package/compile/__tests__/config-diff.test.js +100 -0
  2. package/compile/__tests__/index.test.js +461 -0
  3. package/compile/__tests__/util.test.js +450 -0
  4. package/compile/action-name-map.ts +64 -0
  5. package/compile/config-diff.ts +155 -0
  6. package/compile/index.ts +668 -352
  7. package/compile/util.ts +134 -10
  8. package/package.json +7 -3
  9. package/skills/bricks-ctor/SKILL.md +23 -17
  10. package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
  11. package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +19 -0
  12. package/skills/bricks-ctor/{rules → references}/automations.md +11 -0
  13. package/skills/bricks-ctor/references/buttress.md +245 -0
  14. package/skills/bricks-ctor/references/data-calculation.md +252 -0
  15. package/skills/bricks-ctor/{rules → references}/media-flow.md +7 -0
  16. package/skills/bricks-ctor/references/simulator.md +132 -0
  17. package/skills/bricks-ctor/references/source-editing-tools.md +81 -0
  18. package/skills/bricks-ctor/references/verification-toolchain.md +200 -0
  19. package/skills/bricks-design/SKILL.md +150 -45
  20. package/skills/bricks-design/references/architecture-truths.md +132 -0
  21. package/skills/bricks-design/references/avoiding-complexity.md +91 -0
  22. package/skills/bricks-design/references/design-critique.md +195 -0
  23. package/skills/bricks-design/references/design-languages.md +265 -0
  24. package/skills/bricks-design/references/performance.md +116 -0
  25. package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
  26. package/skills/bricks-design/references/translating-inputs.md +152 -0
  27. package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
  28. package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
  29. package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
  30. package/skills/bricks-design/references/workflow.md +134 -0
  31. package/skills/bricks-ux/SKILL.md +114 -0
  32. package/skills/bricks-ux/references/accessibility.md +162 -0
  33. package/skills/bricks-ux/references/flow-states.md +175 -0
  34. package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
  35. package/skills/bricks-ux/references/monitoring-screens.md +153 -0
  36. package/skills/bricks-ux/references/pressable-composition.md +126 -0
  37. package/skills/bricks-ux/references/user-journey.md +168 -0
  38. package/skills/bricks-ux/references/ux-critique.md +256 -0
  39. package/tools/__tests__/_cli-error.test.ts +35 -0
  40. package/tools/__tests__/_mcp-config.test.ts +67 -0
  41. package/tools/__tests__/pull.test.ts +108 -0
  42. package/tools/_cli-error.ts +17 -0
  43. package/tools/_edits-log.ts +41 -0
  44. package/tools/_git-author.ts +10 -2
  45. package/tools/_last-pushed-commit.ts +28 -0
  46. package/tools/_mcp-config.ts +42 -0
  47. package/tools/_shell.ts +8 -1
  48. package/tools/deploy.ts +17 -6
  49. package/tools/mcp-env.ts +13 -0
  50. package/tools/mcp-server.ts +8 -0
  51. package/tools/mcp-tools/__tests__/data-calc-editing.test.js +516 -0
  52. package/tools/mcp-tools/__tests__/entry-editing.test.js +866 -0
  53. package/tools/mcp-tools/__tests__/huggingface.test.ts +49 -0
  54. package/tools/mcp-tools/__tests__/icons.test.ts +21 -0
  55. package/tools/mcp-tools/__tests__/mcp-env.test.js +19 -0
  56. package/tools/mcp-tools/_editing-helpers.ts +98 -0
  57. package/tools/mcp-tools/_verify.ts +50 -0
  58. package/tools/mcp-tools/compile.ts +21 -9
  59. package/tools/mcp-tools/data-calc-editing.ts +1311 -0
  60. package/tools/mcp-tools/entry-editing.ts +2297 -0
  61. package/tools/mcp-tools/huggingface.ts +23 -13
  62. package/tools/mcp-tools/icons.ts +23 -7
  63. package/tools/mcp-tools/media.ts +4 -1
  64. package/tools/postinstall.ts +95 -38
  65. package/tools/pull.ts +100 -23
  66. package/tools/push-config.ts +114 -0
  67. package/tools/{preview-main.mjs → simulator-main.mjs} +207 -12
  68. package/tools/simulator-preload.cjs +16 -0
  69. package/tools/{preview.ts → simulator.ts} +4 -4
  70. package/types/{animation.ts → animation.d.ts} +24 -8
  71. package/types/{automation.ts → automation.d.ts} +16 -20
  72. package/types/{brick-base.ts → brick-base.d.ts} +1 -1
  73. package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
  74. package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
  75. package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
  76. package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
  77. package/types/bricks/{Image.ts → Image.d.ts} +21 -9
  78. package/types/bricks/{Items.ts → Items.d.ts} +11 -7
  79. package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
  80. package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
  81. package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
  82. package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
  83. package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
  84. package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
  85. package/types/bricks/Scene3D.d.ts +676 -0
  86. package/types/bricks/{Sketch.ts → Sketch.d.ts} +10 -8
  87. package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
  88. package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
  89. package/types/bricks/{Text.ts → Text.d.ts} +9 -9
  90. package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
  91. package/types/bricks/{Video.ts → Video.d.ts} +80 -13
  92. package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
  93. package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
  94. package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
  95. package/types/bricks/{index.ts → index.d.ts} +1 -0
  96. package/types/{common.ts → common.d.ts} +3 -6
  97. package/types/data-calc-command/base.d.ts +57 -0
  98. package/types/data-calc-command/collection.d.ts +418 -0
  99. package/types/data-calc-command/color.d.ts +432 -0
  100. package/types/data-calc-command/constant.d.ts +50 -0
  101. package/types/data-calc-command/datetime.d.ts +147 -0
  102. package/types/data-calc-command/file.d.ts +129 -0
  103. package/types/data-calc-command/index.d.ts +13 -0
  104. package/types/data-calc-command/iteratee.d.ts +23 -0
  105. package/types/data-calc-command/logictype.d.ts +190 -0
  106. package/types/data-calc-command/math.d.ts +275 -0
  107. package/types/data-calc-command/object.d.ts +119 -0
  108. package/types/data-calc-command/sandbox.d.ts +66 -0
  109. package/types/data-calc-command/string.d.ts +407 -0
  110. package/types/{data-calc.ts → data-calc.d.ts} +1 -0
  111. package/types/{data.ts → data.d.ts} +4 -2
  112. package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
  113. package/types/generators/{HttpServer.ts → HttpServer.d.ts} +56 -2
  114. package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +43 -1
  115. package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
  116. package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +5 -1
  117. package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +10 -1
  118. package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +6 -1
  119. package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +27 -4
  120. package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
  121. package/types/generators/{Tick.ts → Tick.d.ts} +1 -1
  122. package/types/generators/{VadGgml.ts → VadGgml.d.ts} +12 -2
  123. package/types/{subspace.ts → subspace.d.ts} +1 -1
  124. package/utils/__tests__/calc.test.js +25 -0
  125. package/utils/__tests__/id.test.js +154 -0
  126. package/utils/calc.ts +5 -1
  127. package/utils/data.ts +5 -7
  128. package/utils/event-props.ts +27 -1
  129. package/utils/id.ts +109 -56
  130. package/skills/bricks-ctor/rules/buttress.md +0 -156
  131. package/skills/bricks-ctor/rules/data-calculation.md +0 -209
  132. package/skills/bricks-design/LICENSE.txt +0 -180
  133. package/types/data-calc-command.ts +0 -7005
  134. /package/skills/bricks-ctor/{rules → references}/local-sync.md +0 -0
  135. /package/skills/bricks-ctor/{rules → references}/remote-data-bank.md +0 -0
  136. /package/skills/bricks-ctor/{rules → references}/standby-transition.md +0 -0
  137. /package/types/{canvas.ts → canvas.d.ts} +0 -0
  138. /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
  139. /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
  140. /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
  141. /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
  142. /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
  143. /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
  144. /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
  145. /package/types/generators/{File.ts → File.d.ts} +0 -0
  146. /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
  147. /package/types/generators/{Http.ts → Http.d.ts} +0 -0
  148. /package/types/generators/{Information.ts → Information.d.ts} +0 -0
  149. /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
  150. /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
  151. /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
  152. /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
  153. /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
  154. /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
  155. /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
  156. /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
  157. /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
  158. /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
  159. /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
  160. /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
  161. /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
  162. /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
  163. /package/types/generators/{Question.ts → Question.d.ts} +0 -0
  164. /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
  165. /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
  166. /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
  167. /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
  168. /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
  169. /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
  170. /package/types/generators/{Step.ts → Step.d.ts} +0 -0
  171. /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
  172. /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
  173. /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
  174. /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
  175. /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
  176. /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
  177. /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
  178. /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
  179. /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
  180. /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
  181. /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
  182. /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
  183. /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
  184. /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
  185. /package/types/generators/{index.ts → index.d.ts} +0 -0
  186. /package/types/{index.ts → index.d.ts} +0 -0
  187. /package/types/{switch.ts → switch.d.ts} +0 -0
  188. /package/types/{system.ts → system.d.ts} +0 -0
@@ -0,0 +1,49 @@
1
+ import { buildGGUFSplitFiles } from '../huggingface'
2
+
3
+ declare const describe: (name: string, fn: () => void) => void
4
+ declare const it: (name: string, fn: () => void) => void
5
+ declare const expect: (actual: unknown) => { toEqual: (expected: unknown) => void }
6
+
7
+ describe('buildGGUFSplitFiles', () => {
8
+ it('builds split GGUF files with indexed sibling metadata lookup', () => {
9
+ const siblings = [
10
+ {
11
+ rfilename: 'other-file.txt',
12
+ size: 999,
13
+ },
14
+ {
15
+ rfilename: 'model-00001-of-00003.gguf',
16
+ size: 1,
17
+ lfs: { sha256: 'one' },
18
+ },
19
+ {
20
+ rfilename: 'model-00002-of-00003.gguf',
21
+ size: 2,
22
+ lfs: { sha256: 'two' },
23
+ },
24
+ {
25
+ rfilename: 'model-00002-of-00003.gguf',
26
+ size: 22,
27
+ lfs: { sha256: 'two-late' },
28
+ },
29
+ ]
30
+
31
+ expect(buildGGUFSplitFiles('model-00001-of-00003.gguf', '00003', siblings)).toEqual([
32
+ {
33
+ rfilename: 'model-00001-of-00003.gguf',
34
+ size: 1,
35
+ lfs: { sha256: 'one' },
36
+ },
37
+ {
38
+ rfilename: 'model-00002-of-00003.gguf',
39
+ size: 2,
40
+ lfs: { sha256: 'two' },
41
+ },
42
+ {
43
+ rfilename: 'model-00003-of-00003.gguf',
44
+ size: undefined,
45
+ lfs: undefined,
46
+ },
47
+ ])
48
+ })
49
+ })
@@ -0,0 +1,21 @@
1
+ import { searchIcons } from '../icons'
2
+
3
+ declare const describe: (name: string, fn: () => void) => void
4
+ declare const test: (name: string, fn: () => void) => void
5
+ declare const expect: (actual: unknown) => {
6
+ toBe: (expected: unknown) => void
7
+ toHaveLength: (expected: number) => void
8
+ }
9
+
10
+ describe('icon_search helpers', () => {
11
+ test('searches the selected style corpus directly', () => {
12
+ const results = searchIcons('x', 10, 'brands')
13
+
14
+ expect(results).toHaveLength(10)
15
+ expect(results.every((result) => result.item.styles.includes('brands'))).toBe(true)
16
+ })
17
+
18
+ test('keeps unstyled searches capped by limit', () => {
19
+ expect(searchIcons('arrow', 5)).toHaveLength(5)
20
+ })
21
+ })
@@ -0,0 +1,19 @@
1
+ import { isTruthyEnv, shouldRegisterEditingTools } from '../../mcp-env'
2
+
3
+ describe('mcp env helpers', () => {
4
+ test('recognizes explicit truthy env values', () => {
5
+ for (const value of ['1', 'true', 'TRUE', 'yes', 'on', ' on ']) {
6
+ expect(isTruthyEnv(value)).toBe(true)
7
+ }
8
+ })
9
+
10
+ test('does not enable editing tools by default', () => {
11
+ expect(shouldRegisterEditingTools({})).toBe(false)
12
+ expect(shouldRegisterEditingTools({ BRICKS_CTOR_ENABLE_EDITING_TOOLS: '0' })).toBe(false)
13
+ expect(shouldRegisterEditingTools({ BRICKS_CTOR_ENABLE_EDITING_TOOLS: 'false' })).toBe(false)
14
+ })
15
+
16
+ test('enables editing tools when explicitly requested', () => {
17
+ expect(shouldRegisterEditingTools({ BRICKS_CTOR_ENABLE_EDITING_TOOLS: '1' })).toBe(true)
18
+ })
19
+ })
@@ -0,0 +1,98 @@
1
+ import * as t from '@babel/types'
2
+ import path from 'node:path'
3
+
4
+ export const oxfmtOptions = {
5
+ trailingComma: 'all',
6
+ tabWidth: 2,
7
+ semi: false,
8
+ singleQuote: true,
9
+ printWidth: 100,
10
+ } as const
11
+
12
+ export const isRecord = (value: unknown): value is Record<string, unknown> =>
13
+ Boolean(value) && typeof value === 'object' && !Array.isArray(value)
14
+
15
+ export const isIdentifierName = (value: string) => /^[$A-Z_a-z][$\w]*$/.test(value)
16
+
17
+ export const normalizeRelPath = (file: string) => file.replace(/\\/g, '/').replace(/^\.\/+/, '')
18
+
19
+ export const projectRelativePath = (projectDir: string, absPath: string) =>
20
+ normalizeRelPath(path.relative(projectDir, absPath))
21
+
22
+ export const getPropertyKeyName = (key: t.Expression | t.PrivateName) => {
23
+ if (t.isIdentifier(key)) return key.name
24
+ if (t.isStringLiteral(key) || t.isNumericLiteral(key)) return String(key.value)
25
+ return null
26
+ }
27
+
28
+ export const makeObjectKey = (key: string) =>
29
+ isIdentifierName(key) ? t.identifier(key) : t.stringLiteral(key)
30
+
31
+ export const getObjectProperty = (object: t.ObjectExpression, key: string) =>
32
+ object.properties.find((property): property is t.ObjectProperty => {
33
+ if (!t.isObjectProperty(property)) return false
34
+ return getPropertyKeyName(property.key) === key
35
+ })
36
+
37
+ export const getStringProperty = (object: t.ObjectExpression, key: string) => {
38
+ const property = getObjectProperty(object, key)
39
+ if (!property || !t.isStringLiteral(property.value)) return undefined
40
+ return property.value.value
41
+ }
42
+
43
+ export const setObjectProperty = (object: t.ObjectExpression, key: string, value: t.Expression) => {
44
+ const existing = getObjectProperty(object, key)
45
+ if (existing) {
46
+ existing.value = value
47
+ return
48
+ }
49
+ object.properties.push(t.objectProperty(makeObjectKey(key), value))
50
+ }
51
+
52
+ export const removeObjectProperty = (object: t.ObjectExpression, key: string) => {
53
+ const index = object.properties.findIndex((property) => {
54
+ if (!t.isObjectProperty(property)) return false
55
+ return getPropertyKeyName(property.key) === key
56
+ })
57
+ if (index >= 0) object.properties.splice(index, 1)
58
+ }
59
+
60
+ export const insertImport = (ast: t.File, declaration: t.ImportDeclaration) => {
61
+ const body = ast.program.body
62
+ const lastImportIndex = body.findLastIndex((statement) => t.isImportDeclaration(statement))
63
+ body.splice(lastImportIndex + 1, 0, declaration)
64
+ }
65
+
66
+ export const ensureBricksCtorImport = (
67
+ ast: t.File,
68
+ importKind: 'type' | 'value',
69
+ names: Iterable<string>,
70
+ ) => {
71
+ const missing = new Set(Array.from(names).filter(Boolean))
72
+ if (missing.size === 0) return
73
+
74
+ for (const statement of ast.program.body) {
75
+ if (!t.isImportDeclaration(statement) || statement.source.value !== 'bricks-ctor') continue
76
+ const isTypeImport = statement.importKind === 'type'
77
+ if ((importKind === 'type') !== isTypeImport) continue
78
+
79
+ statement.specifiers.forEach((specifier) => {
80
+ if (!t.isImportSpecifier(specifier)) return
81
+ const imported = specifier.imported
82
+ if (t.isIdentifier(imported)) missing.delete(imported.name)
83
+ if (t.isStringLiteral(imported)) missing.delete(imported.value)
84
+ })
85
+
86
+ missing.forEach((name) => {
87
+ statement.specifiers.push(t.importSpecifier(t.identifier(name), t.identifier(name)))
88
+ })
89
+ return
90
+ }
91
+
92
+ const declaration = t.importDeclaration(
93
+ Array.from(missing).map((name) => t.importSpecifier(t.identifier(name), t.identifier(name))),
94
+ t.stringLiteral('bricks-ctor'),
95
+ )
96
+ if (importKind === 'type') declaration.importKind = 'type'
97
+ insertImport(ast, declaration)
98
+ }
@@ -0,0 +1,50 @@
1
+ import { sh } from '../_shell'
2
+ import { computeConfigChange, readBuildConfig, type ConfigChange } from '../../compile/config-diff'
3
+
4
+ // Result of an editing tool's compile verification, carrying the config delta the
5
+ // compile produced. Shared by entry-editing and data-calc-editing.
6
+ export type VerificationResult = {
7
+ status: 'skipped' | 'compile:ok' | 'compile:failed'
8
+ errors: string[]
9
+ configChange?: ConfigChange
10
+ }
11
+
12
+ // Strip ANSI colors from the spawned compile so the captured output stays plain text.
13
+ const noColorEnv = { FORCE_COLOR: '0', NO_COLOR: '1' }
14
+
15
+ // Recompile the project and diff the result against the prior build artifact.
16
+ export const compileAndDiff = async (projectDir: string): Promise<VerificationResult> => {
17
+ // Snapshot the prior compiled config before recompiling (the new artifact overwrites it).
18
+ const before = await readBuildConfig(projectDir)
19
+ try {
20
+ // Turn off the editing-tools audit for this spawned compile so compile() doesn't write
21
+ // a duplicate generic entry — the calling editing tool records its own richer entry.
22
+ await sh`bun compile`
23
+ .cwd(projectDir)
24
+ .env({ ...noColorEnv, BRICKS_CTOR_ENABLE_EDITING_TOOLS: '0' })
25
+ .text()
26
+ } catch (err: any) {
27
+ const stdout = err.stdout?.toString() ?? ''
28
+ const stderr = err.stderr?.toString() ?? ''
29
+ const output = [stdout, stderr, err.message].filter(Boolean).join('\n').trim()
30
+ return { status: 'compile:failed', errors: output ? [output] : ['bun compile failed'] }
31
+ }
32
+ const after = await readBuildConfig(projectDir)
33
+ return { status: 'compile:ok', errors: [], configChange: computeConfigChange(before, after) }
34
+ }
35
+
36
+ const shouldVerify = (inputVerify?: boolean) => {
37
+ if (inputVerify !== undefined) return inputVerify
38
+ const value = process.env.BRICKS_CTOR_MCP_EDIT_VERIFY ?? 'true'
39
+ return !['0', 'false', 'no', 'off'].includes(value.toLowerCase())
40
+ }
41
+
42
+ // Compile verification shared by the source-editing tools: per-call `verify` wins,
43
+ // then the BRICKS_CTOR_MCP_EDIT_VERIFY env toggle, defaulting to on.
44
+ export const verifyProject = async (
45
+ projectDir: string,
46
+ verify?: boolean,
47
+ ): Promise<VerificationResult> => {
48
+ if (!shouldVerify(verify)) return { status: 'skipped', errors: [] }
49
+ return compileAndDiff(projectDir)
50
+ }
@@ -3,24 +3,33 @@ import { readFile } from 'node:fs/promises'
3
3
  import { z } from 'zod'
4
4
  import { sh } from '../_shell'
5
5
 
6
+ // Disable ANSI color codes from spawned tools so MCP text output stays readable
7
+ // (the host renders raw text; escape sequences leak into the result otherwise).
8
+ const noColorEnv = { FORCE_COLOR: '0', NO_COLOR: '1' }
9
+
6
10
  export function register(server: McpServer, projectDir: string) {
7
11
  const { dirname } = import.meta
8
12
 
13
+ // `bun compile` records the resulting config delta to `.bricks/edits.jsonl` itself
14
+ // (see compile() in compile/index.ts), so the spawned output below already reflects it.
9
15
  server.tool('compile', {}, async () => {
10
- let log = ''
16
+ let log = 'Type checking & Compiling...\n'
11
17
  try {
12
- log += 'Type checking & Compiling...'
13
- log += await sh`bun compile`.cwd(projectDir).text()
14
- } catch (err) {
15
- log += `${err.stdout.toString()}\n${err.stderr.toString()}`
18
+ log += await sh`bun compile`.cwd(projectDir).env(noColorEnv).text()
19
+ } catch (err: any) {
20
+ const stdout = err.stdout?.toString() ?? ''
21
+ const stderr = err.stderr?.toString() ?? ''
22
+ log += [stdout, stderr].filter(Boolean).join('\n')
16
23
  }
17
24
  return {
18
25
  content: [{ type: 'text', text: log }],
19
26
  }
20
27
  })
21
28
 
29
+ if (process.env.BRICKS_CTOR_MCP_DISABLE_PREVIEW === '1') return
30
+
22
31
  server.tool(
23
- 'preview',
32
+ 'simulator',
24
33
  {
25
34
  delay: z
26
35
  .number()
@@ -61,11 +70,14 @@ export function register(server: McpServer, projectDir: string) {
61
70
  ]
62
71
  if (testId) args.push('--test-id', testId)
63
72
  if (testTitleLike) args.push('--test-title-like', testTitleLike)
64
- log = await sh`bunx --bun electron ${toolsDir}/preview-main.mjs ${args}`
73
+ log = await sh`bunx --bun electron ${toolsDir}/simulator-main.mjs ${args}`
65
74
  .cwd(projectDir)
75
+ .env(noColorEnv)
66
76
  .text()
67
- } catch (err) {
68
- log = `${err.stdout.toString()}\n${err.stderr.toString()}`
77
+ } catch (err: any) {
78
+ const stdout = err.stdout?.toString() ?? ''
79
+ const stderr = err.stderr?.toString() ?? ''
80
+ log = [stdout, stderr].filter(Boolean).join('\n')
69
81
  error = true
70
82
  }
71
83
  let screenshotBase64: string | null = null