cardbuddy 0.14.0 → 0.14.2
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/dist/mcp.js +4 -0
- package/dist/preview-editor/assets/{index-Bccog4uC.js → index-BN79pTkQ.js} +23 -23
- package/dist/preview-editor/index.html +1 -1
- package/dist/standalone.js +10 -2
- package/dist/vendored/mcp/client.mjs +6 -0
- package/dist/vendored/server/core/ai-chat.js +4 -2
- package/dist/vendored/server/core/index.js +7 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +21 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.py +23 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +18 -13
- package/dist/vendored/skills/dsl-card-generator/scripts/slices_normalize.mjs +4 -5
- package/dist/vendored/skills/shared/finmall-config.mjs +11 -1
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +1 -1
- package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +7 -2
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +14 -7
- package/package.json +3 -3
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16-Cg6Nrosk.png" />
|
|
9
9
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32-B_1744sp.png" />
|
|
10
10
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-BQcES9QK.png" />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-BN79pTkQ.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-cbTCHp8J.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
package/dist/standalone.js
CHANGED
|
@@ -22,8 +22,11 @@ import vue from '@vitejs/plugin-vue'
|
|
|
22
22
|
import chokidar from 'chokidar'
|
|
23
23
|
import chalk from 'chalk'
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// 一律经 core.js 取 server/core 的东西:仓库内它桥到 ../../../server/core/index.js,
|
|
26
|
+
// 发布态由 build.mjs 生成 dist/core.js 桥到包内快照 ./vendored/server/core/index.js。
|
|
27
|
+
// 直接写仓库相对路径(如 ../../../server/core/cardFiles.js)在**安装态会解析到包外** —— 0.14.0
|
|
28
|
+
// 就是因此在全局安装后 ERR_MODULE_NOT_FOUND(连 --help 都起不来)。见 build.mjs 门禁 D。
|
|
29
|
+
import { mountCoreMiddlewares, installLogMirror, subscribeLog, refreshCardList } from './core.js'
|
|
27
30
|
import { loadConfig } from './config.js'
|
|
28
31
|
import { ensurePortFree } from './portGuard.js'
|
|
29
32
|
|
|
@@ -440,6 +443,11 @@ export async function startServer(userConfig = {}) {
|
|
|
440
443
|
// (首次 AI 请求才 import),故这里在启动期注入即可;已有的显式环境变量优先,不覆盖。
|
|
441
444
|
if (!process.env.CARD_ROOT) process.env.CARD_ROOT = config.root
|
|
442
445
|
if (!process.env.CARD_API_BASE) process.env.CARD_API_BASE = apiBase
|
|
446
|
+
// 技能层(skills/*/scripts,发布态为包内快照)认宿主工程根用的是 DSL_CARD_PROJECT_ROOT。
|
|
447
|
+
// 这里显式桥一次:脚本不能靠「自身位置往上推」认工程根 —— 发布态它住在
|
|
448
|
+
// <pkg>/dist/vendored/skills/... 里,推出来的是包内快照目录,UI/ 产物会写进 node_modules
|
|
449
|
+
// (0.14.1 的 route_input 事故)。技能层的 AI 工具与 skill_exec 子进程都继承本进程 env。
|
|
450
|
+
if (!process.env.DSL_CARD_PROJECT_ROOT) process.env.DSL_CARD_PROJECT_ROOT = config.root
|
|
443
451
|
// upstream(FINMALL_BASE_URL)透传给资源代理中间件,使其能回源 FinMall 取站内资源
|
|
444
452
|
mountCoreMiddlewares(app, {
|
|
445
453
|
root: config.root,
|
|
@@ -19,6 +19,12 @@ import { devApiBase } from '../tools/finmall-env.mjs'
|
|
|
19
19
|
|
|
20
20
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
21
21
|
const ROOT = process.env.CARD_ROOT ? resolve(process.env.CARD_ROOT) : resolve(__dirname, '..')
|
|
22
|
+
// 本模块下面会 import 技能层脚本(route_input / scaffold / …),它们按 DSL_CARD_PROJECT_ROOT
|
|
23
|
+
// 认宿主工程根。必须在**任何** skills/* 模块被加载之前落定 —— 那些模块在 import 期就算好路径常量
|
|
24
|
+
// (晚一步注入等于没注入)。CARD_ROOT 给了就以它为准;没有 CARD_ROOT 时 ROOT 已是本模块的上一级,
|
|
25
|
+
// 仓库内即仓库根。这样即使外部 MCP 客户端只配了命令、没配 cwd,技能层也不会把 UI/ 产物写进
|
|
26
|
+
// node_modules 里的包内快照(0.14.1 的 route_input 事故)。
|
|
27
|
+
if (!process.env.DSL_CARD_PROJECT_ROOT) process.env.DSL_CARD_PROJECT_ROOT = ROOT
|
|
22
28
|
// dev server 端口由 .env.cb 的 CARDBUDDY_DEV_PORT 控制(默认 5180);
|
|
23
29
|
// 仍可用 CARD_API_BASE 覆盖(如 dev server 跑在非默认端口)。
|
|
24
30
|
const BASE = (process.env.CARD_API_BASE || devApiBase()).replace(/\/$/, '')
|
|
@@ -240,7 +240,7 @@ function buildSystemPrompt({ shellAllowed = false, vision = false, autoApprove =
|
|
|
240
240
|
4. 改完用人话说明:改了什么、为什么、需要用户确认什么。
|
|
241
241
|
5. 信息足够后立即收敛:直接调用工具落盘或回复方案;同一内容不要重复读取。**思考里不要逐字草拟 schema / IR / 长 JSON,也不要复述卡片数据**——那会同时吃掉思考与输出额度,想清结构就立刻调工具把内容写出去。
|
|
242
242
|
6. 读文件用 fs_read(仓库内任意文本文件 / 挂载附件),列目录用 fs_list —— fs_read 只能读文件,对目录一定报错,别拿它试目录;被中止过的轮次按上下文里的中止标记从中断处继续,不要从零重做。
|
|
243
|
-
7. 跑技能脚本用 skill_exec(script 传 skills/<skill>/scripts/<脚本>,args 传参数数组):设计稿前处理管线与 spec_audit / crop_slices / expand_ir / iterate / check_fidelity 等建卡脚本都靠它。${skillAuto ? '本仓库已设「技能脚本免确认」:调用即执行(审计写 .cache/ai-audit.log),不要停下来等用户批准,也不要因为怕要授权而改用 shell_exec 绕路、或把命令甩给用户自己跑。' : '每次调用会先请用户确认(用户可选「本回合不再询问」),被拒就把命令原样写给用户自行运行。'}${shellAllowed ? `\n8. 技能脚本之外的本地命令(npm / node / git / rg / cd 等)用 shell_exec:经真实 shell 执行,管道 |、链接 && / ;、重定向、引号与循环 / 条件 / 子shell 都可用(其中真正执行的每个外部命令需在白名单),不必再为元字符把命令拆开;复制文件用 fs_copy。${shellAuto ? '本仓库已设「本地命令免确认」:调用即执行。' : '每次调用都会弹给用户确认,被拒返回错误。'}cwd
|
|
243
|
+
7. 跑技能脚本用 skill_exec(script 传 skills/<skill>/scripts/<脚本>,args 传参数数组):设计稿前处理管线与 spec_audit / crop_slices / expand_ir / iterate / check_fidelity 等建卡脚本都靠它。${skillAuto ? '本仓库已设「技能脚本免确认」:调用即执行(审计写 .cache/ai-audit.log),不要停下来等用户批准,也不要因为怕要授权而改用 shell_exec 绕路、或把命令甩给用户自己跑。' : '每次调用会先请用户确认(用户可选「本回合不再询问」),被拒就把命令原样写给用户自行运行。'}${shellAllowed ? `\n8. 技能脚本之外的本地命令(npm / node / git / rg / cd 等)用 shell_exec:经真实 shell 执行,管道 |、链接 && / ;、重定向、引号与循环 / 条件 / 子shell 都可用(其中真正执行的每个外部命令需在白名单),不必再为元字符把命令拆开;复制文件用 fs_copy。${shellAuto ? '本仓库已设「本地命令免确认」:调用即执行。' : '每次调用都会弹给用户确认,被拒返回错误。'}cwd 已是工程根(技能脚本按 DSL_CARD_PROJECT_ROOT / cwd 认工程根,不按自身位置推算,落点由服务端注入),不要写 cd;白名单不含 sips / convert / magick / python3 —— 图像裁剪派生一律 crop_slices.mjs,Python 脚本一律 skill_exec,别去试系统图像命令。` : ''}
|
|
244
244
|
|
|
245
245
|
## 设计稿建卡协议(Stage Machine:逐阶段推进,上一阶段产物齐了才进下一阶段;用户挂载设计稿目录 / 图片并要求建卡时严格执行,不要自由发挥)
|
|
246
246
|
S0 路由:输入形态已由服务端机械判定,写在「用户挂载的本地文件」段(kind / preprocess / next):照它执行,不要再调 card_route_input;该段缺失才自己调 card_route_input({ path })。
|
|
@@ -1306,7 +1306,9 @@ async function runShellExec(root, args, res, approved) {
|
|
|
1306
1306
|
)
|
|
1307
1307
|
}
|
|
1308
1308
|
|
|
1309
|
-
/** 技能脚本执行:路径形状校验 → 解释器解析 → 审批(或按策略免确认)→ 跑(cwd
|
|
1309
|
+
/** 技能脚本执行:路径形状校验 → 解释器解析 → 审批(或按策略免确认)→ 跑(cwd=工程根;
|
|
1310
|
+
* 脚本认工程根走 DSL_CARD_PROJECT_ROOT > cwd,**不按自身位置推算** —— 发布态脚本住在包内快照里,
|
|
1311
|
+
* 按自身位置推会把 UI/ 产物写进 node_modules,见 tools/tests/cardbuddy-project-anchor.test.mjs) */
|
|
1310
1312
|
async function runSkillExec(root, args, res, approved) {
|
|
1311
1313
|
const script = resolveSkillScript(root, args.script)
|
|
1312
1314
|
const argv = normalizeSkillArgs(args.args)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { createSyncMiddleware, createPreviewMiddleware, createLoginCredentialsMiddleware } from './cardSync.js'
|
|
14
14
|
import { createSliceUploadMiddleware } from './sliceUpload.js'
|
|
15
|
-
import { createCardFilesMiddleware } from './cardFiles.js'
|
|
15
|
+
import { createCardFilesMiddleware, refreshCardList } from './cardFiles.js'
|
|
16
16
|
import { createSaveMiddleware } from './cardSave.js'
|
|
17
17
|
import { createImportMetaMiddleware } from './cardImportMeta.js'
|
|
18
18
|
import { createHistoryMiddleware } from './cardHistory.js'
|
|
@@ -57,6 +57,12 @@ export {
|
|
|
57
57
|
createCardImageMiddleware,
|
|
58
58
|
createOpenFolderMiddleware,
|
|
59
59
|
createCodeBridgeMiddleware,
|
|
60
|
+
// refreshCardList:standalone / --preview 服务在文件变动后重建卡片列表快照要用它。
|
|
61
|
+
// 必须经本入口导出,**不要**让包外调用方直接 import './cardFiles.js' —— 发布态
|
|
62
|
+
// dist/core.js 被构造成桥到包内快照(./vendored/server/core/index.js),而写死的仓库相对路径
|
|
63
|
+
// 在安装态会解析到包外(0.14.0 的 standalone.js 就因此 ERR_MODULE_NOT_FOUND)。
|
|
64
|
+
// build.mjs 门禁 D 会静态拦住这类「dist 里解析不到的相对引用」。
|
|
65
|
+
refreshCardList,
|
|
60
66
|
createLogMiddleware,
|
|
61
67
|
installLogMirror,
|
|
62
68
|
subscribeLog,
|
|
@@ -52,9 +52,29 @@ export const baselinesDir =
|
|
|
52
52
|
/** 兼容旧脚本的别名:宿主项目根目录。 */
|
|
53
53
|
export const repoRoot = projectRoot
|
|
54
54
|
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* 本 skill 所在的 skills 父目录 —— **按脚本自身位置**推算。
|
|
57
|
+
* 仓库内即 <repo>/skills;发布态快照里是 <pkg>/dist/vendored/skills。
|
|
58
|
+
* 只用于「找不到宿主技能库时的兜底」,别拿它推宿主工程根(见 hostSkillsDir)。
|
|
59
|
+
*/
|
|
56
60
|
export const skillsDir = path.dirname(skillRoot)
|
|
57
61
|
|
|
62
|
+
/**
|
|
63
|
+
* 宿主工程的 skills 根 —— 给用户 / 模型看的命令里该用的那一份。
|
|
64
|
+
*
|
|
65
|
+
* 与 skillsDir 的区别(0.14.1 事故):发布态脚本住在 `<pkg>/dist/vendored/skills/...`,
|
|
66
|
+
* 那不是宿主的技能库;命令里出现 node_modules 路径既是错的、也误导模型去改包内文件。
|
|
67
|
+
* 解析顺序与 server/core/skillPaths.js 的只读候选一致:
|
|
68
|
+
* DSL_SKILLS_ROOT(显式)> <projectRoot>/skills(存在)> 本 skill 所在快照的 skills/(兜底)。
|
|
69
|
+
*/
|
|
70
|
+
export const hostSkillsDir = (() => {
|
|
71
|
+
const explicit = process.env.DSL_SKILLS_ROOT
|
|
72
|
+
if (explicit && fs.existsSync(explicit)) return path.resolve(explicit)
|
|
73
|
+
const inProject = path.join(projectRoot, 'skills')
|
|
74
|
+
if (fs.existsSync(inProject)) return inProject
|
|
75
|
+
return skillsDir
|
|
76
|
+
})()
|
|
77
|
+
|
|
58
78
|
/**
|
|
59
79
|
* 解析卡片目录的绝对路径。
|
|
60
80
|
* @param {string} id 卡片 id
|
|
@@ -64,9 +64,32 @@ baselines_dir = Path(
|
|
|
64
64
|
repo_root = project_root
|
|
65
65
|
|
|
66
66
|
# skills 父目录。
|
|
67
|
+
# 按脚本自身位置推算:仓库内即 <repo>/skills,发布态快照里是 <pkg>/dist/vendored/skills。
|
|
68
|
+
# 只用于「找不到宿主技能库时的兜底」,别拿它推宿主工程根(见 host_skills_dir)。
|
|
67
69
|
skills_dir = skill_root.parent
|
|
68
70
|
|
|
69
71
|
|
|
72
|
+
def _host_skills_dir() -> Path:
|
|
73
|
+
"""
|
|
74
|
+
宿主工程的 skills 根 —— 给用户 / 模型看的命令里该用的那一份。
|
|
75
|
+
|
|
76
|
+
与 skills_dir 的区别(0.14.1 事故):发布态脚本住在 <pkg>/dist/vendored/skills/...,
|
|
77
|
+
那不是宿主的技能库;命令里出现 node_modules 路径既是错的、也误导模型去改包内文件。
|
|
78
|
+
解析顺序与 server/core/skillPaths.js 的只读候选一致:
|
|
79
|
+
DSL_SKILLS_ROOT(显式)> <project_root>/skills(存在)> 本 skill 所在快照的 skills/(兜底)。
|
|
80
|
+
"""
|
|
81
|
+
explicit = os.environ.get("DSL_SKILLS_ROOT")
|
|
82
|
+
if explicit and Path(explicit).is_dir():
|
|
83
|
+
return Path(explicit).resolve()
|
|
84
|
+
in_project = project_root / "skills"
|
|
85
|
+
if in_project.is_dir():
|
|
86
|
+
return in_project
|
|
87
|
+
return skills_dir
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
host_skills_dir = _host_skills_dir()
|
|
91
|
+
|
|
92
|
+
|
|
70
93
|
def card_dir(card_id: str) -> Path:
|
|
71
94
|
"""解析卡片目录的绝对路径:`<cards_dir>/<id>`。"""
|
|
72
95
|
return cards_dir / card_id
|
|
@@ -22,12 +22,12 @@ import fs from 'node:fs'
|
|
|
22
22
|
import path from 'node:path'
|
|
23
23
|
import { spawnSync } from 'node:child_process'
|
|
24
24
|
import { fileURLToPath } from 'node:url'
|
|
25
|
-
import {
|
|
25
|
+
import { hostSkillsDir, projectRoot } from './lib/paths.mjs'
|
|
26
26
|
|
|
27
27
|
const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp'])
|
|
28
28
|
/** 已知入口文件:判定交给它所在目录(index.html=MeaXure 导出;manifest/clusters/spec/prompt=要素包;schema=卡片目录)。 */
|
|
29
29
|
const ENTRY_FILES = new Set(['index.html', 'manifest.json', 'clusters.json', 'spec.json', 'spec.md', 'prompt.md', 'schema.json'])
|
|
30
|
-
const SKETCH_SKILL = path.join(
|
|
30
|
+
const SKETCH_SKILL = path.join(hostSkillsDir, 'sketch-spec-to-card')
|
|
31
31
|
/**
|
|
32
32
|
* 多画板选择门禁(铁律见 references/rules.md §十三):UI 产物允许全量生成,
|
|
33
33
|
* 但卡片生成必须以用户指定的单个画板 / 单个 cardKey 为单位。
|
|
@@ -36,13 +36,18 @@ const SKETCH_SKILL = path.join(skillsDir, 'sketch-spec-to-card')
|
|
|
36
36
|
const SELECTION_GATE =
|
|
37
37
|
'# 多画板门禁(rules.md §十三 + §13.4):用 spec_tree.mjs --list-cards 把全量画板按「画板序号.卡片序号」列出(一个画板含 N 张卡则展开为 K.1 ~ K.N)+ 末尾加「全部」;让用户选的方式 = 正文逐条写清单 + 一句提问后停止输出、等用户回序号再继续(**没有** request_user_input 这个工具,不要去找弹窗,也不要替用户挑);序号格式「画板序号.卡片序号」(如 1.2),单独回画板序号默认取该画板第 1 张卡;UI 产物可回「全部」全量生成,产卡默认单卡片(回「全部」才算显式批量授权);禁止自行挑画板或挑卡、禁止擅自批量产卡;清单逐行配图:每个卡片行下紧跟它自己的 card-N.png 绝对路径,同一画板的卡共享画板预览图(artboard.png)但各有聚焦图;与序号一一对应,缺图显式标缺失,禁止把图堆在清单末尾。'
|
|
38
38
|
/**
|
|
39
|
-
* 要素包根目录 UI
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* 要素包根目录 UI/ —— **锚在宿主工程根,不锚在脚本自身位置**。
|
|
40
|
+
*
|
|
41
|
+
* 曾经的写法(`path.resolve(skillsDir, '..')`)在仓库内恰好等于工程根,怎么跑都对;
|
|
42
|
+
* 可发布态脚本住在 `<pkg>/dist/vendored/skills/...`,往上两级是包内快照目录,于是
|
|
43
|
+
* `card_route_input`(MCP 里直接吃包内快照那份)会把 uiDir 算成
|
|
44
|
+
* `node_modules/cardbuddy/dist/vendored/UI/<稿名>` —— 产物写进 node_modules、重装即丢,
|
|
45
|
+
* 同时把「建议命令」和实际落点一起带偏(0.14.1 的 npx 工程事故)。
|
|
46
|
+
* 工程根一律取 `projectRoot`(= DSL_CARD_PROJECT_ROOT || cwd,与 parse_meaxure.py /
|
|
47
|
+
* build_prompt.py / lib/paths.py 同一契约);可用 DSL_CARD_UI_DIR 直接指定 UI 根。
|
|
42
48
|
*/
|
|
43
|
-
const REPO_ROOT =
|
|
44
|
-
const UI_ROOT =
|
|
45
|
-
process.env.DSL_CARD_UI_DIR || path.join(process.env.DSL_CARD_PROJECT_ROOT || REPO_ROOT, 'UI')
|
|
49
|
+
const REPO_ROOT = projectRoot
|
|
50
|
+
const UI_ROOT = process.env.DSL_CARD_UI_DIR || path.join(REPO_ROOT, 'UI')
|
|
46
51
|
|
|
47
52
|
function exists(target) {
|
|
48
53
|
try {
|
|
@@ -250,8 +255,8 @@ function route(inputAbs) {
|
|
|
250
255
|
skillChain: ['dsl-card-generator'],
|
|
251
256
|
workflow: 'Workflow B — 编辑已有卡片',
|
|
252
257
|
next: [
|
|
253
|
-
`node ${shortPath(path.join(
|
|
254
|
-
`node ${shortPath(path.join(
|
|
258
|
+
`node ${shortPath(path.join(hostSkillsDir, 'dsl-card-generator', 'scripts', 'describe_card.mjs'))} --id ${path.basename(inputAbs)}`,
|
|
259
|
+
`node ${shortPath(path.join(hostSkillsDir, 'dsl-card-generator', 'scripts', 'diff_card.mjs'))} --id ${path.basename(inputAbs)}`,
|
|
255
260
|
],
|
|
256
261
|
note: '输入已是卡片目录,走编辑链路(card_node_models 定位 + add_component 增改 + diff_card 兜底),不需要设计稿前处理。',
|
|
257
262
|
}
|
|
@@ -291,7 +296,7 @@ function route(inputAbs) {
|
|
|
291
296
|
SELECTION_GATE,
|
|
292
297
|
`# 拿到回复后(产物已存在,${done.via},已生成 ${done.generated}/${done.total} 画板),跳过前处理,只消费用户指定的那个画板:`,
|
|
293
298
|
`cat ${shortPath(done.manifest)}`,
|
|
294
|
-
`node ${shortPath(path.join(
|
|
299
|
+
`node ${shortPath(path.join(hostSkillsDir, 'dsl-card-generator', 'scripts', 'spec_tree.mjs'))} --spec ${shortPath(path.join(uiDir, '<画板>', 'spec.json'))}`,
|
|
295
300
|
...(done.alsoFound && done.alsoFound.length
|
|
296
301
|
? [`# 同一导出另有产物目录(目标画板在主 manifest 里是 pending 时先去这些目录找,找到就直接用,别重跑管线):${done.alsoFound.map((m) => `${m.uiDir}(已生成 ${m.generated}/${m.total})`).join('、')}`]
|
|
297
302
|
: []),
|
|
@@ -313,7 +318,7 @@ function route(inputAbs) {
|
|
|
313
318
|
const bundle = detectUiBundle(inputAbs)
|
|
314
319
|
if (bundle.matched) {
|
|
315
320
|
const specHint = bundle.specJson
|
|
316
|
-
? `node ${shortPath(path.join(
|
|
321
|
+
? `node ${shortPath(path.join(hostSkillsDir, 'dsl-card-generator', 'scripts', 'spec_tree.mjs'))} --spec ${shortPath(bundle.specJson)}`
|
|
317
322
|
: `cat ${shortPath(bundle.manifest || inputAbs)}`
|
|
318
323
|
return {
|
|
319
324
|
kind: 'ui-bundle',
|
|
@@ -332,7 +337,7 @@ function route(inputAbs) {
|
|
|
332
337
|
specHint,
|
|
333
338
|
...(bundle.artboardBundle ? [] : [
|
|
334
339
|
`# 先用 --list-cards 列出每个画板下的卡片清单(画板序号.卡片序号),交给用户选:`,
|
|
335
|
-
`node ${shortPath(path.join(
|
|
340
|
+
`node ${shortPath(path.join(hostSkillsDir, 'dsl-card-generator', 'scripts', 'spec_tree.mjs'))} --list-cards ${shortPath(inputAbs)}`,
|
|
336
341
|
SELECTION_GATE,
|
|
337
342
|
]),
|
|
338
343
|
`# 再按 SKILL.md §8 Workflow A 六步闭环生成 schema(scaffold_card → add_component/expand_ir → validate+lint → render_diff)`,
|
|
@@ -24,13 +24,12 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import fs from 'node:fs'
|
|
26
26
|
import path from 'node:path'
|
|
27
|
-
import { fileURLToPath } from 'node:url'
|
|
28
27
|
import { buildContentIndex, checkSliceName, sha256File } from './lib/slice_dedupe.mjs'
|
|
28
|
+
import { cardsDir as CARDS_DIR, projectRoot as REPO_ROOT, slicesDir as SLICES_DIR } from './lib/paths.mjs'
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const CARDS_DIR = path.join(REPO_ROOT, 'src/cards')
|
|
30
|
+
// 工程根一律走 lib/paths.mjs(DSL_CARD_PROJECT_ROOT || cwd)—— 原先按脚本自身位置
|
|
31
|
+
// (`HERE/../../..`)推算,仓库内恰好等于工程根,发布态却会指到 <pkg>/dist/vendored,
|
|
32
|
+
// 于是切图改名 / 卡片引用改写全落到包内快照上(重装即丢)。
|
|
34
33
|
const MANIFEST = path.join(SLICES_DIR, 'manifest.json')
|
|
35
34
|
|
|
36
35
|
/**
|
|
@@ -29,7 +29,17 @@ function findRepoRoot(startDir) {
|
|
|
29
29
|
return path.resolve(__dirname, '..', '..')
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* 宿主工程根:优先显式注入(cardbuddy 的 standalone.js / mcp.js / client.mjs 会按工程根设
|
|
34
|
+
* `DSL_CARD_PROJECT_ROOT`),其次才按自身位置向上找 package.json。
|
|
35
|
+
* 为什么要有第一优先级:发布态本文件住在 `<pkg>/dist/vendored/skills/shared/`,
|
|
36
|
+
* 往上第一个带 package.json 的目录是**包自己**(node_modules/cardbuddy),
|
|
37
|
+
* 于是 .env.cb 会去读包内那份(不存在)→ 静默退化成默认值,消费者工程的
|
|
38
|
+
* FINMALL_BASE_URL / APP_ID / 端口全被忽略。与 route_input.mjs / parse_meaxure.py 同一契约。
|
|
39
|
+
*/
|
|
40
|
+
const PROJECT_ROOT = process.env.DSL_CARD_PROJECT_ROOT
|
|
41
|
+
? path.resolve(process.env.DSL_CARD_PROJECT_ROOT)
|
|
42
|
+
: findRepoRoot(__dirname)
|
|
33
43
|
|
|
34
44
|
/**
|
|
35
45
|
* 解析 .env 文件,返回 key-value 对象。
|
|
@@ -119,7 +119,7 @@ python skills/sketch-spec-to-card/scripts/build_prompt.py --only "未登录"
|
|
|
119
119
|
python skills/sketch-spec-to-card/scripts/cluster_states.py --out <UI目录>
|
|
120
120
|
python skills/sketch-spec-to-card/scripts/cluster_states.py --out <UI目录> --auto-refs # 从 src/cards 动态探测参考卡
|
|
121
121
|
```
|
|
122
|
-
- `--src` 缺省取环境变量 `MEAXURE_SRC`,否则为当前目录;实际请显式传入 `--src`。默认 `--out` =
|
|
122
|
+
- `--src` 缺省取环境变量 `MEAXURE_SRC`,否则为当前目录;实际请显式传入 `--src`。默认 `--out` = **工程根**/`UI`,工程根取 `DSL_CARD_PROJECT_ROOT`、缺省当前工作目录(cwd)—— **不按脚本自身位置推算**:发布态脚本住在 `<pkg>/dist/vendored/skills/…` 里,按自身位置推会把 `UI/` 写进 node_modules(0.14.1 事故;回归见 `tools/tests/cardbuddy-project-anchor.test.mjs`)。
|
|
123
123
|
|
|
124
124
|
## 4. 与 dsl-card-generator 的衔接(边界)
|
|
125
125
|
- **本 skill 只产出要素,不调用 dsl-card-generator**。职责分离:解析/标注/切图归本 skill,建卡 schema 归 dsl-card-generator。
|
|
@@ -12,6 +12,7 @@ build_prompt.py — 由 parse_meaxure.py 的 spec.json(视觉驱动)生成
|
|
|
12
12
|
"""
|
|
13
13
|
import argparse
|
|
14
14
|
import json
|
|
15
|
+
import os
|
|
15
16
|
import re
|
|
16
17
|
import sys
|
|
17
18
|
from collections import Counter
|
|
@@ -19,8 +20,12 @@ from pathlib import Path
|
|
|
19
20
|
|
|
20
21
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
21
22
|
SKILL_DIR = SCRIPT_DIR.parent
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
# 宿主工程根:与 parse_meaxure.py / dsl-card-generator/scripts/lib/paths.py 同一契约
|
|
24
|
+
# (DSL_CARD_PROJECT_ROOT 优先,缺省 cwd)—— **不要**按脚本自身位置往上推,
|
|
25
|
+
# 发布态那是 <pkg>/dist/vendored,会把 UI/ 产物写进 node_modules。
|
|
26
|
+
PROJECT_ROOT = Path(os.environ.get("DSL_CARD_PROJECT_ROOT") or Path.cwd()).resolve()
|
|
27
|
+
REPO_ROOT = PROJECT_ROOT
|
|
28
|
+
DEFAULT_OUT = PROJECT_ROOT / "UI"
|
|
24
29
|
VALIDATE_REL = "skills/dsl-card-generator/scripts/validate_schema.py"
|
|
25
30
|
|
|
26
31
|
# 人力卡学习规则库(与 dsl-card-generator 共享,要素侧只读)
|
|
@@ -46,8 +46,12 @@ from urllib.parse import unquote
|
|
|
46
46
|
|
|
47
47
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
48
48
|
SKILL_DIR = SCRIPT_DIR.parent
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
# 宿主工程根:与 dsl-card-generator/scripts/lib/paths.py 同一契约
|
|
50
|
+
# (DSL_CARD_PROJECT_ROOT 优先,缺省 cwd)—— **不要**按脚本自身位置往上推。
|
|
51
|
+
# 发布态脚本住在 <pkg>/dist/vendored/skills/sketch-spec-to-card/scripts/,
|
|
52
|
+
# 往上两级是包内快照目录,UI/ 产物会被写进 node_modules(重装即丢、多项目互相污染)。
|
|
53
|
+
PROJECT_ROOT = Path(os.environ.get("DSL_CARD_PROJECT_ROOT") or Path.cwd()).resolve()
|
|
54
|
+
DEFAULT_OUT = PROJECT_ROOT / "UI"
|
|
51
55
|
DEFAULT_SRC = Path(os.environ.get('MEAXURE_SRC') or str(Path.cwd())) # 可用环境变量 MEAXURE_SRC 指定,缺省当前目录
|
|
52
56
|
|
|
53
57
|
sys.path.insert(0, str(SCRIPT_DIR))
|
|
@@ -1724,12 +1728,15 @@ def main():
|
|
|
1724
1728
|
cdir.mkdir(parents=True, exist_ok=True)
|
|
1725
1729
|
|
|
1726
1730
|
# 整图截图(参考)
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
+
# imagePath 缺失时 `src / ""` 会等于**导出目录本身**:exists() 为真 → 下面按画板名兜底的
|
|
1732
|
+
# glob 永远不跑,copy2(目录, 文件) 直接抛 IsADirectoryError(整份管线在写 artboard.png 时崩)。
|
|
1733
|
+
# 故必须显式判「非空且是文件」,让兜底真正生效。
|
|
1734
|
+
preview_rel = unquote(ab.get("imagePath", "") or "")
|
|
1735
|
+
src_png = (src / preview_rel) if preview_rel else None
|
|
1736
|
+
if not (src_png and src_png.is_file()):
|
|
1737
|
+
cand = [c for c in src.glob(f"preview/*{name.split('_', 1)[-1]}*.png") if c.is_file()]
|
|
1731
1738
|
src_png = cand[0] if cand else None
|
|
1732
|
-
if src_png
|
|
1739
|
+
if src_png:
|
|
1733
1740
|
shutil.copy2(src_png, cdir / "artboard.png")
|
|
1734
1741
|
entry["preview"] = f"{name}/artboard.png"
|
|
1735
1742
|
# —— P1 优化(RC5):整屏 artboard.png 降采样,降低多模态 token 成本 ——
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cardbuddy",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "Local dev service for the CardBuddy card editor — decouples card CRUD (sync/save/validate/render-status/screenshot) from the static public editor, so the editor can be deployed to any static host while each developer runs a local service against their own card source.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"cardbuddy": "
|
|
7
|
+
"cardbuddy": "dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"start": "node src/cli.js",
|
|
21
21
|
"build": "node build.mjs",
|
|
22
|
-
"prepublishOnly": "node build.mjs"
|
|
22
|
+
"prepublishOnly": "node build.mjs && node publishSim.mjs"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ckpack/vue-color": "^1.6.0",
|