@bovinphang/frontend-craft 2.6.0 → 2.8.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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +72 -1
- package/CHANGELOG.zh-CN.md +71 -1
- package/README.md +10 -6
- package/README.openclaw.md +100 -42
- package/README.openclaw.zh-CN.md +100 -42
- package/README.zh-CN.md +10 -6
- package/agents/fec-architect.md +70 -70
- package/agents/fec-code-reviewer.md +82 -82
- package/agents/fec-debugger.md +20 -20
- package/agents/fec-design-token-mapper.md +36 -36
- package/agents/fec-doc-updater.md +13 -13
- package/agents/fec-e2e-runner.md +45 -45
- package/agents/fec-figma-implementer.md +59 -59
- package/agents/fec-performance-optimizer.md +128 -128
- package/agents/fec-refactor-cleaner.md +14 -14
- package/agents/fec-security-reviewer.md +66 -67
- package/agents/fec-test-planner.md +27 -27
- package/agents/fec-typescript-reviewer.md +88 -88
- package/agents/fec-ui-checker.md +74 -74
- package/commands/fec-debug.md +19 -19
- package/commands/fec-doc-sync.md +11 -11
- package/commands/fec-init.md +37 -38
- package/commands/fec-plan.md +58 -58
- package/commands/fec-refactor-clean.md +11 -11
- package/commands/fec-review.md +41 -41
- package/commands/fec-scaffold.md +41 -41
- package/commands/fec-tdd.md +13 -13
- package/dist/bin/frontend-craft.js +29 -27
- package/docs/example-prompts.md +84 -69
- package/docs/ja-JP/README.md +56 -52
- package/docs/ko-KR/README.md +45 -41
- package/docs/project-structure.md +19 -7
- package/docs/superpowers/plans/2026-05-29-fec-debug.md +491 -0
- package/docs/superpowers/specs/2026-05-29-fec-debug-design.md +340 -0
- package/docs/zh-CN/example-prompts.md +84 -69
- package/docs/zh-CN/project-structure.md +14 -5
- package/docs/zh-TW/README.md +33 -29
- package/localized/zh-CN/agents/fec-architect.md +125 -0
- package/localized/zh-CN/agents/fec-code-reviewer.md +152 -0
- package/localized/zh-CN/agents/fec-debugger.md +44 -0
- package/localized/zh-CN/agents/fec-design-token-mapper.md +75 -0
- package/localized/zh-CN/agents/fec-doc-updater.md +30 -0
- package/localized/zh-CN/agents/fec-e2e-runner.md +107 -0
- package/localized/zh-CN/agents/fec-figma-implementer.md +104 -0
- package/localized/zh-CN/agents/fec-performance-optimizer.md +293 -0
- package/localized/zh-CN/agents/fec-refactor-cleaner.md +32 -0
- package/localized/zh-CN/agents/fec-security-reviewer.md +116 -0
- package/localized/zh-CN/agents/fec-test-planner.md +50 -0
- package/localized/zh-CN/agents/fec-typescript-reviewer.md +142 -0
- package/localized/zh-CN/agents/fec-ui-checker.md +120 -0
- package/localized/zh-CN/commands/fec-debug.md +30 -0
- package/localized/zh-CN/commands/fec-doc-sync.md +19 -0
- package/localized/zh-CN/commands/fec-init.md +70 -0
- package/localized/zh-CN/commands/fec-plan.md +94 -0
- package/localized/zh-CN/commands/fec-refactor-clean.md +19 -0
- package/localized/zh-CN/commands/fec-review.md +66 -0
- package/localized/zh-CN/commands/fec-scaffold.md +205 -0
- package/localized/zh-CN/commands/fec-tdd.md +21 -0
- package/localized/zh-CN/skills/TEMPLATE.md +155 -0
- package/localized/zh-CN/skills/eval_queries.json +1013 -0
- package/localized/zh-CN/skills/fec-accessibility-check/SKILL.md +39 -0
- package/localized/zh-CN/skills/fec-accessibility-check/references/report-template.md +23 -0
- package/localized/zh-CN/skills/fec-accessibility-check/references/screen-reader-testing.md +33 -0
- package/localized/zh-CN/skills/fec-alchemy/SKILL.md +74 -0
- package/localized/zh-CN/skills/fec-alchemy/assets/absorption-plan-template.md +47 -0
- package/localized/zh-CN/skills/fec-alchemy/assets/intake-template.md +55 -0
- package/localized/zh-CN/skills/fec-alchemy/assets/review-checklist.md +31 -0
- package/localized/zh-CN/skills/fec-alchemy/references/methodology.md +94 -0
- package/localized/zh-CN/skills/fec-alchemy/references/originality-and-licensing.md +57 -0
- package/localized/zh-CN/skills/fec-api-integration/SKILL.md +78 -0
- package/localized/zh-CN/skills/fec-api-integration/references/integration-patterns.md +80 -0
- package/localized/zh-CN/skills/fec-backend-requirements-handoff/SKILL.md +45 -0
- package/localized/zh-CN/skills/fec-browser-storage/SKILL.md +34 -0
- package/localized/zh-CN/skills/fec-browser-storage/references/storage-patterns.md +65 -0
- package/localized/zh-CN/skills/fec-canvas-threejs/SKILL.md +66 -0
- package/localized/zh-CN/skills/fec-canvas-threejs/references/rendering-patterns.md +67 -0
- package/localized/zh-CN/skills/fec-canvas-threejs/references/shader-webgl-patterns.md +67 -0
- package/localized/zh-CN/skills/fec-code-review/SKILL.md +143 -0
- package/localized/zh-CN/skills/fec-code-review/references/report-template.md +29 -0
- package/localized/zh-CN/skills/fec-component-testing/SKILL.md +119 -0
- package/localized/zh-CN/skills/fec-data-fetching/SKILL.md +62 -0
- package/localized/zh-CN/skills/fec-data-fetching/references/query-patterns.md +103 -0
- package/localized/zh-CN/skills/fec-debug-framework/SKILL.md +157 -0
- package/localized/zh-CN/skills/fec-debug-framework/references/report-template.md +58 -0
- package/localized/zh-CN/skills/fec-dependency-upgrade/SKILL.md +34 -0
- package/localized/zh-CN/skills/fec-dependency-upgrade/references/dependency-upgrade-workflow.md +56 -0
- package/localized/zh-CN/skills/fec-doc-sync/SKILL.md +39 -0
- package/localized/zh-CN/skills/fec-drawio-studio/SKILL.md +73 -0
- package/localized/zh-CN/skills/fec-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
- package/localized/zh-CN/skills/fec-drawio-studio/data/brand-icons.json +878 -0
- package/localized/zh-CN/skills/fec-drawio-studio/data/shape-index.json +83570 -0
- package/localized/zh-CN/skills/fec-drawio-studio/references/data-residency.md +27 -0
- package/localized/zh-CN/skills/fec-drawio-studio/references/diagram-patterns.md +60 -0
- package/localized/zh-CN/skills/fec-drawio-studio/references/flowchart-quality.md +49 -0
- package/localized/zh-CN/skills/fec-drawio-studio/references/layout-and-export.md +72 -0
- package/localized/zh-CN/skills/fec-drawio-studio/references/troubleshooting.md +29 -0
- package/localized/zh-CN/skills/fec-drawio-studio/references/xml-and-mermaid.md +58 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/brand-symbols.mjs +99 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/diagram-lint.mjs +205 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/diagram-url.mjs +102 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/drawio-export.mjs +134 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/layout-graph.mjs +107 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/png-embed-fix.mjs +26 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/scan-go-packages.mjs +36 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/scan-js-modules.mjs +52 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/scan-python-classes.mjs +30 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/scan-python-modules.mjs +55 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/shape-query.mjs +121 -0
- package/localized/zh-CN/skills/fec-drawio-studio/scripts/studio-core.mjs +139 -0
- package/localized/zh-CN/skills/fec-drawio-studio/styles/built-in/corporate.json +49 -0
- package/localized/zh-CN/skills/fec-drawio-studio/styles/built-in/default.json +49 -0
- package/localized/zh-CN/skills/fec-drawio-studio/styles/built-in/handdrawn.json +49 -0
- package/localized/zh-CN/skills/fec-drawio-studio/styles/schema.json +112 -0
- package/localized/zh-CN/skills/fec-e2e-testing/SKILL.md +42 -0
- package/localized/zh-CN/skills/fec-e2e-testing/references/e2e-ci-reporting.md +62 -0
- package/localized/zh-CN/skills/fec-e2e-testing/references/e2e-special-scenarios.md +52 -0
- package/localized/zh-CN/skills/fec-e2e-testing/references/e2e-visual-regression.md +60 -0
- package/localized/zh-CN/skills/fec-e2e-testing/references/playwright-patterns.md +95 -0
- package/localized/zh-CN/skills/fec-form-handling/SKILL.md +76 -0
- package/localized/zh-CN/skills/fec-form-handling/references/advanced-form-patterns.md +101 -0
- package/localized/zh-CN/skills/fec-image-generation/SKILL.md +80 -0
- package/localized/zh-CN/skills/fec-image-generation/assets/interactive-diagram.html +371 -0
- package/localized/zh-CN/skills/fec-image-generation/references/artifact-routing.md +80 -0
- package/localized/zh-CN/skills/fec-image-generation/references/diagram-workflows.md +74 -0
- package/localized/zh-CN/skills/fec-image-generation/references/html-technical-diagrams.md +236 -0
- package/localized/zh-CN/skills/fec-image-generation/references/png-qa-autofix.md +30 -0
- package/localized/zh-CN/skills/fec-image-generation/scripts/export-diagram.mjs +364 -0
- package/localized/zh-CN/skills/fec-image-generation/scripts/interactive-diagram-server.mjs +654 -0
- package/localized/zh-CN/skills/fec-image-generation/scripts/png-qa.mjs +431 -0
- package/localized/zh-CN/skills/fec-image-generation/scripts/tech-diagram-render.mjs +1397 -0
- package/localized/zh-CN/skills/fec-implement-from-design/SKILL.md +129 -0
- package/localized/zh-CN/skills/fec-implement-from-design/references/design-plan-template.md +36 -0
- package/localized/zh-CN/skills/fec-legacy-to-modern-migration/SKILL.md +46 -0
- package/localized/zh-CN/skills/fec-legacy-to-modern-migration/references/migration-execution-checklist.md +110 -0
- package/localized/zh-CN/skills/fec-legacy-to-modern-migration/references/migration-report-template.md +41 -0
- package/localized/zh-CN/skills/fec-legacy-to-modern-migration/references/migration-strategy-and-mapping.md +48 -0
- package/localized/zh-CN/skills/fec-legacy-web-standard/SKILL.md +35 -0
- package/localized/zh-CN/skills/fec-legacy-web-standard/references/legacy-patterns.md +85 -0
- package/localized/zh-CN/skills/fec-list-virtualization/SKILL.md +34 -0
- package/localized/zh-CN/skills/fec-list-virtualization/references/virtualization-patterns.md +80 -0
- package/localized/zh-CN/skills/fec-monorepo-project-standard/SKILL.md +142 -0
- package/localized/zh-CN/skills/fec-motion-interaction/SKILL.md +73 -0
- package/localized/zh-CN/skills/fec-motion-interaction/references/motion-patterns.md +112 -0
- package/localized/zh-CN/skills/fec-nextjs-project-standard/SKILL.md +123 -0
- package/localized/zh-CN/skills/fec-nuxt-project-standard/SKILL.md +122 -0
- package/localized/zh-CN/skills/fec-performance-optimization/SKILL.md +80 -0
- package/localized/zh-CN/skills/fec-performance-optimization/references/framework-performance-patterns.md +38 -0
- package/localized/zh-CN/skills/fec-performance-optimization/references/report-template.md +59 -0
- package/localized/zh-CN/skills/fec-pwa-implementation/SKILL.md +35 -0
- package/localized/zh-CN/skills/fec-pwa-implementation/references/manifest-and-icons.md +53 -0
- package/localized/zh-CN/skills/fec-pwa-implementation/references/service-worker-workbox.md +68 -0
- package/localized/zh-CN/skills/fec-react-project-standard/SKILL.md +91 -0
- package/localized/zh-CN/skills/fec-react-project-standard/references/react-performance-patterns.md +35 -0
- package/localized/zh-CN/skills/fec-react-project-standard/references/react-project-structure.md +197 -0
- package/localized/zh-CN/skills/fec-react-project-standard/references/react-quality-patterns.md +131 -0
- package/localized/zh-CN/skills/fec-react-project-standard/references/react-runtime-patterns.md +293 -0
- package/localized/zh-CN/skills/fec-refactor-clean/SKILL.md +51 -0
- package/localized/zh-CN/skills/fec-refactor-clean/references/report-template.md +42 -0
- package/localized/zh-CN/skills/fec-responsive-layout/SKILL.md +34 -0
- package/localized/zh-CN/skills/fec-responsive-layout/references/responsive-layout-patterns.md +70 -0
- package/localized/zh-CN/skills/fec-route-protection/SKILL.md +135 -0
- package/localized/zh-CN/skills/fec-security-review/SKILL.md +38 -0
- package/localized/zh-CN/skills/fec-security-review/references/report-template.md +27 -0
- package/localized/zh-CN/skills/fec-security-review/references/security-checklist.md +64 -0
- package/localized/zh-CN/skills/fec-source-driven-development/SKILL.md +87 -0
- package/localized/zh-CN/skills/fec-state-management/SKILL.md +140 -0
- package/localized/zh-CN/skills/fec-state-management/references/state-patterns.md +109 -0
- package/localized/zh-CN/skills/fec-storybook-component-doc/SKILL.md +37 -0
- package/localized/zh-CN/skills/fec-storybook-component-doc/references/story-patterns.md +118 -0
- package/localized/zh-CN/skills/fec-svg-animation/SKILL.md +148 -0
- package/localized/zh-CN/skills/fec-tailwind-design-system/SKILL.md +34 -0
- package/localized/zh-CN/skills/fec-tailwind-design-system/references/tailwind-system-patterns.md +59 -0
- package/localized/zh-CN/skills/fec-tdd-workflow/SKILL.md +52 -0
- package/localized/zh-CN/skills/fec-testing-strategy/SKILL.md +45 -0
- package/localized/zh-CN/skills/fec-typescript-project-standard/SKILL.md +54 -0
- package/{skills/fec-typescript-type-safety → localized/zh-CN/skills/fec-typescript-project-standard}/references/type-safety-patterns.md +1 -1
- package/{skills/fec-typescript-type-safety/SKILL.md → localized/zh-CN/skills/fec-typescript-project-standard/references/type-safety.md} +5 -14
- package/localized/zh-CN/skills/fec-ui-design/SKILL.md +108 -0
- package/localized/zh-CN/skills/fec-ui-design/data/product-rules.json +231 -0
- package/localized/zh-CN/skills/fec-ui-design/data/stack-ui-rules.json +146 -0
- package/localized/zh-CN/skills/fec-ui-design/data/style-archetypes.json +373 -0
- package/localized/zh-CN/skills/fec-ui-design/data/ux-quality-rules.json +203 -0
- package/localized/zh-CN/skills/fec-ui-design/references/design-intelligence.md +97 -0
- package/localized/zh-CN/skills/fec-ui-design/references/master-page-overrides.md +30 -0
- package/localized/zh-CN/skills/fec-ui-design/references/pre-delivery-checklist.md +39 -0
- package/localized/zh-CN/skills/fec-ui-design/scripts/design-system.mjs +648 -0
- package/localized/zh-CN/skills/fec-validation-fix/SKILL.md +51 -0
- package/localized/zh-CN/skills/fec-validation-fix/references/report-template.md +43 -0
- package/localized/zh-CN/skills/fec-vite-project-standard/SKILL.md +83 -0
- package/localized/zh-CN/skills/fec-vue3-project-standard/SKILL.md +58 -0
- package/localized/zh-CN/skills/fec-vue3-project-standard/references/vue3-composables-routing.md +162 -0
- package/localized/zh-CN/skills/fec-vue3-project-standard/references/vue3-performance-patterns.md +13 -0
- package/localized/zh-CN/skills/fec-vue3-project-standard/references/vue3-project-structure.md +176 -0
- package/localized/zh-CN/skills/fec-vue3-project-standard/references/vue3-state-api-quality.md +216 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/SKILL.md +61 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/data/starter-themes.json +59 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/references/chapter-craft.md +37 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/references/content-plan.md +38 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/references/recording-and-audio.md +33 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/references/theme-system.md +36 -0
- package/localized/zh-CN/skills/fec-web-video-presentation/scripts/create-video-presentation.mjs +430 -0
- package/localized/zh-CN/skills/fec-web-workers/SKILL.md +35 -0
- package/localized/zh-CN/skills/fec-web-workers/references/advanced-workers.md +54 -0
- package/localized/zh-CN/skills/fec-web-workers/references/basic-worker.md +67 -0
- package/localized/zh-CN/skills/metadata.json +1646 -0
- package/localized/zh-CN/skills/relations.json +755 -0
- package/localized/zh-CN/templates/claude/CLAUDE.md +106 -0
- package/localized/zh-CN/templates/claude/settings.json +62 -0
- package/localized/zh-CN/templates/codex/AGENTS.md +105 -0
- package/localized/zh-CN/templates/codex/config.toml +32 -0
- package/localized/zh-CN/templates/openclaw/AGENTS.md +101 -0
- package/localized/zh-CN/templates/openclaw/OPENCLAW-CONFIG.md +12 -0
- package/localized/zh-CN/templates/shared/rules/fec-agent-workflow.md +54 -0
- package/localized/zh-CN/templates/shared/rules/fec-api-layer.md +99 -0
- package/localized/zh-CN/templates/shared/rules/fec-ci-cd.md +40 -0
- package/localized/zh-CN/templates/shared/rules/fec-code-comments.md +33 -0
- package/localized/zh-CN/templates/shared/rules/fec-design-system.md +72 -0
- package/localized/zh-CN/templates/shared/rules/fec-error-handling.md +56 -0
- package/localized/zh-CN/templates/shared/rules/fec-git-conventions.md +73 -0
- package/localized/zh-CN/templates/shared/rules/fec-i18n.md +64 -0
- package/localized/zh-CN/templates/shared/rules/fec-naming-conventions.md +120 -0
- package/localized/zh-CN/templates/shared/rules/fec-performance.md +101 -0
- package/localized/zh-CN/templates/shared/rules/fec-react.md +82 -0
- package/localized/zh-CN/templates/shared/rules/fec-refactoring.md +47 -0
- package/localized/zh-CN/templates/shared/rules/fec-rendering-patterns.md +40 -0
- package/localized/zh-CN/templates/shared/rules/fec-responsive-design.md +50 -0
- package/localized/zh-CN/templates/shared/rules/fec-source-driven-development.md +31 -0
- package/localized/zh-CN/templates/shared/rules/fec-state-management.md +60 -0
- package/localized/zh-CN/templates/shared/rules/fec-testing.md +98 -0
- package/localized/zh-CN/templates/shared/rules/fec-typescript.md +112 -0
- package/localized/zh-CN/templates/shared/rules/fec-vue.md +64 -0
- package/localized/zh-CN/templates/shared/rules/fec-working-modes.md +50 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -3
- package/skills/TEMPLATE.md +74 -74
- package/skills/eval_queries.json +717 -619
- package/skills/fec-accessibility-check/SKILL.md +21 -21
- package/skills/fec-accessibility-check/references/report-template.md +12 -12
- package/skills/fec-accessibility-check/references/screen-reader-testing.md +24 -24
- package/skills/fec-alchemy/SKILL.md +50 -50
- package/skills/fec-alchemy/assets/absorption-plan-template.md +32 -32
- package/skills/fec-alchemy/assets/intake-template.md +55 -55
- package/skills/fec-alchemy/assets/review-checklist.md +23 -23
- package/skills/fec-alchemy/references/methodology.md +66 -66
- package/skills/fec-alchemy/references/originality-and-licensing.md +42 -42
- package/skills/fec-api-integration/SKILL.md +61 -61
- package/skills/fec-api-integration/references/integration-patterns.md +30 -30
- package/skills/fec-backend-requirements-handoff/SKILL.md +45 -0
- package/skills/fec-browser-storage/SKILL.md +16 -16
- package/skills/fec-browser-storage/references/storage-patterns.md +16 -16
- package/skills/fec-canvas-threejs/SKILL.md +21 -21
- package/skills/fec-canvas-threejs/references/rendering-patterns.md +10 -10
- package/skills/fec-canvas-threejs/references/shader-webgl-patterns.md +37 -37
- package/skills/fec-code-review/SKILL.md +94 -94
- package/skills/fec-code-review/references/report-template.md +13 -13
- package/skills/fec-component-testing/SKILL.md +39 -39
- package/skills/fec-data-fetching/SKILL.md +16 -16
- package/skills/fec-data-fetching/references/query-patterns.md +27 -27
- package/skills/fec-debug-framework/SKILL.md +95 -95
- package/skills/fec-debug-framework/references/report-template.md +34 -34
- package/skills/fec-dependency-upgrade/SKILL.md +17 -17
- package/skills/fec-dependency-upgrade/references/dependency-upgrade-workflow.md +31 -31
- package/skills/fec-doc-sync/SKILL.md +23 -23
- package/skills/fec-drawio-studio/SKILL.md +73 -0
- package/skills/fec-drawio-studio/data/THIRD_PARTY_NOTICES.md +10 -0
- package/skills/fec-drawio-studio/data/brand-icons.json +878 -0
- package/skills/fec-drawio-studio/data/shape-index.json +83570 -0
- package/skills/fec-drawio-studio/references/data-residency.md +27 -0
- package/skills/fec-drawio-studio/references/diagram-patterns.md +60 -0
- package/skills/fec-drawio-studio/references/flowchart-quality.md +49 -0
- package/skills/fec-drawio-studio/references/layout-and-export.md +72 -0
- package/skills/fec-drawio-studio/references/troubleshooting.md +29 -0
- package/skills/fec-drawio-studio/references/xml-and-mermaid.md +58 -0
- package/skills/fec-drawio-studio/scripts/brand-symbols.mjs +99 -0
- package/skills/fec-drawio-studio/scripts/diagram-lint.mjs +205 -0
- package/skills/fec-drawio-studio/scripts/diagram-url.mjs +102 -0
- package/skills/fec-drawio-studio/scripts/drawio-export.mjs +134 -0
- package/skills/fec-drawio-studio/scripts/layout-graph.mjs +107 -0
- package/skills/fec-drawio-studio/scripts/png-embed-fix.mjs +26 -0
- package/skills/fec-drawio-studio/scripts/scan-go-packages.mjs +36 -0
- package/skills/fec-drawio-studio/scripts/scan-js-modules.mjs +52 -0
- package/skills/fec-drawio-studio/scripts/scan-python-classes.mjs +30 -0
- package/skills/fec-drawio-studio/scripts/scan-python-modules.mjs +55 -0
- package/skills/fec-drawio-studio/scripts/scan-rust-modules.mjs +35 -0
- package/skills/fec-drawio-studio/scripts/scan-ts-modules.mjs +7 -0
- package/skills/fec-drawio-studio/scripts/shape-query.mjs +121 -0
- package/skills/fec-drawio-studio/scripts/studio-core.mjs +139 -0
- package/skills/fec-drawio-studio/styles/built-in/corporate.json +49 -0
- package/skills/fec-drawio-studio/styles/built-in/default.json +49 -0
- package/skills/fec-drawio-studio/styles/built-in/handdrawn.json +49 -0
- package/skills/fec-drawio-studio/styles/schema.json +112 -0
- package/skills/fec-e2e-testing/SKILL.md +24 -22
- package/skills/fec-e2e-testing/references/e2e-ci-reporting.md +62 -0
- package/skills/fec-e2e-testing/references/e2e-special-scenarios.md +52 -0
- package/skills/fec-e2e-testing/references/e2e-visual-regression.md +60 -0
- package/skills/fec-e2e-testing/references/playwright-patterns.md +6 -6
- package/skills/fec-form-handling/SKILL.md +21 -21
- package/skills/fec-form-handling/references/advanced-form-patterns.md +41 -41
- package/skills/fec-image-generation/SKILL.md +81 -0
- package/skills/fec-image-generation/assets/interactive-diagram.html +371 -0
- package/skills/fec-image-generation/references/artifact-routing.md +80 -0
- package/skills/fec-image-generation/references/diagram-workflows.md +74 -0
- package/skills/fec-image-generation/references/html-technical-diagrams.md +236 -0
- package/skills/fec-image-generation/references/png-qa-autofix.md +30 -0
- package/skills/fec-image-generation/scripts/export-diagram.mjs +364 -0
- package/skills/fec-image-generation/scripts/interactive-diagram-server.mjs +654 -0
- package/skills/fec-image-generation/scripts/png-qa.mjs +431 -0
- package/skills/fec-image-generation/scripts/tech-diagram-render.mjs +1397 -0
- package/skills/fec-implement-from-design/SKILL.md +81 -74
- package/skills/fec-implement-from-design/references/design-plan-template.md +16 -16
- package/skills/fec-legacy-to-modern-migration/SKILL.md +29 -173
- package/skills/fec-legacy-to-modern-migration/references/migration-execution-checklist.md +110 -0
- package/skills/fec-legacy-to-modern-migration/references/migration-report-template.md +37 -37
- package/skills/fec-legacy-to-modern-migration/references/migration-strategy-and-mapping.md +48 -0
- package/skills/fec-legacy-web-standard/SKILL.md +17 -17
- package/skills/fec-legacy-web-standard/references/legacy-patterns.md +16 -16
- package/skills/fec-list-virtualization/SKILL.md +16 -16
- package/skills/fec-list-virtualization/references/virtualization-patterns.md +31 -31
- package/skills/fec-monorepo-project-standard/SKILL.md +52 -52
- package/skills/fec-motion-interaction/SKILL.md +51 -44
- package/skills/fec-motion-interaction/references/motion-patterns.md +11 -11
- package/skills/fec-nextjs-project-standard/SKILL.md +64 -58
- package/skills/fec-nuxt-project-standard/SKILL.md +57 -57
- package/skills/fec-performance-optimization/SKILL.md +54 -50
- package/skills/fec-performance-optimization/references/framework-performance-patterns.md +38 -0
- package/skills/fec-performance-optimization/references/report-template.md +34 -28
- package/skills/fec-pwa-implementation/SKILL.md +17 -17
- package/skills/fec-pwa-implementation/references/manifest-and-icons.md +12 -12
- package/skills/fec-pwa-implementation/references/service-worker-workbox.md +13 -13
- package/skills/fec-react-project-standard/SKILL.md +72 -64
- package/skills/fec-react-project-standard/references/react-performance-patterns.md +35 -0
- package/skills/fec-react-project-standard/references/react-project-structure.md +197 -0
- package/skills/fec-react-project-standard/references/react-quality-patterns.md +131 -0
- package/skills/fec-react-project-standard/references/react-runtime-patterns.md +293 -0
- package/skills/fec-refactor-clean/SKILL.md +30 -30
- package/skills/fec-refactor-clean/references/report-template.md +23 -23
- package/skills/fec-responsive-layout/SKILL.md +17 -17
- package/skills/fec-responsive-layout/references/responsive-layout-patterns.md +39 -28
- package/skills/fec-route-protection/SKILL.md +26 -26
- package/skills/fec-security-review/SKILL.md +20 -20
- package/skills/fec-security-review/references/report-template.md +12 -12
- package/skills/fec-security-review/references/security-checklist.md +27 -27
- package/skills/fec-source-driven-development/SKILL.md +66 -40
- package/skills/fec-state-management/SKILL.md +34 -34
- package/skills/fec-state-management/references/state-patterns.md +19 -19
- package/skills/fec-storybook-component-doc/SKILL.md +19 -19
- package/skills/fec-storybook-component-doc/references/story-patterns.md +12 -12
- package/skills/fec-svg-animation/SKILL.md +32 -32
- package/skills/fec-tailwind-design-system/SKILL.md +17 -17
- package/skills/fec-tailwind-design-system/references/tailwind-system-patterns.md +36 -32
- package/skills/fec-tdd-workflow/SKILL.md +28 -28
- package/skills/fec-testing-strategy/SKILL.md +29 -29
- package/skills/fec-typescript-project-standard/SKILL.md +54 -0
- package/skills/fec-typescript-project-standard/references/type-safety-patterns.md +102 -0
- package/skills/fec-typescript-project-standard/references/type-safety.md +148 -0
- package/skills/fec-ui-design/SKILL.md +90 -72
- package/skills/fec-ui-design/data/style-archetypes.json +40 -0
- package/skills/fec-ui-design/data/ux-quality-rules.json +35 -0
- package/skills/fec-ui-design/references/design-intelligence.md +81 -21
- package/skills/fec-ui-design/references/master-page-overrides.md +15 -15
- package/skills/fec-ui-design/references/pre-delivery-checklist.md +38 -32
- package/skills/fec-ui-design/scripts/design-system.mjs +119 -1
- package/skills/fec-validation-fix/SKILL.md +26 -26
- package/skills/fec-validation-fix/references/report-template.md +25 -25
- package/skills/fec-vite-project-standard/SKILL.md +39 -39
- package/skills/fec-vue3-project-standard/SKILL.md +39 -30
- package/skills/fec-vue3-project-standard/references/vue3-composables-routing.md +163 -0
- package/skills/fec-vue3-project-standard/references/vue3-performance-patterns.md +13 -0
- package/skills/fec-vue3-project-standard/references/vue3-project-structure.md +177 -0
- package/skills/fec-vue3-project-standard/references/vue3-state-api-quality.md +216 -0
- package/skills/fec-web-video-presentation/SKILL.md +61 -0
- package/skills/fec-web-video-presentation/data/starter-themes.json +59 -0
- package/skills/fec-web-video-presentation/references/chapter-craft.md +37 -0
- package/skills/fec-web-video-presentation/references/content-plan.md +38 -0
- package/skills/fec-web-video-presentation/references/recording-and-audio.md +33 -0
- package/skills/fec-web-video-presentation/references/theme-system.md +36 -0
- package/skills/fec-web-video-presentation/scripts/create-video-presentation.mjs +430 -0
- package/skills/fec-web-workers/SKILL.md +16 -16
- package/skills/fec-web-workers/references/advanced-workers.md +13 -13
- package/skills/fec-web-workers/references/basic-worker.md +8 -8
- package/skills/metadata.json +402 -220
- package/skills/relations.json +120 -25
- package/templates/claude/CLAUDE.md +60 -60
- package/templates/codex/AGENTS.md +76 -76
- package/templates/codex/config.toml +4 -4
- package/templates/openclaw/AGENTS.md +68 -68
- package/templates/openclaw/OPENCLAW-CONFIG.md +8 -8
- package/templates/shared/rules/fec-agent-workflow.md +40 -40
- package/templates/shared/rules/fec-api-layer.md +52 -52
- package/templates/shared/rules/fec-ci-cd.md +29 -117
- package/templates/shared/rules/fec-code-comments.md +22 -22
- package/templates/shared/rules/fec-design-system.md +65 -241
- package/templates/shared/rules/fec-error-handling.md +41 -185
- package/templates/shared/rules/fec-git-conventions.md +42 -42
- package/templates/shared/rules/fec-i18n.md +48 -96
- package/templates/shared/rules/fec-naming-conventions.md +91 -91
- package/templates/shared/rules/fec-performance.md +78 -78
- package/templates/shared/rules/fec-react.md +58 -58
- package/templates/shared/rules/fec-refactoring.md +34 -30
- package/templates/shared/rules/fec-rendering-patterns.md +40 -183
- package/templates/shared/rules/fec-responsive-design.md +37 -214
- package/templates/shared/rules/fec-source-driven-development.md +22 -22
- package/templates/shared/rules/fec-state-management.md +45 -148
- package/templates/shared/rules/fec-testing.md +64 -64
- package/templates/shared/rules/fec-typescript.md +98 -341
- package/templates/shared/rules/fec-vue.md +42 -42
- package/templates/shared/rules/fec-working-modes.md +34 -34
- package/skills/fec-e2e-testing/references/e2e-advanced.md +0 -176
- package/skills/fec-react-project-standard/references/react-project-details.md +0 -642
- package/skills/fec-vue3-project-standard/references/vue3-project-details.md +0 -547
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import zlib from "node:zlib";
|
|
3
|
+
import { parseArgs, readText } from "./studio-core.mjs";
|
|
4
|
+
|
|
5
|
+
const args = parseArgs(process.argv.slice(2));
|
|
6
|
+
const input = args._[0];
|
|
7
|
+
if (!input) {
|
|
8
|
+
console.error(
|
|
9
|
+
"usage: node diagram-url.mjs <diagram-file> [--edit|--create] [--type xml|mermaid|csv] [--lightbox] [--dark true|false|auto] [--base-url URL] [--json|--shortcut]",
|
|
10
|
+
);
|
|
11
|
+
process.exit(2);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function encodeDiagramXml(xml) {
|
|
15
|
+
const encoded = encodeURIComponent(xml);
|
|
16
|
+
return zlib.deflateRawSync(Buffer.from(encoded, "utf8")).toString("base64");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function viewerUrl(xml, options = {}) {
|
|
20
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? "https://viewer.diagrams.net/");
|
|
21
|
+
return `${baseUrl}?highlight=0000ff&edit=_blank#R${encodeDiagramXml(xml)}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function editorUrl(xml, options = {}) {
|
|
25
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? "https://app.diagrams.net/");
|
|
26
|
+
return `${baseUrl}?offline=1#R${encodeDiagramXml(xml)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createUrl(content, options = {}) {
|
|
30
|
+
const type = normalizeType(options.type);
|
|
31
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? "https://app.diagrams.net/");
|
|
32
|
+
const params = new URLSearchParams();
|
|
33
|
+
if (options.lightbox) {
|
|
34
|
+
params.set("lightbox", "1");
|
|
35
|
+
params.set("edit", "_blank");
|
|
36
|
+
} else {
|
|
37
|
+
params.set("grid", "0");
|
|
38
|
+
params.set("pv", "0");
|
|
39
|
+
params.set("edit", "_blank");
|
|
40
|
+
}
|
|
41
|
+
params.set("border", String(options.border ?? 10));
|
|
42
|
+
const dark = normalizeDark(options.dark);
|
|
43
|
+
if (dark === true) params.set("dark", "1");
|
|
44
|
+
|
|
45
|
+
const payload = encodeURIComponent(
|
|
46
|
+
JSON.stringify({
|
|
47
|
+
type,
|
|
48
|
+
compressed: true,
|
|
49
|
+
data: encodeDiagramXml(content),
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
const query = params.toString();
|
|
53
|
+
return `${baseUrl}${query ? `?${query}` : ""}#create=${payload}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function internetShortcut(url) {
|
|
57
|
+
return `[InternetShortcut]\r\nURL=${url}\r\n`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeType(value = "xml") {
|
|
61
|
+
if (["xml", "mermaid", "csv"].includes(value)) return value;
|
|
62
|
+
throw new Error(`Unsupported diagram type "${value}". Expected xml, mermaid, or csv.`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeDark(value = "auto") {
|
|
66
|
+
if (value === true || value === "true") return true;
|
|
67
|
+
if (value === false || value === "false") return false;
|
|
68
|
+
if (value === "auto" || value === undefined) return "auto";
|
|
69
|
+
throw new Error(`Unsupported dark mode "${value}". Expected true, false, or auto.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeBaseUrl(value) {
|
|
73
|
+
return String(value).replace(/[?#].*$/, "").replace(/\/?$/, "/");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const content = readText(input);
|
|
78
|
+
const type = normalizeType(args.type ?? "xml");
|
|
79
|
+
if (type !== "xml" && !args.create) {
|
|
80
|
+
throw new Error("Mermaid and CSV inputs require --create because viewer/editor #R URLs only load XML.");
|
|
81
|
+
}
|
|
82
|
+
const options = {
|
|
83
|
+
baseUrl: args.baseUrl,
|
|
84
|
+
border: args.border,
|
|
85
|
+
dark: args.dark,
|
|
86
|
+
lightbox: Boolean(args.lightbox),
|
|
87
|
+
type,
|
|
88
|
+
};
|
|
89
|
+
const mode = args.create ? "create" : args.edit ? "edit" : "view";
|
|
90
|
+
const url = args.create ? createUrl(content, options) : args.edit ? editorUrl(content, options) : viewerUrl(content, options);
|
|
91
|
+
const shortcut = internetShortcut(url);
|
|
92
|
+
if (args.shortcut) {
|
|
93
|
+
process.stdout.write(shortcut);
|
|
94
|
+
} else if (args.json) {
|
|
95
|
+
process.stdout.write(`${JSON.stringify({ url, mode, type, windowsShortcut: shortcut }, null, 2)}\n`);
|
|
96
|
+
} else {
|
|
97
|
+
process.stdout.write(`${url}\n`);
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { formatResult, parseArgs } from "./studio-core.mjs";
|
|
7
|
+
|
|
8
|
+
const INSTALL_HINTS = [
|
|
9
|
+
"draw.io desktop CLI was not found.",
|
|
10
|
+
"Install draw.io desktop, then rerun the export.",
|
|
11
|
+
"Windows: winget install JGraph.Draw",
|
|
12
|
+
"macOS: brew install --cask drawio",
|
|
13
|
+
"Linux: install drawio from your package manager or snap install drawio",
|
|
14
|
+
"Manual download: https://github.com/jgraph/drawio-desktop/releases",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export function buildExportPlan(input, args = {}, env = process.env, platform = process.platform) {
|
|
18
|
+
const drawio = findDrawio(args.drawio, env, platform);
|
|
19
|
+
if (!drawio) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
title: "Draw.io Export",
|
|
23
|
+
errors: [INSTALL_HINTS.join("\n")],
|
|
24
|
+
command: null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const format = String(args.format ?? "png");
|
|
29
|
+
const output = String(args.output ?? defaultOutput(input, format));
|
|
30
|
+
const commandArgs = ["-x", "-f", format];
|
|
31
|
+
if (args.embed) commandArgs.push("-e");
|
|
32
|
+
if (args.scale) commandArgs.push("-s", String(args.scale));
|
|
33
|
+
if (args.width) commandArgs.push("--width", String(args.width));
|
|
34
|
+
if (args.border) commandArgs.push("--border", String(args.border));
|
|
35
|
+
if (args.crop) commandArgs.push("--crop");
|
|
36
|
+
if (args.page !== undefined) commandArgs.push("-p", String(args.page));
|
|
37
|
+
commandArgs.push("-o", output, input);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
ok: true,
|
|
41
|
+
title: "Draw.io Export",
|
|
42
|
+
drawio,
|
|
43
|
+
command: [drawio, ...commandArgs],
|
|
44
|
+
output,
|
|
45
|
+
format,
|
|
46
|
+
dryRun: Boolean(args.dryRun),
|
|
47
|
+
errors: [],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function findDrawio(explicitPath, env = process.env, platform = process.platform) {
|
|
52
|
+
if (explicitPath) return existingFile(explicitPath);
|
|
53
|
+
|
|
54
|
+
for (const command of ["drawio", "draw.io"]) {
|
|
55
|
+
const found = findOnPath(command, env, platform);
|
|
56
|
+
if (found) return found;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const candidate of defaultCandidates(env, platform)) {
|
|
60
|
+
const found = existingFile(candidate);
|
|
61
|
+
if (found) return found;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function findOnPath(command, env, platform) {
|
|
68
|
+
const pathValue = env.PATH ?? env.Path ?? "";
|
|
69
|
+
const dirs = pathValue.split(path.delimiter).filter(Boolean);
|
|
70
|
+
const extensions = platform === "win32" ? pathExtensions(env) : [""];
|
|
71
|
+
for (const dir of dirs) {
|
|
72
|
+
for (const ext of extensions) {
|
|
73
|
+
const candidate = path.join(dir, command.endsWith(ext) ? command : `${command}${ext}`);
|
|
74
|
+
const found = existingFile(candidate);
|
|
75
|
+
if (found) return found;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function pathExtensions(env) {
|
|
82
|
+
const configured = env.PATHEXT ? env.PATHEXT.split(";").filter(Boolean) : [".EXE", ".CMD", ".BAT", ""];
|
|
83
|
+
return [...new Set(["", ...configured, ...configured.map((item) => item.toLowerCase())])];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function defaultCandidates(env, platform) {
|
|
87
|
+
if (platform === "win32") {
|
|
88
|
+
return [
|
|
89
|
+
path.join(env.ProgramFiles ?? "C:\\Program Files", "draw.io", "draw.io.exe"),
|
|
90
|
+
path.join(env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)", "draw.io", "draw.io.exe"),
|
|
91
|
+
path.join(env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local"), "Programs", "draw.io", "draw.io.exe"),
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
if (platform === "darwin") {
|
|
95
|
+
return ["/Applications/draw.io.app/Contents/MacOS/draw.io"];
|
|
96
|
+
}
|
|
97
|
+
return ["/usr/bin/drawio", "/usr/local/bin/drawio", "/snap/bin/drawio"];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function existingFile(candidate) {
|
|
101
|
+
if (!candidate) return null;
|
|
102
|
+
const resolved = path.resolve(candidate);
|
|
103
|
+
return fs.existsSync(resolved) && fs.statSync(resolved).isFile() ? resolved : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function defaultOutput(input, format) {
|
|
107
|
+
return `${input.replace(/\.(drawio|xml)$/i, "")}.${format}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function printFailure(plan, args) {
|
|
111
|
+
const output = formatResult(plan, args.formatOutput ?? (args.json ? "json" : "markdown"));
|
|
112
|
+
process.stderr.write(output);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const args = parseArgs(process.argv.slice(2));
|
|
116
|
+
const input = args._[0];
|
|
117
|
+
if (!input) {
|
|
118
|
+
console.error("usage: node drawio-export.mjs <file.drawio> [--format png|svg|pdf] [--output file] [--scale 2] [--width 2000] [--border 20] [--dry-run] [--json]");
|
|
119
|
+
process.exit(2);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const plan = buildExportPlan(input, args);
|
|
123
|
+
if (!plan.ok) {
|
|
124
|
+
printFailure(plan, args);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (args.dryRun) {
|
|
129
|
+
process.stdout.write(formatResult(plan, args.formatOutput ?? (args.json ? "json" : "markdown")));
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const result = spawnSync(plan.command[0], plan.command.slice(1), { stdio: "inherit" });
|
|
134
|
+
process.exit(result.status ?? 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { parseArgs, printOrWrite, readText, snap, writeText, xmlEscape } from "./studio-core.mjs";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_W = 140;
|
|
6
|
+
const DEFAULT_H = 64;
|
|
7
|
+
const NODE_STYLE = "rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;";
|
|
8
|
+
const EDGE_STYLE = "edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;";
|
|
9
|
+
const PALETTE = [
|
|
10
|
+
["#dae8fc", "#6c8ebf"], ["#d5e8d4", "#82b366"], ["#ffe6cc", "#d79b00"],
|
|
11
|
+
["#e1d5e7", "#9673a6"], ["#fff2cc", "#d6b656"], ["#f8cecc", "#b85450"],
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export function buildDot(graph) {
|
|
15
|
+
const direction = String(graph.direction ?? "TB").toUpperCase() === "LR" ? "LR" : "TB";
|
|
16
|
+
const lines = [`digraph G { rankdir=${direction}; splines=ortho; node [shape=box fixedsize=true];`];
|
|
17
|
+
for (const node of graph.nodes) {
|
|
18
|
+
lines.push(`"${escapeDot(node.id)}" [width=${Number(node.width ?? DEFAULT_W) / 72} height=${Number(node.height ?? DEFAULT_H) / 72}];`);
|
|
19
|
+
}
|
|
20
|
+
for (const edge of graph.edges ?? []) lines.push(`"${escapeDot(edge.source)}" -> "${escapeDot(edge.target)}";`);
|
|
21
|
+
lines.push("}");
|
|
22
|
+
return lines.join("\n");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function runDot(dot) {
|
|
26
|
+
const proc = spawnSync("dot", ["-Tplain"], { input: dot, encoding: "utf8" });
|
|
27
|
+
if (proc.error?.code === "ENOENT") throw new Error("Graphviz `dot` not found on PATH");
|
|
28
|
+
if (proc.status !== 0) throw new Error(`Graphviz failed: ${proc.stderr.trim()}`);
|
|
29
|
+
const pos = new Map();
|
|
30
|
+
let height = 0;
|
|
31
|
+
for (const line of proc.stdout.split(/\r?\n/)) {
|
|
32
|
+
const parts = splitPlain(line);
|
|
33
|
+
if (parts[0] === "graph") height = Number(parts[3]);
|
|
34
|
+
if (parts[0] === "node") pos.set(parts[1], { x: Number(parts[2]), y: Number(parts[3]) });
|
|
35
|
+
}
|
|
36
|
+
return { height, pos };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function graphToDrawio(graph, layout) {
|
|
40
|
+
const groups = [...new Set((graph.nodes ?? []).map((node) => node.group).filter(Boolean))];
|
|
41
|
+
const groupColor = (group) => PALETTE[Math.max(0, groups.indexOf(group)) % PALETTE.length];
|
|
42
|
+
const cells = [];
|
|
43
|
+
const manifest = { nodes: {}, edges: graph.edges ?? [] };
|
|
44
|
+
for (const node of graph.nodes ?? []) {
|
|
45
|
+
const p = layout.pos.get(node.id);
|
|
46
|
+
if (!p) continue;
|
|
47
|
+
const w = Number(node.width ?? DEFAULT_W);
|
|
48
|
+
const h = Number(node.height ?? DEFAULT_H);
|
|
49
|
+
const x = snap(p.x * 72 - w / 2 + 40);
|
|
50
|
+
const y = snap((layout.height - p.y) * 72 - h / 2 + 40);
|
|
51
|
+
const style = node.style ?? (node.group ? tintStyle(groupColor(node.group)) : NODE_STYLE);
|
|
52
|
+
manifest.nodes[node.id] = { x, y, width: w, height: h, group: node.group ?? null };
|
|
53
|
+
cells.push(` <mxCell id="${xmlEscape(node.id)}" value="${xmlEscape(node.label ?? node.id)}" style="${xmlEscape(style)}" vertex="1" parent="1">
|
|
54
|
+
<mxGeometry x="${x}" y="${y}" width="${w}" height="${h}" as="geometry"/>
|
|
55
|
+
</mxCell>`);
|
|
56
|
+
}
|
|
57
|
+
let edgeIndex = 0;
|
|
58
|
+
for (const edge of graph.edges ?? []) {
|
|
59
|
+
cells.push(` <mxCell id="edge_${edgeIndex++}" value="${xmlEscape(edge.label ?? "")}" style="${EDGE_STYLE}" edge="1" parent="1" source="${xmlEscape(edge.source)}" target="${xmlEscape(edge.target)}">
|
|
60
|
+
<mxGeometry relative="1" as="geometry"/>
|
|
61
|
+
</mxCell>`);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
manifest,
|
|
65
|
+
xml: `<mxfile host="drawio">
|
|
66
|
+
<diagram name="Page-1">
|
|
67
|
+
<mxGraphModel grid="1" gridSize="10" page="1" pageWidth="1100" pageHeight="850">
|
|
68
|
+
<root>
|
|
69
|
+
<mxCell id="0"/>
|
|
70
|
+
<mxCell id="1" parent="0"/>
|
|
71
|
+
${cells.join("\n")}
|
|
72
|
+
</root>
|
|
73
|
+
</mxGraphModel>
|
|
74
|
+
</diagram>
|
|
75
|
+
</mxfile>
|
|
76
|
+
`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function tintStyle([fill, stroke]) {
|
|
81
|
+
return `rounded=1;whiteSpace=wrap;html=1;fillColor=${fill};strokeColor=${stroke};`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function escapeDot(value) {
|
|
85
|
+
return String(value).replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function splitPlain(line) {
|
|
89
|
+
return line.match(/"[^"]*"|\S+/g)?.map((part) => part.replace(/^"|"$/g, "")) ?? [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const args = parseArgs(process.argv.slice(2));
|
|
93
|
+
const input = args._[0];
|
|
94
|
+
if (!input) {
|
|
95
|
+
console.error("usage: node layout-graph.mjs graph.json --output diagram.drawio [--manifest layout.json]");
|
|
96
|
+
process.exit(2);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const graph = JSON.parse(readText(input));
|
|
100
|
+
const rendered = graphToDrawio(graph, runDot(buildDot(graph)));
|
|
101
|
+
printOrWrite(rendered.xml, args.output);
|
|
102
|
+
if (args.manifest) writeText(args.manifest, `${JSON.stringify(rendered.manifest, null, 2)}\n`);
|
|
103
|
+
if (!args.output) process.stderr.write(`rendered ${graph.nodes.length} nodes\n`);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(`error: ${error.message}`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { parseArgs } from "./studio-core.mjs";
|
|
4
|
+
|
|
5
|
+
const IEND = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82]);
|
|
6
|
+
const IEND_TAIL = IEND.subarray(4);
|
|
7
|
+
|
|
8
|
+
export function repairPngBuffer(buffer) {
|
|
9
|
+
if (buffer.subarray(-12).equals(IEND)) return { buffer, changed: false, reason: "already-valid" };
|
|
10
|
+
if (buffer.subarray(-4).equals(IEND.subarray(0, 4))) {
|
|
11
|
+
return { buffer: Buffer.concat([buffer, IEND_TAIL]), changed: true, reason: "appended-iend-tail" };
|
|
12
|
+
}
|
|
13
|
+
return { buffer, changed: false, reason: "not-truncated-iend" };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const args = parseArgs(process.argv.slice(2));
|
|
17
|
+
const input = args._[0];
|
|
18
|
+
if (!input) {
|
|
19
|
+
console.error("usage: node png-embed-fix.mjs <file.png> [--json]");
|
|
20
|
+
process.exit(2);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const repaired = repairPngBuffer(fs.readFileSync(input));
|
|
24
|
+
if (repaired.changed) fs.writeFileSync(input, repaired.buffer);
|
|
25
|
+
const result = { file: input, changed: repaired.changed, reason: repaired.reason };
|
|
26
|
+
process.stdout.write(args.json ? `${JSON.stringify(result, null, 2)}\n` : `${input}: ${repaired.reason}\n`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseArgs, printOrWrite, reduceTransitive, toPosix, walkFiles } from "./studio-core.mjs";
|
|
5
|
+
|
|
6
|
+
export function scanGoPackages(root) {
|
|
7
|
+
const absRoot = path.resolve(root);
|
|
8
|
+
const mod = fs.existsSync(path.join(absRoot, "go.mod")) ? fs.readFileSync(path.join(absRoot, "go.mod"), "utf8").match(/^module\s+(.+)$/m)?.[1] : "";
|
|
9
|
+
const files = walkFiles(absRoot, (file) => file.endsWith(".go") && !file.endsWith("_test.go"));
|
|
10
|
+
const packageDirs = [...new Set(files.map((file) => path.dirname(file)))].sort();
|
|
11
|
+
const dirToId = new Map(packageDirs.map((dir) => [dir, mod ? `${mod}/${toPosix(path.relative(absRoot, dir))}`.replace(/\/$/, "") : toPosix(path.relative(absRoot, dir) || ".")]));
|
|
12
|
+
const nodes = [...dirToId.entries()].map(([dir, id]) => ({ id, label: path.basename(dir), group: toPosix(path.dirname(path.relative(absRoot, dir))).replace(".", "") || undefined }));
|
|
13
|
+
const ids = new Set(nodes.map((node) => node.id));
|
|
14
|
+
const edges = [];
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
const source = dirToId.get(path.dirname(file));
|
|
17
|
+
const text = fs.readFileSync(file, "utf8");
|
|
18
|
+
for (const spec of extractGoImports(text)) if (ids.has(spec)) edges.push({ source, target: spec });
|
|
19
|
+
}
|
|
20
|
+
return { direction: "LR", nodes, edges: reduceTransitive(nodes, edges) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractGoImports(text) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const match of text.matchAll(/import\s+(?:[._A-Za-z]\w*\s+)?(?:"([^"]+)"|\(([\s\S]*?)\))/g)) {
|
|
26
|
+
if (match[1]) out.push(match[1]);
|
|
27
|
+
if (match[2]) for (const line of match[2].split(/\r?\n/)) {
|
|
28
|
+
const item = line.match(/"([^"]+)"/)?.[1];
|
|
29
|
+
if (item) out.push(item);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const args = parseArgs(process.argv.slice(2));
|
|
36
|
+
printOrWrite(`${JSON.stringify(scanGoPackages(args._[0] ?? "."), null, 2)}\n`, args.output);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parseArgs, printOrWrite, reduceTransitive, toPosix, walkFiles } from "./studio-core.mjs";
|
|
6
|
+
|
|
7
|
+
const JS_EXTS = [".js", ".jsx", ".mjs", ".cjs"];
|
|
8
|
+
const IMPORT_RE = /\bimport\s+(?:[^'"]+?\s+from\s+)?['"]([^'"]+)['"]|\bexport\s+[^'"]*?\s+from\s+['"]([^'"]+)['"]|\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)|\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
9
|
+
|
|
10
|
+
export function scanJavaScriptModules(root, extensions = JS_EXTS) {
|
|
11
|
+
return scanModules(root, extensions);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function scanModules(root, extensions) {
|
|
15
|
+
const absRoot = path.resolve(root);
|
|
16
|
+
const files = walkFiles(absRoot, (file) => extensions.includes(path.extname(file)));
|
|
17
|
+
const byBase = new Map();
|
|
18
|
+
const nodes = files.map((file) => {
|
|
19
|
+
const id = toPosix(path.relative(absRoot, file)).replace(/\.[^.]+$/, "");
|
|
20
|
+
byBase.set(path.resolve(file), id);
|
|
21
|
+
return { id, label: id.split("/").pop(), group: id.includes("/") ? id.split("/").slice(0, -1).join("/") : undefined };
|
|
22
|
+
});
|
|
23
|
+
const edges = [];
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
const source = byBase.get(path.resolve(file));
|
|
26
|
+
const text = fs.readFileSync(file, "utf8");
|
|
27
|
+
for (const match of text.matchAll(IMPORT_RE)) {
|
|
28
|
+
const spec = match[1] ?? match[2] ?? match[3] ?? match[4];
|
|
29
|
+
if (!spec?.startsWith(".")) continue;
|
|
30
|
+
const target = resolveRelative(file, spec, extensions, byBase);
|
|
31
|
+
if (target) edges.push({ source, target });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { direction: "LR", nodes, edges: reduceTransitive(nodes, edges) };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveRelative(fromFile, spec, extensions, byBase) {
|
|
38
|
+
const base = path.resolve(path.dirname(fromFile), spec);
|
|
39
|
+
const candidates = [base, ...extensions.map((ext) => base + ext), ...extensions.map((ext) => path.join(base, `index${ext}`))];
|
|
40
|
+
for (const candidate of candidates) {
|
|
41
|
+
const id = byBase.get(path.resolve(candidate));
|
|
42
|
+
if (id) return id;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
48
|
+
const args = parseArgs(process.argv.slice(2));
|
|
49
|
+
const root = args._[0] ?? ".";
|
|
50
|
+
const graph = scanJavaScriptModules(root);
|
|
51
|
+
printOrWrite(`${JSON.stringify(graph, null, 2)}\n`, args.output);
|
|
52
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseArgs, printOrWrite, reduceTransitive, toPosix, walkFiles } from "./studio-core.mjs";
|
|
5
|
+
|
|
6
|
+
export function scanPythonClasses(root) {
|
|
7
|
+
const absRoot = path.resolve(root);
|
|
8
|
+
const files = walkFiles(absRoot, (file) => file.endsWith(".py"));
|
|
9
|
+
const classes = [];
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
const moduleId = toPosix(path.relative(absRoot, file)).replace(/\/__init__\.py$/, "").replace(/\.py$/, "").replaceAll("/", ".");
|
|
12
|
+
const text = fs.readFileSync(file, "utf8");
|
|
13
|
+
for (const match of text.matchAll(/^\s*class\s+([A-Za-z_]\w*)\s*(?:\(([^)]*)\))?:/gm)) {
|
|
14
|
+
const id = `${moduleId}.${match[1]}`.replace(/^\./, "");
|
|
15
|
+
classes.push({ id, name: match[1], bases: (match[2] ?? "").split(",").map((item) => item.trim().split(".").pop()).filter(Boolean), moduleId });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const byName = new Map(classes.map((cls) => [cls.name, cls.id]));
|
|
19
|
+
const nodes = classes.map((cls) => ({ id: cls.id, label: cls.name, group: cls.moduleId }));
|
|
20
|
+
const edges = [];
|
|
21
|
+
for (const cls of classes) {
|
|
22
|
+
for (const base of cls.bases) {
|
|
23
|
+
if (byName.has(base)) edges.push({ source: cls.id, target: byName.get(base) });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { direction: "TB", nodes, edges: reduceTransitive(nodes, edges) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const args = parseArgs(process.argv.slice(2));
|
|
30
|
+
printOrWrite(`${JSON.stringify(scanPythonClasses(args._[0] ?? "."), null, 2)}\n`, args.output);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseArgs, printOrWrite, reduceTransitive, toPosix, walkFiles } from "./studio-core.mjs";
|
|
5
|
+
|
|
6
|
+
export function scanPythonModules(root) {
|
|
7
|
+
const absRoot = path.resolve(root);
|
|
8
|
+
const files = walkFiles(absRoot, (file) => file.endsWith(".py"));
|
|
9
|
+
const moduleByFile = new Map();
|
|
10
|
+
const nodes = files.map((file) => {
|
|
11
|
+
const rel = toPosix(path.relative(absRoot, file));
|
|
12
|
+
const id = rel.replace(/\/__init__\.py$/, "").replace(/\.py$/, "").replaceAll("/", ".");
|
|
13
|
+
moduleByFile.set(file, id || path.basename(absRoot));
|
|
14
|
+
return { id: id || path.basename(absRoot), label: path.basename(id || absRoot), group: id.includes(".") ? id.split(".").slice(0, -1).join(".") : undefined };
|
|
15
|
+
});
|
|
16
|
+
const ids = new Set(nodes.map((node) => node.id));
|
|
17
|
+
const edges = [];
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
const source = moduleByFile.get(file);
|
|
20
|
+
const text = fs.readFileSync(file, "utf8");
|
|
21
|
+
for (const line of text.split(/\r?\n/)) {
|
|
22
|
+
const importMatch = line.match(/^\s*import\s+(.+)/);
|
|
23
|
+
if (importMatch) {
|
|
24
|
+
for (const item of importMatch[1].split(",")) addEdge(source, item.trim().split(/\s+as\s+/)[0], ids, edges);
|
|
25
|
+
}
|
|
26
|
+
const fromMatch = line.match(/^\s*from\s+([.\w]+)\s+import\s+(.+)/);
|
|
27
|
+
if (fromMatch) addEdge(source, resolveFromImport(source, fromMatch[1]), ids, edges);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { direction: "LR", nodes, edges: reduceTransitive(nodes, edges) };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function addEdge(source, target, ids, edges) {
|
|
34
|
+
if (!target) return;
|
|
35
|
+
const parts = target.split(".");
|
|
36
|
+
while (parts.length) {
|
|
37
|
+
const candidate = parts.join(".");
|
|
38
|
+
if (ids.has(candidate)) {
|
|
39
|
+
edges.push({ source, target: candidate });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
parts.pop();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolveFromImport(source, spec) {
|
|
47
|
+
if (!spec.startsWith(".")) return spec;
|
|
48
|
+
const dots = spec.match(/^\.+/)?.[0].length ?? 0;
|
|
49
|
+
const suffix = spec.slice(dots);
|
|
50
|
+
const base = source.split(".").slice(0, Math.max(0, source.split(".").length - dots));
|
|
51
|
+
return [...base, suffix].filter(Boolean).join(".");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const args = parseArgs(process.argv.slice(2));
|
|
55
|
+
printOrWrite(`${JSON.stringify(scanPythonModules(args._[0] ?? "."), null, 2)}\n`, args.output);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseArgs, printOrWrite, reduceTransitive, toPosix, walkFiles } from "./studio-core.mjs";
|
|
5
|
+
|
|
6
|
+
export function scanRustModules(root) {
|
|
7
|
+
const srcRoot = fs.existsSync(path.join(root, "src")) ? path.join(root, "src") : root;
|
|
8
|
+
const absRoot = path.resolve(srcRoot);
|
|
9
|
+
const files = walkFiles(absRoot, (file) => file.endsWith(".rs"));
|
|
10
|
+
const fileToId = new Map();
|
|
11
|
+
const nodes = files.map((file) => {
|
|
12
|
+
let id = toPosix(path.relative(absRoot, file)).replace(/\.rs$/, "").replace(/\/mod$/, "").replace(/^(lib|main)$/, "crate").replaceAll("/", "::");
|
|
13
|
+
fileToId.set(file, id);
|
|
14
|
+
return { id, label: id.split("::").pop(), group: id.includes("::") ? id.split("::").slice(0, -1).join("::") : undefined };
|
|
15
|
+
});
|
|
16
|
+
const ids = new Set(nodes.map((node) => node.id));
|
|
17
|
+
const edges = [];
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
const source = fileToId.get(file);
|
|
20
|
+
const text = fs.readFileSync(file, "utf8");
|
|
21
|
+
for (const match of text.matchAll(/\buse\s+(crate|self|super)::([^;{]+)[;{]/g)) {
|
|
22
|
+
const target = match[2].split("::").filter(Boolean)[0];
|
|
23
|
+
const candidates = [...ids].filter((id) => id === target || id.endsWith(`::${target}`));
|
|
24
|
+
if (candidates[0]) edges.push({ source, target: candidates[0] });
|
|
25
|
+
}
|
|
26
|
+
for (const match of text.matchAll(/\bmod\s+([A-Za-z_]\w*)\s*;/g)) {
|
|
27
|
+
const candidates = [...ids].filter((id) => id === match[1] || id.endsWith(`::${match[1]}`));
|
|
28
|
+
if (candidates[0]) edges.push({ source, target: candidates[0] });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { direction: "LR", nodes, edges: reduceTransitive(nodes, edges) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const args = parseArgs(process.argv.slice(2));
|
|
35
|
+
printOrWrite(`${JSON.stringify(scanRustModules(args._[0] ?? "."), null, 2)}\n`, args.output);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs, printOrWrite } from "./studio-core.mjs";
|
|
3
|
+
import { scanModules } from "./scan-js-modules.mjs";
|
|
4
|
+
|
|
5
|
+
const args = parseArgs(process.argv.slice(2));
|
|
6
|
+
const graph = scanModules(args._[0] ?? ".", [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
7
|
+
printOrWrite(`${JSON.stringify(graph, null, 2)}\n`, args.output);
|