cardbuddy 0.14.3 → 0.16.0
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/cli.js +0 -0
- package/dist/preview-editor/assets/index-BkbBxDTT.css +1 -0
- package/dist/preview-editor/assets/index-Bro07DTa.js +313 -0
- package/dist/preview-editor/index.html +2 -2
- package/dist/standalone.js +3 -0
- package/dist/vendored/card-editor/runtime/adapter.js +2 -2
- package/dist/vendored/mcp/client.mjs +9 -2
- package/dist/vendored/server/core/ai-chat.js +1702 -134
- package/dist/vendored/server/core/browser.js +12 -3
- package/dist/vendored/server/core/cardFiles.js +3 -0
- package/dist/vendored/server/core/cardHistory.js +1 -0
- package/dist/vendored/server/core/cardImportMeta.js +7 -6
- package/dist/vendored/server/core/cardOpenFolder.js +1 -0
- package/dist/vendored/server/core/cardSave.js +1 -0
- package/dist/vendored/server/core/cardSync.js +3 -0
- package/dist/vendored/server/core/cardValidate.js +16 -5
- package/dist/vendored/server/core/codeBridge.js +5 -1
- package/dist/vendored/server/core/logStream.js +1 -0
- package/dist/vendored/server/core/sliceStatic.js +24 -4
- package/dist/vendored/server/core/sliceUpload.js +1 -0
- package/dist/vendored/server/core/turnJournal.js +6 -0
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +257 -201
- package/dist/vendored/skills/card-skill-trainer/scripts/diagnose_case.mjs +14 -82
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/diagnosis.mjs +207 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/layout-correction-sim.mjs +62 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/paradigm-router.mjs +76 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/pixel-worker-runner.mjs +31 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/pixel-worker.mjs +138 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/scoring.mjs +24 -20
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/telemetry.mjs +46 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/plan_next_round.mjs +4 -1
- package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +279 -90
- package/dist/vendored/skills/card-skill-trainer/scripts/score_round.mjs +74 -2
- package/dist/vendored/skills/card-skill-trainer/scripts/sink_rules.mjs +127 -0
- package/dist/vendored/skills/dsl-card-ai/SKILL.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/SKILL.md +208 -380
- package/dist/vendored/skills/dsl-card-generator/references/dsl_structure.md +1 -15
- package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +11 -20
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +233 -61
- package/dist/vendored/skills/dsl-card-generator/references/ir-format.md +202 -4
- package/dist/vendored/skills/dsl-card-generator/references/iteration_protocol.md +87 -0
- package/dist/vendored/skills/dsl-card-generator/references/previewer_contract.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/references/quickref.md +42 -33
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/title-underline-overlay.json +59 -11
- package/dist/vendored/skills/dsl-card-generator/references/recipes.md +44 -8
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +26 -6
- package/dist/vendored/skills/dsl-card-generator/references/scripts_reference.md +54 -0
- package/dist/vendored/skills/dsl-card-generator/references/semantic_adjudication.md +23 -0
- package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +6 -6
- package/dist/vendored/skills/dsl-card-generator/references/spec_to_schema_patterns.md +33 -4
- package/dist/vendored/skills/dsl-card-generator/references/ui_fidelity_rules.md +13 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/add_component.mjs +5 -14
- package/dist/vendored/skills/dsl-card-generator/scripts/autofix_schema.py +133 -2
- package/dist/vendored/skills/dsl-card-generator/scripts/check_fidelity.mjs +226 -19
- package/dist/vendored/skills/dsl-card-generator/scripts/check_magic_values.mjs +6 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/check_topology.py +0 -35
- package/dist/vendored/skills/dsl-card-generator/scripts/crop_slices.mjs +92 -6
- package/dist/vendored/skills/dsl-card-generator/scripts/expand_ir.mjs +277 -27
- package/dist/vendored/skills/dsl-card-generator/scripts/gate.mjs +147 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/generate_cards.mjs +12 -8
- package/dist/vendored/skills/dsl-card-generator/scripts/ir_merge.mjs +288 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/ir_restore.mjs +175 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +1015 -54
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/component_contract.mjs +315 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/geometry_audit.mjs +131 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout-tokens.mjs +1 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout_rules.mjs +160 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +23 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.py +0 -37
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/python.mjs +99 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/adaptive-width.mjs +105 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/bindings.mjs +260 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/expr.mjs +52 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/image.mjs +96 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/registry.mjs +166 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/structure.mjs +127 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/rules/walk.mjs +61 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/scope_alias.mjs +125 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slicePath.mjs +17 -5
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +102 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_assemble.mjs +5727 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_extract.mjs +4214 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/v3_shared.mjs +1110 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +322 -71
- package/dist/vendored/skills/dsl-card-generator/scripts/preflight.mjs +87 -43
- package/dist/vendored/skills/dsl-card-generator/scripts/readiness.mjs +634 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/register_card_template.mjs +5 -8
- package/dist/vendored/skills/dsl-card-generator/scripts/render_card.mjs +91 -43
- package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +7 -11
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_audit.mjs +272 -46
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_to_schema_v3.mjs +43 -10499
- package/dist/vendored/skills/dsl-card-generator/scripts/v3_regression.mjs +188 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/validate_schema.py +23 -15
- package/dist/vendored/skills/shared/exit-codes.md +79 -0
- package/dist/vendored/skills/shared/spec-contract.md +118 -0
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +43 -20
- package/dist/vendored/skills/sketch-spec-to-card/references/spec-schema.md +4 -4
- package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +47 -9
- package/dist/vendored/skills/sketch-spec-to-card/scripts/cluster_states.py +14 -25
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +546 -91
- package/dist/vendored/skills/sketch-spec-to-card/scripts/png_utils.py +51 -5
- package/dist/vendored/skills/sketch-spec-to-card/scripts/test_text_only_artboard.py +217 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +64 -34
- package/package.json +1 -1
- package/dist/preview-editor/assets/index-Cm0USikc.js +0 -307
- package/dist/preview-editor/assets/index-cbTCHp8J.css +0 -1
- package/dist/vendored/skills/card-skill-trainer/scripts/test_missing_slice.py +0 -122
- package/dist/vendored/skills/dsl-card-generator/scripts/analyze_component_usage.mjs +0 -153
|
@@ -83,8 +83,51 @@ summary.correctorRejected = correctorRejected
|
|
|
83
83
|
summary.correctorCoverage = reports.length ? correctorAttempts / reports.length : null
|
|
84
84
|
summary.correctorAcceptRate = correctorAttempts ? correctorAccepted / correctorAttempts : null
|
|
85
85
|
|
|
86
|
-
// ---
|
|
87
|
-
//
|
|
86
|
+
// --- Telemetry 聚合:速度 / 错误 / token(2026-09-25)---
|
|
87
|
+
// 旧报告缺 durationMs / telemetry.json 时对应段自动跳过,跨轮兼容;口径不进任何门禁。
|
|
88
|
+
const durations = reports.map((item) => item.durationMs).filter((v) => typeof v === 'number')
|
|
89
|
+
if (durations.length) {
|
|
90
|
+
const sorted = [...durations].sort((a, b) => a - b)
|
|
91
|
+
// nearest-rank 百分位:ceil(p*n)-1(P95 of [1.5s,3.5s] 应取 3.5s,floor 秩会错误取到 1.5s)
|
|
92
|
+
const pct = (p) => sorted[Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1))]
|
|
93
|
+
summary.avgCaseMs = Math.round(average(durations))
|
|
94
|
+
summary.p50CaseMs = pct(0.5)
|
|
95
|
+
summary.p95CaseMs = pct(0.95)
|
|
96
|
+
}
|
|
97
|
+
const telemetryPath = path.join(reportDir, 'telemetry.json')
|
|
98
|
+
if (fs.existsSync(telemetryPath)) {
|
|
99
|
+
try {
|
|
100
|
+
const t = JSON.parse(fs.readFileSync(telemetryPath, 'utf8'))
|
|
101
|
+
// 旧 telemetry.json 可能缺派生字段:从原始累计字段现算兜底
|
|
102
|
+
const derivedCpm = (t.wallMs > 0 && t.scoredCases)
|
|
103
|
+
? Number((t.scoredCases / (t.wallMs / 60000)).toFixed(2)) : null
|
|
104
|
+
const derivedStageAverages = !t.stageAverages && t.stageMs && t.scoredCases
|
|
105
|
+
? Object.fromEntries(Object.entries(t.stageMs)
|
|
106
|
+
.map(([stage, ms]) => [stage, Math.round(ms / t.scoredCases)]))
|
|
107
|
+
: null
|
|
108
|
+
summary.telemetry = {
|
|
109
|
+
wallMs: t.wallMs ?? null,
|
|
110
|
+
concurrency: t.concurrency ?? null,
|
|
111
|
+
casesPerMinute: t.casesPerMinute ?? derivedCpm,
|
|
112
|
+
stageAverages: t.stageAverages ?? derivedStageAverages,
|
|
113
|
+
renderCalls: t.renderCalls ?? null,
|
|
114
|
+
renderRetries: t.renderRetries ?? null,
|
|
115
|
+
sessionRebuilds: t.sessionRebuilds ?? null,
|
|
116
|
+
stageErrors: t.stageErrors ?? null,
|
|
117
|
+
caseErrors: t.caseErrors ?? null,
|
|
118
|
+
usage: t.usage ?? null,
|
|
119
|
+
reviewTokensEst: t.reviewTokensEst?.total != null
|
|
120
|
+
? {
|
|
121
|
+
...t.reviewTokensEst,
|
|
122
|
+
avgPerFailedCase: t.reviewTokensEst.cases
|
|
123
|
+
? Math.round(t.reviewTokensEst.total / t.reviewTokensEst.cases) : 0,
|
|
124
|
+
}
|
|
125
|
+
: null,
|
|
126
|
+
}
|
|
127
|
+
} catch { /* telemetry.json 损坏不阻塞汇总 */ }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// --- 失败签名 / 未达标维度直方图 / 失败类分布 ---// 签名口径 v2(lib/scoring.mjs):取归一化缺口最大的维度,而非固定优先级顺序。
|
|
88
131
|
// 旧口径会把 comp=0.42 / pos=0.42 的缺组件卡报成 duplicate-content-detected(重影只超 1 处),
|
|
89
132
|
// 把下一轮修复方向带偏;跨轮签名趋势对比请以口径 v2 之后的轮次为界。
|
|
90
133
|
const signatures = {}
|
|
@@ -175,6 +218,35 @@ if (summary.highMaskCases.length) {
|
|
|
175
218
|
}
|
|
176
219
|
} else lines.push('- (无)')
|
|
177
220
|
|
|
221
|
+
// —— 速度与消耗(telemetry,2026-09-25)——
|
|
222
|
+
lines.push('', '## 速度与消耗', '')
|
|
223
|
+
if (summary.avgCaseMs != null) {
|
|
224
|
+
lines.push(`- 单例耗时: 均值 ${(summary.avgCaseMs / 1000).toFixed(1)}s · P50 ${(summary.p50CaseMs / 1000).toFixed(1)}s · P95 ${(summary.p95CaseMs / 1000).toFixed(1)}s`)
|
|
225
|
+
}
|
|
226
|
+
if (summary.telemetry) {
|
|
227
|
+
const t = summary.telemetry
|
|
228
|
+
lines.push(
|
|
229
|
+
`- 墙钟: ${t.wallMs != null ? `${(t.wallMs / 1000).toFixed(1)}s` : 'N/A'}${t.concurrency != null ? `(${t.concurrency} 并发渲染)` : ''}`,
|
|
230
|
+
`- 吞吐: ${t.casesPerMinute != null ? `${t.casesPerMinute} 例/分钟` : 'N/A'}${t.renderCalls != null ? ` · 渲染 ${t.renderCalls} 次` : ''}${t.renderRetries ? ` · 重试 ${t.renderRetries}` : ''}${t.sessionRebuilds ? ` · 会话重建 ${t.sessionRebuilds}` : ''}`,
|
|
231
|
+
)
|
|
232
|
+
if (t.stageAverages && Object.keys(t.stageAverages).length) {
|
|
233
|
+
const stageStr = Object.entries(t.stageAverages)
|
|
234
|
+
.sort((a, b) => b[1] - a[1])
|
|
235
|
+
.map(([stage, ms]) => `${stage} ${(ms / 1000).toFixed(2)}s`)
|
|
236
|
+
.join(' · ')
|
|
237
|
+
lines.push(`- 阶段均值(按案例): ${stageStr}`)
|
|
238
|
+
}
|
|
239
|
+
if (t.stageErrors && Object.keys(t.stageErrors).length) {
|
|
240
|
+
lines.push(`- 阶段错误: ${Object.entries(t.stageErrors).map(([stage, n]) => `${stage}×${n}`).join(' · ')}`)
|
|
241
|
+
}
|
|
242
|
+
if (t.usage) lines.push(`- LLM usage(生成链路): ${t.usage.calls} 次 / in ${t.usage.inputTokens} / out ${t.usage.outputTokens} tok`)
|
|
243
|
+
if (t.reviewTokensEst) {
|
|
244
|
+
lines.push(`- 归因证据包估算: ${t.reviewTokensEst.total} tok(${t.reviewTokensEst.cases} 个失败例 · 均 ${t.reviewTokensEst.avgPerFailedCase} tok)— Agent 在环归因预算参考`)
|
|
245
|
+
}
|
|
246
|
+
} else if (summary.avgCaseMs == null) {
|
|
247
|
+
lines.push('- (无 telemetry 数据:旧轮次报告或未启用计时)')
|
|
248
|
+
}
|
|
249
|
+
|
|
178
250
|
// —— 逐案截图对比图廊 ——
|
|
179
251
|
lines.push('', '## 截图对比', '')
|
|
180
252
|
const sortedReports = [...reports].sort((a, b) =>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sink_rules.mjs — 把 lessons.jsonl 的已推广规则回灌到目标 skill 文档。
|
|
4
|
+
*
|
|
5
|
+
* 训练闭环的最后一环:lesson 标了 status=promoted + targetFiles 指向
|
|
6
|
+
* dsl-card-generator / sketch-spec-to-card / card-skill-trainer 的规则文档
|
|
7
|
+
* (generation_pitfalls.md / SKILL.md / recipes.md 等),但规则文本从未真正
|
|
8
|
+
* 写进这些文件——本脚本补上这一步。
|
|
9
|
+
*
|
|
10
|
+
* 规则:
|
|
11
|
+
* 1. 读 lessons.jsonl,筛 status=promoted(或 candidate 且有 rule 文本)
|
|
12
|
+
* 2. targetFiles 含 .md 的 lesson,检查 rule 文本是否已在目标文件中
|
|
13
|
+
* 3. 不在 → 按目标文件的节号约定追加新节(generation_pitfalls 用 ## N.,
|
|
14
|
+
* SKILL.md / recipes.md 用 ### 训练规则 N)
|
|
15
|
+
* 4. 已在 → 跳过(幂等)
|
|
16
|
+
*
|
|
17
|
+
* 用法:
|
|
18
|
+
* node skills/card-skill-trainer/scripts/sink_rules.mjs [--dry-run]
|
|
19
|
+
*
|
|
20
|
+
* 退出码:0 = 正常(即使无新规则也 0);1 = 写入失败。
|
|
21
|
+
*/
|
|
22
|
+
import fs from 'node:fs'
|
|
23
|
+
import path from 'node:path'
|
|
24
|
+
import { fileURLToPath } from 'node:url'
|
|
25
|
+
|
|
26
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..')
|
|
27
|
+
const LESSONS = path.join(ROOT, 'training', 'lessons', 'lessons.jsonl')
|
|
28
|
+
|
|
29
|
+
const dryRun = process.argv.includes('--dry-run')
|
|
30
|
+
|
|
31
|
+
function loadLessons() {
|
|
32
|
+
if (!fs.existsSync(LESSONS)) return []
|
|
33
|
+
return fs.readFileSync(LESSONS, 'utf8')
|
|
34
|
+
.split('\n').filter((l) => l.trim())
|
|
35
|
+
.map((l) => { try { return JSON.parse(l) } catch { return null } })
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 规则文本是否已存在于目标文件(归一化后前 40 字符片段匹配) */
|
|
40
|
+
function ruleAlreadyInFile(filePath, rule) {
|
|
41
|
+
if (!fs.existsSync(filePath)) return false
|
|
42
|
+
const content = fs.readFileSync(filePath, 'utf8').replace(/\s+/g, '')
|
|
43
|
+
const snippet = (rule || '').replace(/\s+/g, '').slice(0, 40)
|
|
44
|
+
return snippet.length >= 10 && content.includes(snippet)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 按目标文件分组待回灌的 lessons */
|
|
48
|
+
function groupByTargetFile(lessons) {
|
|
49
|
+
const groups = new Map()
|
|
50
|
+
for (const d of lessons) {
|
|
51
|
+
if (!d.targetFiles || !d.rule) continue
|
|
52
|
+
// 只回灌 status=promoted 或有实质 rule 的 candidate
|
|
53
|
+
if (d.status && d.status !== 'promoted' && d.status !== 'candidate') continue
|
|
54
|
+
// candidate 但 rule 还是 TODO 占位 → 跳过
|
|
55
|
+
if (d.rule.includes('TODO')) continue
|
|
56
|
+
for (const tf of d.targetFiles) {
|
|
57
|
+
if (!tf.endsWith('.md')) continue
|
|
58
|
+
const fullPath = path.resolve(ROOT, tf)
|
|
59
|
+
if (ruleAlreadyInFile(fullPath, d.rule)) continue
|
|
60
|
+
if (!groups.has(tf)) groups.set(tf, [])
|
|
61
|
+
groups.get(tf).push(d)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return groups
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 生成回灌的 markdown 片段 */
|
|
68
|
+
function buildSection(lessons, filePath) {
|
|
69
|
+
const isPitfalls = filePath.includes('generation_pitfalls')
|
|
70
|
+
const lines = []
|
|
71
|
+
let n = 0
|
|
72
|
+
for (const d of lessons) {
|
|
73
|
+
const date = d.round || d.lessonId || ''
|
|
74
|
+
const sig = d.signature || ''
|
|
75
|
+
const id = d.lessonId || d.id || ''
|
|
76
|
+
n++
|
|
77
|
+
if (isPitfalls) {
|
|
78
|
+
lines.push(`\n## auto. ${sig.slice(0, 60) || '训练规则'}${date ? `(${date} 训练回灌)` : ''}`)
|
|
79
|
+
} else {
|
|
80
|
+
lines.push(`\n---\n### ${sig.slice(0, 60) || '训练规则'}${date ? `(${date} 训练回灌)` : ''}`)
|
|
81
|
+
}
|
|
82
|
+
if (id) lines.push(`> lesson: \`${id}\` status: \`${d.status || 'promoted'}\``)
|
|
83
|
+
lines.push('')
|
|
84
|
+
lines.push(d.rule.trim())
|
|
85
|
+
lines.push('')
|
|
86
|
+
}
|
|
87
|
+
return { text: lines.join('\n'), count: n }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function main() {
|
|
91
|
+
const lessons = loadLessons()
|
|
92
|
+
if (!lessons.length) {
|
|
93
|
+
console.log('[sink_rules] 无 lessons.jsonl 或为空,跳过')
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const groups = groupByTargetFile(lessons)
|
|
98
|
+
if (!groups.size) {
|
|
99
|
+
console.log('[sink_rules] 所有规则已回灌或无 .md 目标,无需操作')
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let totalSunk = 0
|
|
104
|
+
for (const [tf, fileLessons] of groups) {
|
|
105
|
+
const fullPath = path.resolve(ROOT, tf)
|
|
106
|
+
if (!fs.existsSync(fullPath)) {
|
|
107
|
+
console.warn(`[sink_rules] 目标文件不存在: ${tf},跳过`)
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
const content = fs.readFileSync(fullPath, 'utf8')
|
|
111
|
+
const isPitfalls = tf.includes('generation_pitfalls')
|
|
112
|
+
const { text, count } = buildSection(fileLessons, tf)
|
|
113
|
+
|
|
114
|
+
if (dryRun) {
|
|
115
|
+
console.log(`[sink_rules] DRY-RUN: 将向 ${tf} 追加 ${count} 条规则`)
|
|
116
|
+
} else {
|
|
117
|
+
const append = content.endsWith('\n') ? text : '\n' + text
|
|
118
|
+
fs.appendFileSync(fullPath, append + '\n')
|
|
119
|
+
console.log(`[sink_rules] ${tf}: 回灌 ${count} 条规则`)
|
|
120
|
+
}
|
|
121
|
+
totalSunk += count
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(`[sink_rules] 共回灌 ${totalSunk} 条规则到 ${groups.size} 个文件${dryRun ? '(dry-run,未实际写入)' : ''}`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
main()
|
|
@@ -218,7 +218,7 @@ node skills/dsl-card-generator/scripts/describe_card.mjs --id <cardId> --json
|
|
|
218
218
|
| `card-dir` | 已存在卡片目录(含 `schema.json`) | 不是建卡是改卡 → 转本技能 Workflow A(修绑定)/ Workflow B(批量改) |
|
|
219
219
|
| `unknown` | 判不出形态(`ok:false`) | **不要猜**:向用户确认输入形态,或让其显式给出 `UI/<画板>/spec.json` 与 `card-N.png` |
|
|
220
220
|
|
|
221
|
-
> **多画板选择门禁(铁律 §十三)**:`meaxure-export` / `ui-bundle` 判定为多画板输入时,**UI 产物可全量生成**(上游前处理一次跑完整份导出),但**产卡必须以单个画板 / 单个 cardKey 为单位**。用户未指定目标画板时:先机械取全量画板(未前处理 → `parse_meaxure.py --src <导出目录> --list`,展示 1 起;已前处理 → `manifest.json` 的 `canvases[]` + `clusters.json` 的 `cards[]`),按 **`1..n` 编号列出 + 末尾追加 `全部`**;**没有弹选项工具**(不存在 `request_user_input`,**不要去调用或寻找**),一律在正文列出清单 + 一句纯文本提问后**停止输出、等用户回序号**再继续。**UI 产物可回 `全部` 全量生成**;**产卡默认单画板 / 单 cardKey**,用户回 `全部` 才视为显式批量授权——**禁止**自行挑画板、禁止未经同意批量 `card_write`。系统级选择弹窗不可实现,「正文列清单 /
|
|
221
|
+
> **多画板选择门禁(铁律 §十三)**:`meaxure-export` / `ui-bundle` 判定为多画板输入时,**UI 产物可全量生成**(上游前处理一次跑完整份导出),但**产卡必须以单个画板 / 单个 cardKey 为单位**。用户未指定目标画板时:先机械取全量画板(未前处理 → `parse_meaxure.py --src <导出目录> --list`,展示 1 起;已前处理 → `manifest.json` 的 `canvases[]` + `clusters.json` 的 `cards[]`),按 **`1..n` 编号列出 + 末尾追加 `全部`**;**没有弹选项工具**(不存在 `request_user_input`,**不要去调用或寻找**),一律在正文列出清单 + 一句纯文本提问后**停止输出、等用户回序号**再继续。**UI 产物可回 `全部` 全量生成**;**产卡默认单画板 / 单 cardKey**,用户回 `全部` 才视为显式批量授权——**禁止**自行挑画板、禁止未经同意批量 `card_write`。系统级选择弹窗不可实现,「正文列清单 / 停下等序号」即等价物;清单**逐行配图**(每画板行下紧跟它自己的预览图绝对路径,缺图显式标缺失,禁止把图堆在清单末尾)——**路径照抄完整绝对路径:禁止用 `…` / `...` 省略前缀、禁止包进反引号或代码块**(缩写的路径渲染端取不到图),转给用户时逐条内联 Markdown 图片看图选。
|
|
222
222
|
|
|
223
223
|
### 6.3 交接生成与收口
|
|
224
224
|
|