@fugood/bricks-project 2.24.0-beta.12 → 2.24.0-beta.13

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.
@@ -0,0 +1,102 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import { z } from 'zod'
3
+ import * as TOON from '@toon-format/toon'
4
+
5
+ const LOTTIEFILES_API_URL = 'https://lottiefiles.com/api'
6
+
7
+ export function register(server: McpServer) {
8
+ server.tool(
9
+ 'lottie_search',
10
+ {
11
+ query: z.string().describe('Search keywords for animations'),
12
+ page: z.number().min(1).optional().default(1),
13
+ limit: z.number().min(1).max(100).optional().default(10),
14
+ },
15
+ async ({ query, page, limit }) => {
16
+ try {
17
+ const url = new URL(`${LOTTIEFILES_API_URL}/search/get-animations`)
18
+ url.searchParams.set('query', query)
19
+ url.searchParams.set('page', String(page))
20
+ url.searchParams.set('limit', String(limit))
21
+ url.searchParams.set('format', 'json')
22
+
23
+ const response = await fetch(url.toString())
24
+ const data = await response.json()
25
+ const animations = data?.data?.data ?? []
26
+
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: TOON.encode({ count: animations.length, animations }),
32
+ },
33
+ ],
34
+ }
35
+ } catch (err: any) {
36
+ return {
37
+ content: [{ type: 'text', text: `Failed to search animations: ${err.message}` }],
38
+ }
39
+ }
40
+ },
41
+ )
42
+
43
+ server.tool(
44
+ 'lottie_get_details',
45
+ {
46
+ id: z.string().describe('Animation file ID'),
47
+ },
48
+ async ({ id }) => {
49
+ try {
50
+ const url = new URL(`${LOTTIEFILES_API_URL}/animations/get-animation-data`)
51
+ url.searchParams.set('fileId', id)
52
+ url.searchParams.set('format', 'json')
53
+
54
+ const response = await fetch(url.toString())
55
+ const data = await response.json()
56
+
57
+ return {
58
+ content: [{ type: 'text', text: TOON.encode(data) }],
59
+ }
60
+ } catch (err: any) {
61
+ return {
62
+ content: [{ type: 'text', text: `Failed to get animation: ${err.message}` }],
63
+ }
64
+ }
65
+ },
66
+ )
67
+
68
+ server.tool(
69
+ 'lottie_popular',
70
+ {
71
+ page: z.number().min(1).optional().default(1),
72
+ limit: z.number().min(1).max(100).optional().default(10),
73
+ },
74
+ async ({ page, limit }) => {
75
+ try {
76
+ const url = new URL(
77
+ `${LOTTIEFILES_API_URL}/iconscout/popular-animations-weekly?api=%26sort%3Dpopular`,
78
+ )
79
+ url.searchParams.set('page', String(page))
80
+ url.searchParams.set('limit', String(limit))
81
+ url.searchParams.set('format', 'json')
82
+
83
+ const response = await fetch(url.toString())
84
+ const data = await response.json()
85
+ const animations = data?.popularWeeklyData?.data ?? []
86
+
87
+ return {
88
+ content: [
89
+ {
90
+ type: 'text',
91
+ text: TOON.encode({ count: animations.length, animations }),
92
+ },
93
+ ],
94
+ }
95
+ } catch (err: any) {
96
+ return {
97
+ content: [{ type: 'text', text: `Failed to get popular animations: ${err.message}` }],
98
+ }
99
+ }
100
+ },
101
+ )
102
+ }
@@ -0,0 +1,110 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import { z } from 'zod'
3
+ import { $ } from 'bun'
4
+
5
+ const runBricks = async (projectDir: string, ...args: string[]) => {
6
+ try {
7
+ return await $`bunx bricks ${args}`.cwd(projectDir).text()
8
+ } catch (err: any) {
9
+ throw new Error(err.stderr?.toString() || err.message)
10
+ }
11
+ }
12
+
13
+ export function register(server: McpServer, projectDir: string) {
14
+ server.tool('media_boxes', {}, async () => {
15
+ try {
16
+ const output = await runBricks(projectDir, 'media', 'boxes')
17
+ return { content: [{ type: 'text', text: output }] }
18
+ } catch (err: any) {
19
+ return { content: [{ type: 'text', text: `Failed to list media boxes: ${err.message}` }] }
20
+ }
21
+ })
22
+
23
+ server.tool('media_get_box', { id: z.string().describe('Media box ID') }, async ({ id }) => {
24
+ try {
25
+ const output = await runBricks(projectDir, 'media', 'box', id)
26
+ return { content: [{ type: 'text', text: output }] }
27
+ } catch (err: any) {
28
+ return { content: [{ type: 'text', text: `Failed to get media box: ${err.message}` }] }
29
+ }
30
+ })
31
+
32
+ server.tool(
33
+ 'media_files',
34
+ {
35
+ boxId: z.string().describe('Media box ID'),
36
+ types: z.string().describe('Comma-separated file types to include').optional(),
37
+ userTag: z.array(z.string()).describe('Filter by user tags').optional(),
38
+ limit: z.number().describe('Limit results').optional(),
39
+ offset: z.number().describe('Offset results').optional(),
40
+ },
41
+ async ({ boxId, types, userTag, limit, offset }) => {
42
+ try {
43
+ const args = ['media', 'files', boxId]
44
+ if (types) args.push('-t', types)
45
+ if (userTag) userTag.forEach((tag) => args.push('-u', tag))
46
+ if (limit != null) args.push('-l', String(limit))
47
+ if (offset != null) args.push('-o', String(offset))
48
+ const output = await runBricks(projectDir, ...args)
49
+ return { content: [{ type: 'text', text: output }] }
50
+ } catch (err: any) {
51
+ return { content: [{ type: 'text', text: `Failed to list media files: ${err.message}` }] }
52
+ }
53
+ },
54
+ )
55
+
56
+ server.tool('media_file', { id: z.string().describe('Media file ID') }, async ({ id }) => {
57
+ try {
58
+ const output = await runBricks(projectDir, 'media', 'file', id)
59
+ return { content: [{ type: 'text', text: output }] }
60
+ } catch (err: any) {
61
+ return { content: [{ type: 'text', text: `Failed to get media file: ${err.message}` }] }
62
+ }
63
+ })
64
+
65
+ server.tool(
66
+ 'media_upload_files',
67
+ {
68
+ boxId: z.string().describe('Target media box ID'),
69
+ files: z.array(z.string()).min(1).describe('File paths to upload'),
70
+ description: z.string().describe('File description').optional(),
71
+ userTags: z.array(z.string()).max(15).describe('User tags (max 15)').optional(),
72
+ imageVersion: z
73
+ .array(z.string())
74
+ .describe('Image resize specs as WxH or WxH:STRATEGY')
75
+ .optional(),
76
+ imageVersionType: z.enum(['jpg', 'png']).describe('Image output format').optional(),
77
+ enableAiAnalysis: z.boolean().describe('Enable AI analysis').optional(),
78
+ aiInstruction: z.string().describe('Custom AI analysis instruction').optional(),
79
+ concurrency: z.number().min(1).describe('Max concurrent uploads').optional(),
80
+ },
81
+ async ({
82
+ boxId,
83
+ files,
84
+ description,
85
+ userTags,
86
+ imageVersion,
87
+ imageVersionType,
88
+ enableAiAnalysis,
89
+ aiInstruction,
90
+ concurrency,
91
+ }) => {
92
+ try {
93
+ const args = ['media', 'upload', boxId, ...files]
94
+ if (description) args.push('-d', description)
95
+ if (userTags) userTags.forEach((tag) => args.push('-t', tag))
96
+ if (imageVersion) imageVersion.forEach((spec) => args.push('--image-version', spec))
97
+ if (imageVersionType) args.push('--image-version-type', imageVersionType)
98
+ if (enableAiAnalysis) args.push('--enable-ai-analysis')
99
+ if (aiInstruction) args.push('--ai-instruction', aiInstruction)
100
+ if (concurrency != null) args.push('--concurrency', String(concurrency))
101
+ const output = await runBricks(projectDir, ...args)
102
+ return { content: [{ type: 'text', text: output }] }
103
+ } catch (err: any) {
104
+ return {
105
+ content: [{ type: 'text', text: `Failed to upload media files: ${err.message}` }],
106
+ }
107
+ }
108
+ },
109
+ )
110
+ }
package/tools/pull.ts CHANGED
@@ -97,12 +97,15 @@ await Promise.all(
97
97
 
98
98
  if (isGitRepo) {
99
99
  await $`cd ${cwd} && git add .`
100
- const commitMsg = force
101
- ? `chore(force-pull): apply force pull-${command}`
102
- : isModule
103
- ? 'chore(project): apply file changes from BRICKS module'
104
- : 'chore(project): apply file changes from BRICKS application'
105
- await $`cd ${cwd} && git commit -m ${commitMsg}`
100
+ const hasChanges = !!(await $`cd ${cwd} && git diff --cached --name-only`.text()).trim()
101
+ if (hasChanges) {
102
+ const commitMsg = force
103
+ ? `chore(force-pull): apply force pull-${command}`
104
+ : isModule
105
+ ? 'chore(project): apply file changes from BRICKS module'
106
+ : 'chore(project): apply file changes from BRICKS application'
107
+ await $`cd ${cwd} && git commit -m ${commitMsg}`
108
+ }
106
109
  if (!force && !useMain) {
107
110
  await $`cd ${cwd} && git merge main`
108
111
  }