cardbuddy 0.11.0 → 0.12.1
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/README.md +46 -5
- package/dist/cli.js +51 -1
- package/dist/config.js +51 -16
- package/dist/core.js +2 -2
- package/dist/init.js +425 -0
- package/dist/preview-editor/assets/index-CHuGGIR3.js +276 -0
- package/dist/preview-editor/assets/{index-B4VZgNSK.css → index-CQScMhyg.css} +1 -1
- package/dist/preview-editor/index.html +2 -2
- package/dist/standalone.js +8 -0
- package/dist/vendored/mcp/client.mjs +537 -0
- package/dist/vendored/mcp/tools.mjs +268 -0
- package/dist/vendored/preview/assets/default-image.png +0 -0
- package/dist/{server-core → vendored/server/core}/ai-chat.js +82 -38
- package/dist/{server-core → vendored/server/core}/browser.js +1 -1
- package/dist/{server-core → vendored/server/core}/cardSave.js +8 -3
- package/dist/{server-core → vendored/server/core}/cardSync.js +6 -14
- package/dist/{server-core → vendored/server/core}/cardValidate.js +11 -8
- package/dist/{server-core → vendored/server/core}/index.js +5 -3
- package/dist/vendored/server/core/skillPaths.js +124 -0
- package/dist/{server-core → vendored/server/core}/sliceUpload.js +5 -8
- package/dist/vendored/skills/README.md +93 -0
- package/dist/vendored/skills/card-doctor/SKILL.md +52 -0
- package/dist/vendored/skills/card-skill-trainer/SKILL.md +312 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r038n.json +43 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r039n.json +47 -0
- package/dist/vendored/skills/card-skill-trainer/baselines/v3-r050n.json +48 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/build_dataset.py +472 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/build_sample.mjs +162 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/diagnose_case.mjs +241 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/lib/scoring.mjs +525 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/round_diff.mjs +133 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/run_round.mjs +1043 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/score_round.mjs +195 -0
- package/dist/vendored/skills/card-skill-trainer/scripts/test_missing_slice.py +122 -0
- package/dist/vendored/skills/dsl-card-ai/SKILL.md +191 -0
- package/dist/vendored/skills/dsl-card-generator/SKILL.md +480 -0
- package/dist/vendored/skills/dsl-card-generator/config/render-contract.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/amount_formatting.md +138 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_index.json +1597 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_templates.json +384 -0
- package/dist/vendored/skills/dsl-card-generator/references/card_to_summary.md +56 -0
- package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +76 -0
- package/dist/vendored/skills/dsl-card-generator/references/component_specs.md +393 -0
- package/dist/vendored/skills/dsl-card-generator/references/dsl_structure.md +208 -0
- package/dist/vendored/skills/dsl-card-generator/references/event_patterns.md +287 -0
- package/dist/vendored/skills/dsl-card-generator/references/expression_engine.md +121 -0
- package/dist/vendored/skills/dsl-card-generator/references/field_name_dict.json +34 -0
- package/dist/vendored/skills/dsl-card-generator/references/field_naming.md +101 -0
- package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +928 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/README.md +69 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/examples_index.json +2967 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/patterns.json +2835 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2.json +648 -0
- package/dist/vendored/skills/dsl-card-generator/references/handcrafted/recipes_v2_schema.md +295 -0
- package/dist/vendored/skills/dsl-card-generator/references/image_diff.md +48 -0
- package/dist/vendored/skills/dsl-card-generator/references/image_to_card.md +129 -0
- package/dist/vendored/skills/dsl-card-generator/references/ir-format.md +212 -0
- package/dist/vendored/skills/dsl-card-generator/references/message-center-pattern.md +149 -0
- package/dist/vendored/skills/dsl-card-generator/references/platform_jsapi.md +196 -0
- package/dist/vendored/skills/dsl-card-generator/references/previewer_contract.md +54 -0
- package/dist/vendored/skills/dsl-card-generator/references/quickref.md +197 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/absolute-badge.json +84 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/accordion-group-toggle.json +18 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertCard-pair.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/alertDialog-call.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-countdown.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-donut.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-gauge-score.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-radar.json +10 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/canvas-title-head.json +13 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/globalSet.json +12 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/label-col-modes.json +21 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/list-expand-collapse.json +17 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/money-format.json +11 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/opacity-switch.json +130 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/progress-bar.json +25 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/tab-billMode.json +15 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipe_snippets/title-underline-overlay.json +21 -0
- package/dist/vendored/skills/dsl-card-generator/references/recipes.md +797 -0
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +235 -0
- package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +177 -0
- package/dist/vendored/skills/dsl-card-generator/references/spec_to_schema_patterns.md +609 -0
- package/dist/vendored/skills/dsl-card-generator/references/ui_fidelity_rules.md +131 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/add_component.mjs +118 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/analyze_component_usage.mjs +153 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/apply_opacity_pattern.mjs +343 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/build_card_index.mjs +108 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/build_card_templates.mjs +190 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_fidelity.mjs +257 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/check_magic_values.mjs +79 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/crop_slices.mjs +98 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/describe_card.mjs +265 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/diff_bands.mjs +257 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/diff_card.mjs +316 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/expand_ir.mjs +873 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/extract_handcrafted.py +1199 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/extract_subtree.mjs +191 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/generate_cards.mjs +389 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/inventory_schemas.py +160 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/iterate.mjs +700 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_image.mjs +56 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/design_sample.mjs +328 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/layout-tokens.mjs +79 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/paths.mjs +103 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +575 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slicePath.mjs +81 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +147 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_autofix.mjs +363 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +1399 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/preflight.mjs +249 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/register_card_template.mjs +208 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_baseline.mjs +231 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_card.mjs +801 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/render_diff.mjs +239 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/resolve_card_template.mjs +73 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/route_input.mjs +447 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/scaffold_card.mjs +81 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/slices_normalize.mjs +316 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_audit.mjs +462 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_to_schema_v3.mjs +7735 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/spec_tree.mjs +355 -0
- package/dist/vendored/skills/dsl-card-generator/scripts/validate_all_schemas.py +71 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/default-image-wzZYQ86K.png +0 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-C9k-07y7.css +1 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-oFRM4f4V.js +55 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/svg-symbols-B2exBrAr.svg +118 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/component-meta.json +2714 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/directive-meta.json +40 -0
- package/dist/vendored/skills/dsl-card-generator/vendor/preview/meta/style-meta.json +1086 -0
- package/dist/vendored/{standalone.html → skills/dsl-card-generator/vendor/preview/standalone.html} +2 -1
- package/dist/vendored/skills/finmall-card-sync/SKILL.md +262 -0
- package/dist/vendored/skills/finmall-session-navigator/SKILL.md +169 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/finmall_login.mjs +882 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/package-lock.json +59 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/package.json +10 -0
- package/dist/vendored/skills/finmall-session-navigator/scripts/session-manager.mjs +96 -0
- package/dist/vendored/skills/shared/README.md +55 -0
- package/dist/vendored/skills/shared/auth-model.md +79 -0
- package/dist/vendored/skills/sketch-spec-to-card/SKILL.md +329 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/boundary.json +14 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/card_refs.json +13 -0
- package/dist/vendored/skills/sketch-spec-to-card/references/spec-schema.md +103 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/build_prompt.py +797 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/card_refs.py +94 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/cluster_states.py +706 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/parse_meaxure.py +1781 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/png_utils.py +318 -0
- package/dist/vendored/skills/sketch-spec-to-card/scripts/vision.py +1118 -0
- package/dist/vendored/templates/helloCard/data.json +10 -0
- package/dist/vendored/templates/helloCard/schema.json +238 -0
- package/dist/vendored/tools/link-skills.mjs +485 -0
- package/package.json +2 -1
- package/dist/preview-editor/assets/index-jl4YJzKv.js +0 -180
- package/dist/vendored/App.vue +0 -2858
- package/dist/vendored/ansi.js +0 -185
- package/dist/vendored/assets/apple-touch-icon.png +0 -0
- package/dist/vendored/assets/default-image.png +0 -0
- package/dist/vendored/assets/favicon-16.png +0 -0
- package/dist/vendored/assets/favicon-32.png +0 -0
- package/dist/vendored/assets/site-icon.png +0 -0
- package/dist/vendored/assets/svgIcons/icons.svg +0 -87
- package/dist/vendored/assets/svgIcons/index.js +0 -39
- package/dist/vendored/components/AiSettings.vue +0 -392
- package/dist/vendored/components/AlertCardInstance.vue +0 -58
- package/dist/vendored/components/AlertCardLayer.vue +0 -59
- package/dist/vendored/components/ApiCallPanel.vue +0 -127
- package/dist/vendored/components/ApiPayloadTree.vue +0 -201
- package/dist/vendored/components/AppToast.vue +0 -37
- package/dist/vendored/components/CardGallery.vue +0 -291
- package/dist/vendored/components/CardPreview.vue +0 -72
- package/dist/vendored/components/CardSelect.vue +0 -206
- package/dist/vendored/components/ChatComposer.vue +0 -413
- package/dist/vendored/components/ChatMessage.vue +0 -383
- package/dist/vendored/components/ChatPanel.vue +0 -937
- package/dist/vendored/components/ComponentPalette.vue +0 -301
- package/dist/vendored/components/ConfirmDialog.vue +0 -54
- package/dist/vendored/components/DatePickerOverlay.vue +0 -345
- package/dist/vendored/components/DebugPanel.vue +0 -1734
- package/dist/vendored/components/DeviceOverlay.vue +0 -70
- package/dist/vendored/components/Dropdown.vue +0 -193
- package/dist/vendored/components/EventEditor.vue +0 -273
- package/dist/vendored/components/HoverGuides.vue +0 -122
- package/dist/vendored/components/JsonTree.vue +0 -547
- package/dist/vendored/components/LogValue.vue +0 -52
- package/dist/vendored/components/MarkdownBody.vue +0 -64
- package/dist/vendored/components/NewCardDialog.vue +0 -132
- package/dist/vendored/components/NodeInspector.vue +0 -295
- package/dist/vendored/components/NumberStepper.vue +0 -125
- package/dist/vendored/components/PickerOverlay.vue +0 -131
- package/dist/vendored/components/PropControl.vue +0 -245
- package/dist/vendored/components/PropTree.vue +0 -128
- package/dist/vendored/components/RemotePreview.vue +0 -173
- package/dist/vendored/components/SelectionOverlay.vue +0 -105
- package/dist/vendored/components/StructureTree.vue +0 -1002
- package/dist/vendored/components/Switch.vue +0 -38
- package/dist/vendored/components/setters/SetBgPosition.vue +0 -64
- package/dist/vendored/components/setters/SetBgSize.vue +0 -90
- package/dist/vendored/components/setters/SetCode.vue +0 -198
- package/dist/vendored/components/setters/SetColor.vue +0 -244
- package/dist/vendored/components/setters/SetCustomSelect.vue +0 -58
- package/dist/vendored/components/setters/SetExpr.vue +0 -107
- package/dist/vendored/components/setters/SetGradient.vue +0 -125
- package/dist/vendored/components/setters/SetGrid9.vue +0 -68
- package/dist/vendored/components/setters/SetIconSelect.vue +0 -67
- package/dist/vendored/components/setters/SetIfForm.vue +0 -61
- package/dist/vendored/components/setters/SetImage.vue +0 -43
- package/dist/vendored/components/setters/SetList.vue +0 -139
- package/dist/vendored/components/setters/SetNumber.vue +0 -118
- package/dist/vendored/components/setters/SetNumberStyle.vue +0 -113
- package/dist/vendored/components/setters/SetPadding.vue +0 -77
- package/dist/vendored/components/setters/SetSegment.vue +0 -160
- package/dist/vendored/components/setters/SetSelect.vue +0 -51
- package/dist/vendored/components/setters/SetSwitch.vue +0 -49
- package/dist/vendored/components/setters/SetText.vue +0 -52
- package/dist/vendored/components/setters/props.js +0 -23
- package/dist/vendored/fidelity/baseline-messageOverview.json +0 -87
- package/dist/vendored/fidelity/calibration.json +0 -29
- package/dist/vendored/fidelity/fidelity-messageOverview-after.html +0 -114
- package/dist/vendored/fidelity/fidelity-messageOverview-after.json +0 -272
- package/dist/vendored/fidelity/fidelity-messageOverview.html +0 -185
- package/dist/vendored/fidelity/fidelity-messageOverview.json +0 -336
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.html +0 -262
- package/dist/vendored/fidelity/fidelity-messageOverview_v2.json +0 -502
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.html +0 -201
- package/dist/vendored/fidelity/fidelity-messageOverview_v3.json +0 -417
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.html +0 -125
- package/dist/vendored/fidelity/fidelity-messageOverview_visual.json +0 -338
- package/dist/vendored/index.html +0 -16
- package/dist/vendored/main.js +0 -37
- package/dist/vendored/runtime/ai-api.js +0 -165
- package/dist/vendored/runtime/alert-card.js +0 -125
- package/dist/vendored/runtime/app-toast.js +0 -33
- package/dist/vendored/runtime/clipboard-node.js +0 -78
- package/dist/vendored/runtime/clipboard.js +0 -29
- package/dist/vendored/runtime/event-model.js +0 -300
- package/dist/vendored/runtime/hoverGuide.js +0 -31
- package/dist/vendored/runtime/jsapi-mock.js +0 -256
- package/dist/vendored/runtime/markdown.js +0 -140
- package/dist/vendored/runtime/node-model.js +0 -217
- package/dist/vendored/skills/.manifest.json +0 -1101
- package/dist/vendored/standalone-main.js +0 -36
- package/dist/vendored/styles/ai-chat.css +0 -1850
- package/dist/vendored/styles/app.css +0 -3648
- package/dist/vendored/styles/card-gallery.css +0 -285
- package/dist/vendored/styles/card-select.css +0 -288
- package/dist/vendored/styles/components.css +0 -383
- package/dist/vendored/styles/dropdown.css +0 -324
- package/dist/vendored/styles/json-tree.css +0 -308
- package/dist/vendored/styles/node-inspector.css +0 -800
- package/dist/vendored/styles/setters.css +0 -1050
- package/dist/vendored/styles/standalone.css +0 -27
- package/dist/vendored/styles/structure-tree.css +0 -460
- /package/dist/vendored/{api.js → preview/api.js} +0 -0
- /package/dist/vendored/{assets → preview/assets}/svg-symbols.svg +0 -0
- /package/dist/vendored/{components → preview/components}/AbsoluteLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Button.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Canvas.vue +0 -0
- /package/dist/vendored/{components → preview/components}/CheckGroup.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Column.vue +0 -0
- /package/dist/vendored/{components → preview/components}/CustomPlaceholder.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Divider.vue +0 -0
- /package/dist/vendored/{components → preview/components}/GridLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Icon.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Image.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Input.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ListLayout.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Map.vue +0 -0
- /package/dist/vendored/{components → preview/components}/MultiColumn.vue +0 -0
- /package/dist/vendored/{components → preview/components}/MultiText.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ProgressSlider.vue +0 -0
- /package/dist/vendored/{components → preview/components}/RadioButton.vue +0 -0
- /package/dist/vendored/{components → preview/components}/RenderTree.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Root.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Row.vue +0 -0
- /package/dist/vendored/{components → preview/components}/SvgCode.vue +0 -0
- /package/dist/vendored/{components → preview/components}/SvgSymbols.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Text.vue +0 -0
- /package/dist/vendored/{components → preview/components}/Video.vue +0 -0
- /package/dist/vendored/{components → preview/components}/ViewContainer.vue +0 -0
- /package/dist/vendored/{components → preview/components}/base.js +0 -0
- /package/dist/vendored/{components → preview/components}/icons.js +0 -0
- /package/dist/vendored/{components → preview/components}/index.js +0 -0
- /package/dist/vendored/{meta → preview/meta}/component-meta.json +0 -0
- /package/dist/vendored/{meta → preview/meta}/directive-meta.json +0 -0
- /package/dist/vendored/{meta → preview/meta}/style-meta.json +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/StyleParser.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/adapter.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/assets.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/canvas-context.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/card-runtime.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/evaluate.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/property.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/selectionOverlay.js +0 -0
- /package/dist/vendored/{runtime → preview/runtime}/toPreview.js +0 -0
- /package/dist/{server-core → vendored/server/core}/assetProxy.js +0 -0
- /package/dist/{server-core → vendored/server/core}/bbox.js +0 -0
- /package/dist/{server-core → vendored/server/core}/buildDataFromSchema.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardFiles.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardHistory.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardImage.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardImportMeta.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardListCache.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardOpenFolder.js +0 -0
- /package/dist/{server-core → vendored/server/core}/cardSidecar.js +0 -0
- /package/dist/{server-core → vendored/server/core}/codeBridge.js +0 -0
- /package/dist/{server-core → vendored/server/core}/logStream.js +0 -0
- /package/dist/{server-core → vendored/server/core}/logger.js +0 -0
- /package/dist/{server-core → vendored/server/core}/nodeModels.js +0 -0
- /package/dist/{server-core → vendored/server/core}/renderStatus.js +0 -0
- /package/dist/{server-core → vendored/server/core}/screenshot.js +0 -0
- /package/dist/{server-core → vendored/server/core}/sliceStatic.js +0 -0
- /package/dist/vendored/{meta → skills/dsl-card-generator/vendor/preview/meta}/action-meta.json +0 -0
- /package/dist/{server-core/_vendor → vendored/tools}/finmall-env.mjs +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import * as client from './client.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP 工具清单(16 个)+ 分发。
|
|
5
|
+
* 工具描述里写死了两处语义边界(与 renderStatus/screenshot 端点、方案文档三处一致):
|
|
6
|
+
* - card_render_status 只保证「结构合法 + 绑定可解析 + 渲染不抛错」,**不保证画得对**;
|
|
7
|
+
* - card_screenshot 才代表「浏览器里实际画出来长什么样」。
|
|
8
|
+
* Agent 误把 render_status.ok 当成「画对了」是整个闭环的头号坑,故反复强调。
|
|
9
|
+
* card_scaffold / card_add_component 为离线脚手架工具(P5),直接复用 scaffold lib,
|
|
10
|
+
* 不依赖 dev server,把「生成骨架 / 插入组件」从手写样板降级为一次工具调用。
|
|
11
|
+
* card_route_input 为离线建卡输入路由(Workflow 0),直接复用 skill 的 route_input.mjs 判定器:
|
|
12
|
+
* 识别到 Sketch MeaXure 标注导出时强制先转 sketch-spec-to-card 前处理,防止直接读整图建卡。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const TOOLS = [
|
|
16
|
+
{
|
|
17
|
+
name: 'card_list',
|
|
18
|
+
description:
|
|
19
|
+
'列出工作区所有卡片(id / 名称 / pageId)。优先走 dev server 的 GET /__cards;dev server 未启动时自动降级为直接扫描 src/cards/。',
|
|
20
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'card_read',
|
|
24
|
+
description:
|
|
25
|
+
'读取单张卡片的 schema.json(组件树/属性/样式/事件)与 data.json(输入参数默认值)。优先走 GET /__card/:id;未启动 dev server 时降级为直接读盘。',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: { id: { type: 'string', description: '卡片 id,对应 src/cards/<id>' } },
|
|
29
|
+
required: ['id'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'card_write',
|
|
34
|
+
description:
|
|
35
|
+
'把编辑后的 schema / data 写回 src/cards/<id>/(仅允许写 schema.json / data.json,白名单由服务端校验)。【闭环收口】写前自动把当前版本存为历史快照(可被 card_rollback 撤销;snapshot:false 可关闭),写完默认自动跑 validate + render_status(dev server 在线时)把校验结果一起返回;设 verify:false 可关闭。dev server 未启动时降级为直接写盘 + 本地 Python 硬校验(render_status 需 dev server,会标注 skipped)。',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
id: { type: 'string', description: '卡片 id' },
|
|
40
|
+
schema: { type: 'object', description: '新 schema.json 内容(可只传要改的字段,整体替换)' },
|
|
41
|
+
data: { type: 'object', description: '新 data.json 内容' },
|
|
42
|
+
verify: { type: 'boolean', description: '写后是否自动校验,默认 true' },
|
|
43
|
+
},
|
|
44
|
+
required: ['id'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'card_validate',
|
|
49
|
+
description:
|
|
50
|
+
'对单卡跑契约校验(Python 校验器):组件/属性/样式/表达式/data.json 一致性。返回 {ok, errors, warnings, durationMs}。优先走 GET /__validate/:id;未启动 dev server 时降级为直接子进程跑 Python。这是「硬」契约(CI 真卡口)。',
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
id: { type: 'string' },
|
|
55
|
+
noData: { type: 'boolean', description: 'true=跳过 data.json 一致性检查' },
|
|
56
|
+
},
|
|
57
|
+
required: ['id'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'card_render_status',
|
|
62
|
+
description:
|
|
63
|
+
'SSR 渲染自检:返回结构统计 + 绑定/运行时/渲染错误 + JSAPI 清单。⚠️ 语义边界:只保证「结构合法 + 绑定可解析 + 渲染不抛错」,**不保证画得对**——视觉正确性以 card_screenshot 为准。必须 dev server 已启动(npm run dev),不支持离线降级。',
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: 'object',
|
|
66
|
+
properties: { id: { type: 'string' } },
|
|
67
|
+
required: ['id'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'card_node_models',
|
|
72
|
+
description:
|
|
73
|
+
'读取单卡的「可编辑字段结构树」(Task #5 暴露的 Property-tree 模型,对应 dev server 的 GET /__node-models/:id)。返回每个节点的 {componentId, className, properties, style, directives, eventactions, components},其中每个字段含 itemKey/itemName/itemType/itemValue/itemValueFrom/itemTypeRange/options(iParamList|iTypeList)/children——AI 无需解析 raw DSL + component-meta 即可知道「某节点有哪些可改字段、各自类型与枚举」。⚠️ 该模型是只读视图,改卡仍走 card_write(整体替换);必须 dev server 已启动(npm run dev),不支持离线降级。',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: { id: { type: 'string', description: '卡片 id,对应 src/cards/<id>' } },
|
|
77
|
+
required: ['id'],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'card_screenshot',
|
|
82
|
+
description:
|
|
83
|
+
'无头浏览器截图(走系统 Chrome)。format=json 返回 {mime,width,height,base64};format=png 直接返回一张图片供多模态阅读(白底纯净卡片,等价于 ?chrome=off)。⚠️ 这是「画面对不对」的唯一真相来源。优先走 dev server;未启动时自动降级到独立渲染器(临时 Vite + 临时 Chrome,不占 5180/8787)。',
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
id: { type: 'string' },
|
|
88
|
+
format: { type: 'string', enum: ['json', 'png'], default: 'json' },
|
|
89
|
+
width: { type: 'number', default: 390 },
|
|
90
|
+
},
|
|
91
|
+
required: ['id'],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'card_sync',
|
|
96
|
+
description:
|
|
97
|
+
'经已登录的 FinMall 浏览器会话从远端拉取最新卡片写回 src/cards(复用 finmall-card-sync)。必须 dev server 已启动且已登录 FinMall,不支持离线降级。',
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: { cardCode: { type: 'string', description: '指定卡片 code;留空=同步全部' } },
|
|
101
|
+
required: [],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'card_save',
|
|
106
|
+
description:
|
|
107
|
+
'把当前卡片 schema 经已登录浏览器写回 FinMall(IDC10010.do)。必须 dev server 已启动且已登录 FinMall,不支持离线降级。',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
schema: { type: 'object', description: '当前卡片 schema(必须含 pageId)' },
|
|
112
|
+
code: { type: 'string' },
|
|
113
|
+
skeletonScreen: { type: 'string' },
|
|
114
|
+
},
|
|
115
|
+
required: ['schema'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'card_slice_upload',
|
|
120
|
+
description:
|
|
121
|
+
'把指定卡片引用的本地切图(src/slices)经已登录浏览器上传到 FinMall 图片库(CC0000.do),写「本地文件 ↔ 线上地址」映射 src/slices/manifest.json,并把该卡 schema.json / data.json 里的 "/slices/<name>" 引用改写为返回的 @filestore/… 线上地址。按 sha256 判重:内容未变的切图直接复用已有地址不重复上传;本地切图真源保留不动。**仅按需调用,不存在自动上传**——推送版本 / 出真机预览前,若卡片仍引用 /slices/ 就先跑本工具,否则线上卡片取不到图。必须 dev server 已启动且已登录 FinMall,不支持离线降级。',
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
cardId: { type: 'string', description: '卡片 id,对应 src/cards/<id>(上传与改写都只作用于这张卡)' },
|
|
126
|
+
force: { type: 'boolean', description: 'true=忽略 sha256 判重,强制重新上传(会得到新的线上地址)' },
|
|
127
|
+
},
|
|
128
|
+
required: ['cardId'],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'card_import_meta',
|
|
133
|
+
description:
|
|
134
|
+
'读取单卡的「导入来源元数据」(Phase E 可追溯性侧车,对应 dev server 的 GET /__import-meta/:id)。返回 {source, interface, baseUrl, appId, workspaceId, cardId, cardCode, cardName, syncedAt, tool, dirty, lastExport}——AI/skill 可据此判断「这张卡是不是从远端同步来的、何时导入、上一次导出读回校验是否通过」。无侧车(本地新建、未同步)时返回 {error:"import-meta not found"}。只读,不污染 DSL。必须 dev server 已启动,不支持离线降级。',
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: { id: { type: 'string', description: '卡片 id,对应 src/cards/<id>' } },
|
|
138
|
+
required: ['id'],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'card_route_input',
|
|
143
|
+
description:
|
|
144
|
+
'建卡输入路由(dsl-card-generator SKILL.md §8 Workflow 0 的机械判定器,离线):判定用户给的输入形态,返回 kind + 判定证据 + 下一步可直接执行的命令。kind=meaxure-export(Sketch MeaXure 标注导出目录:index.html 内嵌 artboards + preview/ 画板整图 + assets/ 切图)时**必须先经 sketch-spec-to-card 前处理**——next 直接给出 parse_meaxure.py(--src/--out/--draft/--build)与 cluster_states.py(--out UI/<稿名>)两条命令,禁止直接读 preview/ 整图建卡;若 UI/*/manifest.json 的 export 字段已匹配该导出,则 preprocess.needed=false 并给出存量产物路径,不重复跑管线。其余 kind:ui-bundle(要素包 → Workflow A 逐卡,overlay_dialog 不产卡)/ image(单张设计稿 → Workflow A 纯视觉推断)/ card-dir(已存在 schema.json → Workflow B 改卡)/ unknown(判不出,需向用户确认,ok=false)。无需 dev server。',
|
|
145
|
+
inputSchema: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
path: {
|
|
149
|
+
type: 'string',
|
|
150
|
+
description:
|
|
151
|
+
'输入路径:MeaXure 标注导出目录,或 UI 要素包目录(也可传其中的 manifest.json / spec.json / prompt.md / card-N.png),或卡片目录 src/cards/<id>,或设计稿图片。相对路径按工程根解析,支持 ~ 展开。',
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
required: ['path'],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'card_scaffold',
|
|
159
|
+
description:
|
|
160
|
+
'新建卡片双模:① intent 模式——秒出「合法最小」骨架(blank|list|detail|form|dialog|tab|canvas-head),data.json 由 inputParams 反推自洽;② from 模式——整卡模板克隆(references/card_templates.json 提供簇→代表卡路由,如 dialog→creditCardBill、list→revenueAnalysis、tab→DepositHolding、amount→myLoan),只重写 pageId/name,版式纪律与绑定范式原样继承,封面不复制(定稿后重新渲染)。从零建卡用 intent,有近似存量卡用 from。无需 dev server。默认写到 src/cards;传 outBase 可改输出根;force=true 覆盖已存在目录。',
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {
|
|
164
|
+
intent: { type: 'string', enum: ['blank', 'list', 'detail', 'form', 'dialog', 'tab', 'canvas-head'], description: '卡片意图(决定骨架结构);from 存在时忽略' },
|
|
165
|
+
from: { type: 'string', description: '整卡模板克隆:源模板卡 id(references/card_templates.json 的 template.sourceCard)' },
|
|
166
|
+
id: { type: 'string', description: '新卡片 id,对应 src/cards/<id>' },
|
|
167
|
+
name: { type: 'string', description: '卡片名称(写入 schema.name),可不传' },
|
|
168
|
+
outBase: { type: 'string', description: '输出根目录,默认 src/cards' },
|
|
169
|
+
platform: { type: 'string', description: '平台码,默认 002' },
|
|
170
|
+
force: { type: 'boolean', description: 'true=覆盖已存在的目录' },
|
|
171
|
+
},
|
|
172
|
+
required: ['id'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'card_add_component',
|
|
177
|
+
description:
|
|
178
|
+
'向已有卡片的某个「容器节点」插入一个「合法最小组件」(P5 脚手架)。定位 parent(必须是容器,有 slots 或 ListLayout/GridLayout)→ 在默认插槽追加一个由 scaffold lib 生成的合法节点 → 写回 → 离线跑 Python 硬校验并把结果带回。纪律:只增不改、不重排 componentId、不碰孤儿字段。text 以 $ 开头视为变量绑定(itemValueFrom=1),否则常量;click=true 给节点挂一个 customAction 点击动作骨架;setProps 可单点覆盖属性(等价 CLI 的 --set-prop key=value)。无需 dev server。',
|
|
179
|
+
inputSchema: {
|
|
180
|
+
type: 'object',
|
|
181
|
+
properties: {
|
|
182
|
+
id: { type: 'string', description: '卡片 id,对应 src/cards/<id>' },
|
|
183
|
+
parent: { type: 'string', description: '目标容器节点的 componentId' },
|
|
184
|
+
className: { type: 'string', description: '要插入的组件类型,如 Text/Row/Button/Image/Column' },
|
|
185
|
+
text: { type: 'string', description: '可选文案/绑定表达式;$ 开头视为变量绑定' },
|
|
186
|
+
click: { type: 'boolean', description: 'true=附带 customAction 点击动作骨架' },
|
|
187
|
+
setProps: {
|
|
188
|
+
type: 'array',
|
|
189
|
+
description:
|
|
190
|
+
'可选属性覆盖,等价 CLI 的 --set-prop key=value。数组元素可为 "key=value" 字符串或 {key,value} 对象;value 以 $ 开头视为变量绑定(itemValueFrom=1)。存在时优先于 text(text 仅在未覆盖 value 时兜底)。',
|
|
191
|
+
items: {},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
required: ['id', 'parent', 'className'],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: 'card_history',
|
|
199
|
+
description:
|
|
200
|
+
'列出单卡的编辑历史快照元数据(ts / 时间 / 字节数 / 节点数),快照由编辑器编辑或 card_write 写卡时自动累积(.history/,上限 30)。只返回元数据不返回整份 schema,用于选择回滚目标。优先走 dev server GET /__history/:id;未启动时降级为直接读盘。',
|
|
201
|
+
inputSchema: {
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties: { id: { type: 'string', description: '卡片 id,对应 src/cards/<id>' } },
|
|
204
|
+
required: ['id'],
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'card_rollback',
|
|
209
|
+
description:
|
|
210
|
+
'把卡片回滚到指定历史快照的 schema(默认最新快照 ts,即「撤销上一次写卡」)。安全语义:回滚前会自动把当前版本另存为新快照,回滚本身可再次回滚撤销;写回走 card_write 同一收口(默认自动 validate + render_status 校验并返回结果)。离线可降级(直接写盘 + Python 硬校验)。',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
id: { type: 'string', description: '卡片 id,对应 src/cards/<id>' },
|
|
215
|
+
ts: { type: 'number', description: '目标快照 ts(来自 card_history);缺省 = 最新快照' },
|
|
216
|
+
verify: { type: 'boolean', description: '回滚写回后是否自动校验,默认 true' },
|
|
217
|
+
},
|
|
218
|
+
required: ['id'],
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
/** 把工具名 + 参数分发到 client 函数;未知工具返回标准错误对象 */
|
|
224
|
+
export async function dispatch(name, args = {}) {
|
|
225
|
+
switch (name) {
|
|
226
|
+
case 'card_list':
|
|
227
|
+
return client.cardList()
|
|
228
|
+
case 'card_read':
|
|
229
|
+
return client.cardRead(args.id)
|
|
230
|
+
case 'card_write':
|
|
231
|
+
return client.cardWrite(args.id, { schema: args.schema, data: args.data }, { verify: args.verify !== false })
|
|
232
|
+
case 'card_validate':
|
|
233
|
+
return client.cardValidate(args.id, { noData: !!args.noData })
|
|
234
|
+
case 'card_render_status':
|
|
235
|
+
return client.cardRenderStatus(args.id)
|
|
236
|
+
case 'card_node_models':
|
|
237
|
+
return client.cardNodeModels(args.id)
|
|
238
|
+
case 'card_screenshot':
|
|
239
|
+
return client.cardScreenshot(args.id, { format: args.format || 'json', width: args.width || 390 })
|
|
240
|
+
case 'card_sync':
|
|
241
|
+
return client.cardSync({ cardCode: args.cardCode })
|
|
242
|
+
case 'card_save':
|
|
243
|
+
return client.cardSave({ schema: args.schema, code: args.code, skeletonScreen: args.skeletonScreen })
|
|
244
|
+
case 'card_slice_upload':
|
|
245
|
+
return client.sliceUpload({ cardId: args.cardId, force: args.force })
|
|
246
|
+
case 'card_import_meta':
|
|
247
|
+
return client.cardImportMeta(args.id)
|
|
248
|
+
case 'card_route_input':
|
|
249
|
+
return client.cardRouteInput({ path: args.path })
|
|
250
|
+
case 'card_scaffold':
|
|
251
|
+
return client.cardScaffold({ intent: args.intent, id: args.id, name: args.name, outBase: args.outBase, platform: args.platform, force: args.force, from: args.from })
|
|
252
|
+
case 'card_add_component':
|
|
253
|
+
return client.cardAddComponent({
|
|
254
|
+
id: args.id,
|
|
255
|
+
parent: args.parent,
|
|
256
|
+
className: args.className,
|
|
257
|
+
text: args.text,
|
|
258
|
+
click: args.click,
|
|
259
|
+
setProps: args.setProps,
|
|
260
|
+
})
|
|
261
|
+
case 'card_history':
|
|
262
|
+
return client.cardHistory(args.id)
|
|
263
|
+
case 'card_rollback':
|
|
264
|
+
return client.cardRollback(args.id, { ts: args.ts, verify: args.verify !== false })
|
|
265
|
+
default:
|
|
266
|
+
return { error: 'unknown-tool', message: `未知道具: ${name}` }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
Binary file
|
|
@@ -50,11 +50,14 @@ import path from 'node:path'
|
|
|
50
50
|
import fs from 'node:fs'
|
|
51
51
|
import os from 'node:os'
|
|
52
52
|
import { execFile, spawn, spawnSync } from 'node:child_process'
|
|
53
|
-
import {
|
|
53
|
+
import { SNAPSHOT_ROOT, skillsDirFor, resolveSkillPath, normalizeSkillRel } from './skillPaths.js'
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
/**
|
|
56
|
+
* 快照根:仓库内运行 = 真仓库根;cardbuddy 发布态 = 包内 dist/vendored(构建期内联的迷你仓库快照,
|
|
57
|
+
* 其下同样有 skills/ 与 mcp/,故本文件里 `../../mcp/tools.mjs` 这类相对引用两种形态都成立)。
|
|
58
|
+
* 它只作「随代码走的那份资源」(mcp 工具 / 技能文档)的兜底根;卡片工程根一律用中间件的 root。
|
|
59
|
+
*/
|
|
60
|
+
const REPO_ROOT = SNAPSHOT_ROOT
|
|
58
61
|
|
|
59
62
|
/** 单卡片最多保留的消息条数,超出从头部裁剪(避免历史无限增长撑爆上下文与磁盘) */
|
|
60
63
|
const MAX_HISTORY_MESSAGES = 200
|
|
@@ -83,7 +86,15 @@ async function loadTools() {
|
|
|
83
86
|
|
|
84
87
|
// ---- System prompt:精简协议 + 按需参考索引 ----
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
/** 技能库根:项目内 skills/ 优先,缺失时回退随包发布的快照(cardbuddy 未跑 init 也能用) */
|
|
90
|
+
function skillsDir(root) {
|
|
91
|
+
return skillsDirFor(root)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** dsl-card-generator 的 references 目录:系统提示词「按需参考索引」的来源 */
|
|
95
|
+
function skillRefsDir(root) {
|
|
96
|
+
return path.join(skillsDirFor(root), 'dsl-card-generator', 'references')
|
|
97
|
+
}
|
|
87
98
|
/** 参考索引的一句话说明:够模型选对文件即可,全文不进上下文 */
|
|
88
99
|
const SKILL_REF_HINTS = {
|
|
89
100
|
'rules.md': '卡片生成铁律(card_write 前必读:结构 / 绑定 / 样式红线)',
|
|
@@ -121,15 +132,17 @@ let _systemPromptCache = null
|
|
|
121
132
|
* 此前整份注入两份 SKILL.md + rules.md(约 125KB / 数万 token),上下文臃肿会显著放大
|
|
122
133
|
* 推理模型的空转思考(实测单轮 reasoning 4 分钟不收敛、界面一直「思考中」)。
|
|
123
134
|
*/
|
|
124
|
-
function buildSystemPrompt({ shellAllowed = false, vision = false, autoApprove = DEFAULT_AUTO_APPROVE } = {}) {
|
|
125
|
-
const
|
|
135
|
+
function buildSystemPrompt({ shellAllowed = false, vision = false, autoApprove = DEFAULT_AUTO_APPROVE, root = '' } = {}) {
|
|
136
|
+
const refsDir = skillRefsDir(root)
|
|
137
|
+
// 缓存键带上实际命中的技能库:仓库内与消费者项目(init 释放的副本)文档集可能不同
|
|
138
|
+
const cacheKey = `${shellAllowed ? 'shell' : 'base'}${vision ? '+vision' : ''}+aa:${autoApprove}+sk:${skillsDir(root)}`
|
|
126
139
|
if (_systemPromptCache && _systemPromptCache.key === cacheKey) return _systemPromptCache.text
|
|
127
140
|
// 免确认策略要如实写进协议:模型若以为「每次都要用户点确认」,会主动停下来等批准 / 把命令甩给用户
|
|
128
141
|
const skillAuto = autoApproves(autoApprove, 'skill_exec')
|
|
129
142
|
const shellAuto = autoApproves(autoApprove, 'shell_exec')
|
|
130
143
|
const lines = []
|
|
131
144
|
for (const entry of SKILL_DOC_ENTRIES) {
|
|
132
|
-
const abs = path.join(
|
|
145
|
+
const abs = path.join(skillsDir(root), entry.path)
|
|
133
146
|
let size = 0
|
|
134
147
|
try {
|
|
135
148
|
size = fs.statSync(abs).size
|
|
@@ -139,10 +152,10 @@ function buildSystemPrompt({ shellAllowed = false, vision = false, autoApprove =
|
|
|
139
152
|
lines.push(`- ${entry.path}(${Math.max(1, Math.round(size / 1024))}KB):${entry.hint}`)
|
|
140
153
|
}
|
|
141
154
|
try {
|
|
142
|
-
for (const name of fs.readdirSync(
|
|
155
|
+
for (const name of fs.readdirSync(refsDir)) {
|
|
143
156
|
let st
|
|
144
157
|
try {
|
|
145
|
-
st = fs.statSync(path.join(
|
|
158
|
+
st = fs.statSync(path.join(refsDir, name))
|
|
146
159
|
} catch {
|
|
147
160
|
continue
|
|
148
161
|
}
|
|
@@ -151,7 +164,7 @@ function buildSystemPrompt({ shellAllowed = false, vision = false, autoApprove =
|
|
|
151
164
|
}
|
|
152
165
|
// 范式片段是子目录,不列出来模型根本不知道存在(曾经为此反推 expand_ir 源码猜字段形状)
|
|
153
166
|
const snippets = fs
|
|
154
|
-
.readdirSync(path.join(
|
|
167
|
+
.readdirSync(path.join(refsDir, 'recipe_snippets'))
|
|
155
168
|
.filter((f) => f.endsWith('.json'))
|
|
156
169
|
.sort()
|
|
157
170
|
if (snippets.length) {
|
|
@@ -259,13 +272,14 @@ const CARD_PATCH_TOOL = {
|
|
|
259
272
|
type: 'function',
|
|
260
273
|
function: {
|
|
261
274
|
name: 'card_patch',
|
|
262
|
-
description: '按 JSON 点路径(数组用数字段)对卡片 schema/data 做最小修改并写回;大卡必用本工具,禁止用 card_write 整包重写。路径必须先用 card_grep 拿真实路径——schema 的节点树是 view.components[].slots[].components[](不是 view.children)。路径中段不存在会直接报错(不再静默新建分支),确要新建传 create:true
|
|
275
|
+
description: '按 JSON 点路径(数组用数字段)对卡片 schema/data 做最小修改并写回;大卡必用本工具,禁止用 card_write 整包重写。路径必须先用 card_grep 拿真实路径——schema 的节点树是 view.components[].slots[].components[](不是 view.children)。路径中段不存在会直接报错(不再静默新建分支),确要新建传 create:true。patches(改/增)与 deletes(删)至少给一个,可只删不改(清理无用样式项、多余节点)。仓库根下走 card_write 管线(带历史快照与校验)。',
|
|
263
276
|
parameters: {
|
|
264
277
|
type: 'object',
|
|
265
278
|
properties: {
|
|
266
279
|
id: { type: 'string', description: '卡片 id' },
|
|
267
280
|
patches: {
|
|
268
281
|
type: 'array',
|
|
282
|
+
description: '要写入的 {path,value} 列表;只删除时可省略(此时必须给 deletes)',
|
|
269
283
|
items: {
|
|
270
284
|
type: 'object',
|
|
271
285
|
properties: {
|
|
@@ -275,11 +289,11 @@ const CARD_PATCH_TOOL = {
|
|
|
275
289
|
required: ['path', 'value'],
|
|
276
290
|
},
|
|
277
291
|
},
|
|
278
|
-
deletes: { type: 'array', items: { type: 'string' }, description: '要删除的 JSON
|
|
292
|
+
deletes: { type: 'array', items: { type: 'string' }, description: '要删除的 JSON 点路径;可单独使用(只删不改)' },
|
|
279
293
|
target: { type: 'string', enum: ['schema', 'data'], description: '默认 schema' },
|
|
280
294
|
create: { type: 'boolean', description: '路径中段不存在时是否新建分支(默认 false = 响亮报错)' },
|
|
281
295
|
},
|
|
282
|
-
required: ['id'
|
|
296
|
+
required: ['id'],
|
|
283
297
|
},
|
|
284
298
|
},
|
|
285
299
|
}
|
|
@@ -337,16 +351,44 @@ function applyByPath(doc, p, setter, { create = false } = {}) {
|
|
|
337
351
|
setter(cur, segs[segs.length - 1])
|
|
338
352
|
}
|
|
339
353
|
|
|
340
|
-
async function cardPatch(root, args, dispatch) {
|
|
354
|
+
async function cardPatch(root, args, dispatch, { allowDispatch = false } = {}) {
|
|
341
355
|
const { id, patches = [], deletes = [], target = 'schema' } = args || {}
|
|
342
|
-
if (!Array.isArray(patches)
|
|
356
|
+
if (!Array.isArray(patches)) throw new Error('patches 必须是数组')
|
|
357
|
+
if (!Array.isArray(deletes)) throw new Error('deletes 必须是数组')
|
|
358
|
+
// 「只删不改」是合法操作(清理无用样式项 / 多余节点);真正的错误是两个都空 —— 空补丁什么都不做,
|
|
359
|
+
// 却会照样走一遍 card_write 管线、平白多存一份历史快照,所以在这里响亮拒掉。
|
|
360
|
+
if (!patches.length && !deletes.length) throw new Error('patches 与 deletes 至少给一个(空补丁无意义)')
|
|
343
361
|
const file = path.join(root, 'src', 'cards', String(id || ''), target === 'data' ? 'data.json' : 'schema.json')
|
|
344
362
|
const doc = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
345
363
|
const create = args.create === true
|
|
346
364
|
for (const pt of patches) applyByPath(doc, pt.path, (obj, k) => (obj[k] = pt.value), { create })
|
|
347
|
-
|
|
348
|
-
//
|
|
349
|
-
|
|
365
|
+
// 删数组元素必须 splice:早先用 delete obj[k],在数组上只留一个 null 空洞
|
|
366
|
+
// (properties 变成 [null, {...}]),补丁报「成功」却把 schema 写成稀疏数组,渲染与校验随后就崩。
|
|
367
|
+
// 同一父数组里删多项时按原始下标从大到小删 —— splice 会让后面的索引左移,升序删会误删邻居。
|
|
368
|
+
const orderedDeletes = [...deletes].sort((a, b) => {
|
|
369
|
+
const sa = String(a).split('.')
|
|
370
|
+
const sb = String(b).split('.')
|
|
371
|
+
const pa = sa.slice(0, -1).join('.')
|
|
372
|
+
const pb = sb.slice(0, -1).join('.')
|
|
373
|
+
if (pa !== pb) return pa < pb ? -1 : 1
|
|
374
|
+
const na = Number(sa[sa.length - 1])
|
|
375
|
+
const nb = Number(sb[sb.length - 1])
|
|
376
|
+
return Number.isFinite(na) && Number.isFinite(nb) ? nb - na : 0
|
|
377
|
+
})
|
|
378
|
+
for (const d of orderedDeletes)
|
|
379
|
+
applyByPath(
|
|
380
|
+
doc,
|
|
381
|
+
d,
|
|
382
|
+
(obj, k) => {
|
|
383
|
+
if (Array.isArray(obj)) obj.splice(Number(k), 1)
|
|
384
|
+
else delete obj[k]
|
|
385
|
+
},
|
|
386
|
+
{ create },
|
|
387
|
+
)
|
|
388
|
+
// 工程根:走 card_write 管线(历史快照 + validate/render_status);其它根(测试/自定义)直接落盘。
|
|
389
|
+
// 仓库内运行由「root === 仓库根」判定;cardbuddy 发布态快照根在包内、永不等于工程根,
|
|
390
|
+
// 故由宿主显式 allowDispatch 开启(它已把 CARD_ROOT 指到同一个工程根,dispatch 落点一致)。
|
|
391
|
+
if (dispatch && (allowDispatch || path.resolve(root) === REPO_ROOT)) {
|
|
350
392
|
const res = await dispatch('card_write', { id, [target]: doc })
|
|
351
393
|
return { ok: true, via: 'card_write', patches: patches.length, deletes: deletes.length, write: res }
|
|
352
394
|
}
|
|
@@ -458,9 +500,12 @@ function resolveSkillScript(root, script) {
|
|
|
458
500
|
if (!SKILL_SCRIPT_RE.test(rel)) {
|
|
459
501
|
throw new Error(`只能执行 skills/<skill>/scripts/ 下的 .mjs / .js / .py 脚本(收到:${rel});其余命令用 shell_exec`)
|
|
460
502
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
503
|
+
// 项目内 skills/ 优先;cardbuddy 安装态尚未释放技能库时回退包内快照。
|
|
504
|
+
// normalizeSkillRel 拒绝绝对路径与任何 `..` 片段,resolveSkillPath 只在候选 skills 根内查找。
|
|
505
|
+
const clean = normalizeSkillRel(rel)
|
|
506
|
+
if (!clean) throw new Error('非法路径:脚本必须在仓库 skills/ 目录内')
|
|
507
|
+
const abs = resolveSkillPath(root, clean)
|
|
508
|
+
if (!abs || !fs.statSync(abs).isFile()) throw new Error(`脚本不存在:${rel}`)
|
|
464
509
|
return { rel, abs, bin: path.extname(abs).toLowerCase() === '.py' ? pythonBin() : 'node' }
|
|
465
510
|
}
|
|
466
511
|
|
|
@@ -1255,21 +1300,14 @@ const SKILL_DOC_EXT = new Set(['.md', '.json', '.txt', '.csv'])
|
|
|
1255
1300
|
function skillRead(root, args) {
|
|
1256
1301
|
const name = String(args?.path || '').trim().replace(/^\.?\/+/u, '')
|
|
1257
1302
|
if (!name) throw new Error('缺少 path')
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1303
|
+
const rel = name.includes('/') ? name.replace(/^skills\//, '') : `dsl-card-generator/references/${name}`
|
|
1304
|
+
// 越界(绝对路径 / 任何 `..` 片段)先判非法,再谈存在性 —— 与 skill_exec 同一套闸。
|
|
1305
|
+
if (!normalizeSkillRel(rel)) throw new Error('非法路径:只能读 skills/ 目录内的文档')
|
|
1306
|
+
// 项目内 skills/ 优先,缺失时回退随包发布的技能快照(cardbuddy 安装态未跑 init 也能读文档)。
|
|
1307
|
+
const full = resolveSkillPath(root, rel)
|
|
1308
|
+
if (!full) throw new Error(`文档不存在:skills/${rel}`)
|
|
1263
1309
|
if (!SKILL_DOC_EXT.has(path.extname(full).toLowerCase())) throw new Error('只能读文档(.md / .json / .txt / .csv);跑脚本用 skill_exec')
|
|
1264
|
-
if (!fs.
|
|
1265
|
-
// 测试根等场景下 skills/ 可能不存在:退回模块加载时的仓库 references 目录
|
|
1266
|
-
const fallback = path.resolve(fs.existsSync(refsDir) ? refsDir : SKILL_REFS_DIR, name)
|
|
1267
|
-
if (!name.includes('/') && fallback.startsWith(SKILL_REFS_DIR + path.sep) && fs.existsSync(fallback)) {
|
|
1268
|
-
const text = fs.readFileSync(fallback, 'utf8')
|
|
1269
|
-
return text.length <= SKILL_READ_MAX ? text : `${text.slice(0, SKILL_READ_MAX)}\n…(已截断,省略 ${text.length - SKILL_READ_MAX} 字符)`
|
|
1270
|
-
}
|
|
1271
|
-
throw new Error(`文档不存在:skills/${rel}`)
|
|
1272
|
-
}
|
|
1310
|
+
if (!fs.statSync(full).isFile()) throw new Error(`文档不存在:skills/${rel}`)
|
|
1273
1311
|
const text = fs.readFileSync(full, 'utf8')
|
|
1274
1312
|
if (text.length <= SKILL_READ_MAX) return text
|
|
1275
1313
|
return `${text.slice(0, SKILL_READ_MAX)}\n…(已截断,省略 ${text.length - SKILL_READ_MAX} 字符)`
|
|
@@ -1732,7 +1770,7 @@ function describePath(target) {
|
|
|
1732
1770
|
* 搜索根先剪掉「被其它根包含」的子根:否则同一文件会在父 / 子根各命中一次,被误判成重名歧义而返回空。
|
|
1733
1771
|
* 同一个根里出现多个同名同尺寸文件才算歧义,此时不猜。
|
|
1734
1772
|
*/
|
|
1735
|
-
async function locateDropped({ name, relPath = '', size = -1 }) {
|
|
1773
|
+
async function locateDropped({ name, relPath = '', size = -1, root = '' }) {
|
|
1736
1774
|
const fileName = String(name || '').trim()
|
|
1737
1775
|
if (!fileName) return null
|
|
1738
1776
|
const wantSize = Number(size) >= 0 ? Number(size) : -1
|
|
@@ -1770,6 +1808,7 @@ async function locateDropped({ name, relPath = '', size = -1 }) {
|
|
|
1770
1808
|
pushRoot(path.join(home, 'Desktop'))
|
|
1771
1809
|
pushRoot(path.join(home, 'Downloads'))
|
|
1772
1810
|
pushRoot(path.join(home, 'Documents'))
|
|
1811
|
+
if (root) pushRoot(root)
|
|
1773
1812
|
pushRoot(REPO_ROOT)
|
|
1774
1813
|
pushRoot(path.dirname(REPO_ROOT))
|
|
1775
1814
|
pushRoot(home)
|
|
@@ -2693,6 +2732,9 @@ function toOpenAiMessages(messages) {
|
|
|
2693
2732
|
*/
|
|
2694
2733
|
export function createAiChatMiddleware(opts = {}) {
|
|
2695
2734
|
const root = opts.root ? path.resolve(opts.root) : REPO_ROOT
|
|
2735
|
+
// card_patch 是否走 card_write 管线(历史快照 + validate/render_status):
|
|
2736
|
+
// 仓库内运行由「root === 仓库根」自动成立;cardbuddy 发布态快照根在包内,由宿主显式开启。
|
|
2737
|
+
const allowDispatch = opts.allowDispatch === true
|
|
2696
2738
|
/** 对话记录落盘目录(.cache/ 已在 .gitignore):随 root 走,测试可用临时根隔离 */
|
|
2697
2739
|
const chatDir = path.join(root, '.cache', 'ai-chats')
|
|
2698
2740
|
|
|
@@ -3007,6 +3049,7 @@ export function createAiChatMiddleware(opts = {}) {
|
|
|
3007
3049
|
name: body.name,
|
|
3008
3050
|
relPath: body.relPath || '',
|
|
3009
3051
|
size: Number.isFinite(Number(body.size)) ? Number(body.size) : -1,
|
|
3052
|
+
root,
|
|
3010
3053
|
})
|
|
3011
3054
|
if (!found) return sendJson(req, res, 404, { error: 'not-located' })
|
|
3012
3055
|
const info = describePath(found)
|
|
@@ -3093,6 +3136,7 @@ export function createAiChatMiddleware(opts = {}) {
|
|
|
3093
3136
|
shellAllowed: Boolean(defaults.shellAllowed),
|
|
3094
3137
|
vision: visionOn,
|
|
3095
3138
|
autoApprove: defaults.autoApprove,
|
|
3139
|
+
root,
|
|
3096
3140
|
})
|
|
3097
3141
|
} catch (e) {
|
|
3098
3142
|
return sendJson(req, res, 500, { error: 'tools-load-failed', message: String((e && e.message) || e) })
|
|
@@ -3540,7 +3584,7 @@ export function createAiChatMiddleware(opts = {}) {
|
|
|
3540
3584
|
result = cardGrep(root, parsedArgs)
|
|
3541
3585
|
break
|
|
3542
3586
|
case 'card_patch':
|
|
3543
|
-
result = await cardPatch(root, parsedArgs, dispatch)
|
|
3587
|
+
result = await cardPatch(root, parsedArgs, dispatch, { allowDispatch })
|
|
3544
3588
|
break
|
|
3545
3589
|
case 'fs_read':
|
|
3546
3590
|
result = fsRead(root, parsedArgs, mountedRoots)
|
|
@@ -4,12 +4,14 @@ import { pathToFileURL } from 'node:url'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
6
6
|
|
|
7
|
-
const skillDir = path.resolve(process.cwd(), 'skills', 'finmall-card-sync')
|
|
8
|
-
const skillPath = path.join(skillDir, 'scripts', 'api_batch_sync.mjs')
|
|
9
7
|
import { recordExport } from './cardImportMeta.js'
|
|
10
8
|
import { checkPushAllowed } from './cardListCache.js'
|
|
11
9
|
import { assertRemoteSource, findCardDir } from './cardSidecar.js'
|
|
12
10
|
import { log } from './logger.js'
|
|
11
|
+
import { resolveSkillPathOrProject } from './skillPaths.js'
|
|
12
|
+
|
|
13
|
+
/** 远端保存脚本相对 skills/ 的路径(与「同步」共用 finmall-card-sync) */
|
|
14
|
+
const SYNC_SCRIPT_REL = 'finmall-card-sync/scripts/api_batch_sync.mjs'
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* 远端保存(写回 FinMall)服务端处理。
|
|
@@ -94,7 +96,10 @@ export function createSaveMiddleware({ root }) {
|
|
|
94
96
|
// 骨架屏:线上编辑器始终传 SVG 缩略图;本地无生成器,传空串(仅影响编辑器列表缩略图)。
|
|
95
97
|
const skeletonScreen = typeof payload.skeletonScreen === 'string' ? payload.skeletonScreen : ''
|
|
96
98
|
try {
|
|
97
|
-
|
|
99
|
+
// 脚本按「项目根 skills/ → 随包技能快照」解析:cardbuddy 安装态无 skills/ 时也能保存。
|
|
100
|
+
const mod = await import(
|
|
101
|
+
pathToFileURL(resolveSkillPathOrProject(root || process.cwd(), SYNC_SCRIPT_REL)).href
|
|
102
|
+
)
|
|
98
103
|
const { response, verified } = await mod.saveCardToRemote(schema, { skeletonScreen })
|
|
99
104
|
// 导出可追溯(Phase E):把远端保存的读回校验结果写进导入元数据侧车。
|
|
100
105
|
// 注意:目录按 cardCode 命名,必须按 pageId 反查到正确的 src/cards/<cardCode>/。
|
|
@@ -5,6 +5,10 @@ import { writeFile, mkdir } from 'node:fs/promises'
|
|
|
5
5
|
import { checkPushAllowed } from './cardListCache.js'
|
|
6
6
|
import { assertRemoteSource } from './cardSidecar.js'
|
|
7
7
|
import { log, fmtBytes } from './logger.js'
|
|
8
|
+
import { resolveSkillPathOrProject } from './skillPaths.js'
|
|
9
|
+
|
|
10
|
+
/** finmall-card-sync 的批量同步脚本(同步 / 远端预览共用),相对 skills/ */
|
|
11
|
+
const SYNC_SCRIPT_REL = 'finmall-card-sync/scripts/api_batch_sync.mjs'
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* 预览「同步远程数据」按钮的服务端处理:经已认证的 FinMall 浏览器会话拉取最新卡片写回 src/cards。
|
|
@@ -34,13 +38,7 @@ export function createSyncMiddleware({ root }) {
|
|
|
34
38
|
return res.end(JSON.stringify({ error: 'invalid json', message: String(e.message) }))
|
|
35
39
|
}
|
|
36
40
|
try {
|
|
37
|
-
const skillPath =
|
|
38
|
-
root,
|
|
39
|
-
'skills',
|
|
40
|
-
'finmall-card-sync',
|
|
41
|
-
'scripts',
|
|
42
|
-
'api_batch_sync.mjs'
|
|
43
|
-
)
|
|
41
|
+
const skillPath = resolveSkillPathOrProject(root, SYNC_SCRIPT_REL)
|
|
44
42
|
const { syncCards } = await import(pathToFileURL(skillPath).href)
|
|
45
43
|
const summary = await syncCards({
|
|
46
44
|
cardCode: payload.cardCode || undefined,
|
|
@@ -160,13 +158,7 @@ export function createPreviewMiddleware({ root }) {
|
|
|
160
158
|
}
|
|
161
159
|
}
|
|
162
160
|
try {
|
|
163
|
-
const skillPath =
|
|
164
|
-
root,
|
|
165
|
-
'skills',
|
|
166
|
-
'finmall-card-sync',
|
|
167
|
-
'scripts',
|
|
168
|
-
'api_batch_sync.mjs'
|
|
169
|
-
)
|
|
161
|
+
const skillPath = resolveSkillPathOrProject(root, SYNC_SCRIPT_REL)
|
|
170
162
|
const { previewCardRemote } = await import(pathToFileURL(skillPath).href)
|
|
171
163
|
const result = await previewCardRemote(payload.schema)
|
|
172
164
|
log.ok('远端预览完成', {
|
|
@@ -5,6 +5,11 @@ import { execFile, execFileSync } from 'node:child_process'
|
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
7
|
|
|
8
|
+
import { resolveSkillPathOrProject } from './skillPaths.js'
|
|
9
|
+
|
|
10
|
+
/** 校验脚本相对 skills/ 的固定路径(导出供 MCP 复用) */
|
|
11
|
+
const VALIDATE_SCRIPT_REL = 'dsl-card-generator/scripts/validate_schema.py'
|
|
12
|
+
|
|
8
13
|
/**
|
|
9
14
|
* 契约校验端点(L0 闭环之一)。
|
|
10
15
|
*
|
|
@@ -18,15 +23,13 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
|
18
23
|
* @param {{ root: string }} opts
|
|
19
24
|
*/
|
|
20
25
|
|
|
21
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* 校验脚本绝对路径(导出供 MCP 复用)。
|
|
28
|
+
* 优先项目根下的 skills/;缺失时回退随包发布的技能快照 —— cardbuddy 安装态即便
|
|
29
|
+
* 没跑过 `cardbuddy init`(项目里还没有 skills/)也能正常校验。
|
|
30
|
+
*/
|
|
22
31
|
export function scriptPath(root) {
|
|
23
|
-
return
|
|
24
|
-
root,
|
|
25
|
-
'skills',
|
|
26
|
-
'dsl-card-generator',
|
|
27
|
-
'scripts',
|
|
28
|
-
'validate_schema.py',
|
|
29
|
-
)
|
|
32
|
+
return resolveSkillPathOrProject(root, VALIDATE_SCRIPT_REL)
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/** 解析可用的 python 解释器;找不到返回 null(导出供 MCP 复用) */
|