@fugood/bricks-project 2.24.0-beta.19 → 2.24.0-beta.20
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.
- package/package.json +3 -3
- package/tools/postinstall.ts +110 -27
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-project",
|
|
3
|
-
"version": "2.24.0-beta.
|
|
3
|
+
"version": "2.24.0-beta.20",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"typecheck": "tsc --noEmit",
|
|
7
7
|
"build": "bun scripts/build.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@fugood/bricks-cli": "^2.24.0-beta.
|
|
10
|
+
"@fugood/bricks-cli": "^2.24.0-beta.20",
|
|
11
11
|
"@huggingface/gguf": "^0.3.2",
|
|
12
12
|
"@iarna/toml": "^3.0.0",
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
@@ -24,5 +24,5 @@
|
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"oxfmt": "^0.36.0"
|
|
26
26
|
},
|
|
27
|
-
"gitHead": "
|
|
27
|
+
"gitHead": "3fbcc074311b542fd2d078d5b107e0e433b6a3b6"
|
|
28
28
|
}
|
package/tools/postinstall.ts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { $ } from 'bun'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
cp,
|
|
4
|
+
lstat,
|
|
5
|
+
mkdir,
|
|
6
|
+
readFile,
|
|
7
|
+
readdir,
|
|
8
|
+
readlink,
|
|
9
|
+
rm,
|
|
10
|
+
stat,
|
|
11
|
+
symlink,
|
|
12
|
+
writeFile,
|
|
13
|
+
} from 'fs/promises'
|
|
14
|
+
import * as path from 'path'
|
|
3
15
|
import TOML from '@iarna/toml'
|
|
4
16
|
|
|
5
17
|
const cwd = process.cwd()
|
|
18
|
+
const projectSkillsDir = path.join(cwd, '.bricks', 'skills')
|
|
19
|
+
const compatibilitySkillLinks = [
|
|
20
|
+
path.join(cwd, '.claude', 'skills'),
|
|
21
|
+
path.join(cwd, '.codex', 'skills'),
|
|
22
|
+
]
|
|
6
23
|
|
|
7
24
|
async function exists(f: string) {
|
|
8
25
|
try {
|
|
@@ -13,6 +30,15 @@ async function exists(f: string) {
|
|
|
13
30
|
}
|
|
14
31
|
}
|
|
15
32
|
|
|
33
|
+
async function pathExists(f: string) {
|
|
34
|
+
try {
|
|
35
|
+
await lstat(f)
|
|
36
|
+
return true
|
|
37
|
+
} catch {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
16
42
|
// handle flag --skip-copy
|
|
17
43
|
const skipCopyProject = process.argv.includes('--skip-copy-project')
|
|
18
44
|
if (skipCopyProject) {
|
|
@@ -34,6 +60,7 @@ type CodexMcpConfig = {
|
|
|
34
60
|
mcp_servers: Record<string, typeof projectMcpServer>
|
|
35
61
|
}
|
|
36
62
|
|
|
63
|
+
// Claude Code and AGENTS.md projects both use the shared project .mcp.json file.
|
|
37
64
|
const defaultMcpConfig = {
|
|
38
65
|
mcpServers: {
|
|
39
66
|
'bricks-project': projectMcpServer,
|
|
@@ -64,43 +91,99 @@ const hasClaudeCode = await exists(`${cwd}/CLAUDE.md`)
|
|
|
64
91
|
const hasAgentsMd = await exists(`${cwd}/AGENTS.md`)
|
|
65
92
|
|
|
66
93
|
if (hasClaudeCode || hasAgentsMd) {
|
|
94
|
+
// Keep the workspace-level JSON MCP config aligned for tools that read .mcp.json.
|
|
67
95
|
const mcpConfigPath = `${cwd}/.mcp.json`
|
|
68
96
|
await handleMcpConfigOverride(mcpConfigPath)
|
|
69
97
|
}
|
|
70
98
|
|
|
71
|
-
const
|
|
99
|
+
const copyMissingSkills = async (sourceDir: string, targetDir: string) => {
|
|
100
|
+
if (!(await exists(sourceDir))) return
|
|
101
|
+
|
|
102
|
+
const packageSkills = await readdir(sourceDir, { withFileTypes: true })
|
|
103
|
+
const skillsToInstall = packageSkills.filter(
|
|
104
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith('.'),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
await mkdir(targetDir, { recursive: true })
|
|
108
|
+
|
|
109
|
+
await Promise.all(
|
|
110
|
+
skillsToInstall.map(async (entry) => {
|
|
111
|
+
const targetSkillDir = path.join(targetDir, entry.name)
|
|
112
|
+
if (await exists(targetSkillDir)) {
|
|
113
|
+
console.log(`Skill '${entry.name}' already exists, skipping`)
|
|
114
|
+
} else {
|
|
115
|
+
await cp(path.join(sourceDir, entry.name), targetSkillDir, { recursive: true })
|
|
116
|
+
console.log(`Installed skill '${entry.name}' to ${targetDir}/`)
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const migrateSkillsDir = async (legacySkillsDir: string, canonicalSkillsDir: string) => {
|
|
123
|
+
if (!(await pathExists(legacySkillsDir))) return
|
|
124
|
+
|
|
125
|
+
const legacyStats = await lstat(legacySkillsDir)
|
|
126
|
+
|
|
127
|
+
if (legacyStats.isSymbolicLink()) {
|
|
128
|
+
const linkTarget = await readlink(legacySkillsDir)
|
|
129
|
+
const resolvedTarget = path.resolve(path.dirname(legacySkillsDir), linkTarget)
|
|
130
|
+
if (resolvedTarget === canonicalSkillsDir) return
|
|
131
|
+
|
|
132
|
+
await copyMissingSkills(resolvedTarget, canonicalSkillsDir)
|
|
133
|
+
await rm(legacySkillsDir, { force: true, recursive: true })
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (legacyStats.isDirectory()) {
|
|
138
|
+
await copyMissingSkills(legacySkillsDir, canonicalSkillsDir)
|
|
139
|
+
await rm(legacySkillsDir, { force: true, recursive: true })
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.warn(`Skipping skills migration for ${legacySkillsDir}; expected a directory or symlink`)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ensureCompatibilitySkillLink = async (linkPath: string, targetDir: string) => {
|
|
147
|
+
await mkdir(path.dirname(linkPath), { recursive: true })
|
|
148
|
+
|
|
149
|
+
if (await pathExists(linkPath)) {
|
|
150
|
+
const linkStats = await lstat(linkPath)
|
|
151
|
+
if (linkStats.isSymbolicLink()) {
|
|
152
|
+
const linkTarget = await readlink(linkPath)
|
|
153
|
+
const resolvedTarget = path.resolve(path.dirname(linkPath), linkTarget)
|
|
154
|
+
if (resolvedTarget === targetDir) return
|
|
155
|
+
} else {
|
|
156
|
+
console.warn(
|
|
157
|
+
`Skipping skills symlink at ${linkPath}; path already exists and is not a symlink`,
|
|
158
|
+
)
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const relativeTarget = path.relative(path.dirname(linkPath), targetDir)
|
|
164
|
+
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir'
|
|
165
|
+
await symlink(relativeTarget, linkPath, symlinkType)
|
|
166
|
+
console.log(`Linked ${linkPath} -> ${relativeTarget}`)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const setupSkills = async () => {
|
|
72
170
|
const packageSkillsDir = `${__dirname}/../skills`
|
|
171
|
+
await mkdir(projectSkillsDir, { recursive: true })
|
|
172
|
+
await copyMissingSkills(packageSkillsDir, projectSkillsDir)
|
|
73
173
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
await $`mkdir -p ${skillsDir}`
|
|
79
|
-
|
|
80
|
-
await Promise.all(
|
|
81
|
-
skillsToInstall.map(async (skill) => {
|
|
82
|
-
const targetSkillDir = `${skillsDir}/${skill}`
|
|
83
|
-
if (await exists(targetSkillDir)) {
|
|
84
|
-
console.log(`Skill '${skill}' already exists, skipping`)
|
|
85
|
-
} else {
|
|
86
|
-
await $`cp -r ${packageSkillsDir}/${skill} ${targetSkillDir}`
|
|
87
|
-
console.log(`Installed skill '${skill}' to ${skillsDir}/`)
|
|
88
|
-
}
|
|
89
|
-
}),
|
|
90
|
-
)
|
|
174
|
+
for (const linkPath of compatibilitySkillLinks) {
|
|
175
|
+
await migrateSkillsDir(linkPath, projectSkillsDir)
|
|
176
|
+
await ensureCompatibilitySkillLink(linkPath, projectSkillsDir)
|
|
91
177
|
}
|
|
92
178
|
}
|
|
93
179
|
|
|
94
|
-
if (hasClaudeCode) {
|
|
95
|
-
// Install skills
|
|
96
|
-
await setupSkills(
|
|
180
|
+
if (hasClaudeCode || hasAgentsMd) {
|
|
181
|
+
// Install project skills once and expose them through compatibility symlinks.
|
|
182
|
+
await setupSkills()
|
|
97
183
|
}
|
|
98
184
|
|
|
99
185
|
if (hasAgentsMd) {
|
|
100
|
-
//
|
|
101
|
-
// Currently no signal file for codex skills, so we just check if AGENTS.md exists
|
|
102
|
-
await setupSkills(`${cwd}/.codex/skills`)
|
|
103
|
-
|
|
186
|
+
// Codex stores its project-local MCP config in .codex/config.toml.
|
|
104
187
|
const defaultCodexMcpConfig = {
|
|
105
188
|
mcp_servers: {
|
|
106
189
|
'bricks-project': projectMcpServer,
|
|
@@ -128,7 +211,7 @@ if (hasAgentsMd) {
|
|
|
128
211
|
console.log(`Updated ${mcpConfigPath}`)
|
|
129
212
|
}
|
|
130
213
|
|
|
131
|
-
//
|
|
214
|
+
// Keep the Codex TOML MCP config aligned with the same bricks-project server entry.
|
|
132
215
|
const codexConfigPath = `${cwd}/.codex/config.toml`
|
|
133
216
|
await handleCodexMcpConfigOverride(codexConfigPath)
|
|
134
217
|
}
|