@oriro/orirocli 0.1.11 → 0.3.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/ATTRIBUTION.md +53 -53
- package/LICENSE +21 -21
- package/README.md +10 -9
- package/dist/cli.js +1866 -190
- package/package.json +65 -64
- package/skills/1password/SKILL.md +118 -118
- package/skills/1password/references/cli-examples.md +29 -29
- package/skills/1password/references/get-started.md +21 -21
- package/skills/21stdev/SKILL.md +64 -64
- package/skills/algorithmic-art/LICENSE +21 -21
- package/skills/algorithmic-art/SKILL.md +446 -446
- package/skills/algorithmic-art/templates/generator_template.js +223 -223
- package/skills/algorithmic-art/templates/viewer.html +598 -598
- package/skills/apple-notes/SKILL.md +81 -81
- package/skills/apple-reminders/SKILL.md +122 -122
- package/skills/bear-notes/SKILL.md +111 -111
- package/skills/blogwatcher/SKILL.md +73 -73
- package/skills/blucli/SKILL.md +51 -51
- package/skills/brand-guidelines/LICENSE +21 -21
- package/skills/brand-guidelines/SKILL.md +76 -76
- package/skills/business/biz-analysis/LICENSE +21 -21
- package/skills/business/biz-analysis/SKILL.md +103 -103
- package/skills/business/biz-corporate-strategy/LICENSE +21 -21
- package/skills/business/biz-corporate-strategy/SKILL.md +76 -76
- package/skills/business/biz-customer-success/LICENSE +21 -21
- package/skills/business/biz-customer-success/SKILL.md +55 -55
- package/skills/business/biz-entrepreneurship/LICENSE +21 -21
- package/skills/business/biz-entrepreneurship/SKILL.md +72 -72
- package/skills/business/biz-hr/LICENSE +21 -21
- package/skills/business/biz-hr/SKILL.md +67 -67
- package/skills/business/biz-international/LICENSE +21 -21
- package/skills/business/biz-international/SKILL.md +51 -51
- package/skills/business/biz-leadership/LICENSE +21 -21
- package/skills/business/biz-leadership/SKILL.md +106 -106
- package/skills/business/biz-marketing-strategy/LICENSE +21 -21
- package/skills/business/biz-marketing-strategy/SKILL.md +119 -119
- package/skills/business/biz-negotiation/LICENSE +21 -21
- package/skills/business/biz-negotiation/SKILL.md +152 -152
- package/skills/business/biz-operations/LICENSE +21 -21
- package/skills/business/biz-operations/SKILL.md +74 -74
- package/skills/business/biz-project/LICENSE +21 -21
- package/skills/business/biz-project/SKILL.md +203 -203
- package/skills/business/biz-risk/LICENSE +21 -21
- package/skills/business/biz-risk/SKILL.md +85 -85
- package/skills/business/biz-sales/LICENSE +21 -21
- package/skills/business/biz-sales/SKILL.md +92 -92
- package/skills/business/biz-startup-ops/LICENSE +21 -21
- package/skills/business/biz-startup-ops/SKILL.md +70 -70
- package/skills/business/biz-strategy/LICENSE +21 -21
- package/skills/business/biz-strategy/SKILL.md +233 -233
- package/skills/business/biz-supply-chain-advanced/LICENSE +21 -21
- package/skills/business/biz-supply-chain-advanced/SKILL.md +68 -68
- package/skills/business/fin-chartered-exams/LICENSE +21 -21
- package/skills/business/fin-chartered-exams/SKILL.md +69 -69
- package/skills/camsnap/SKILL.md +49 -49
- package/skills/canvas/SKILL.md +82 -82
- package/skills/canvas-design/LICENSE +21 -21
- package/skills/canvas-design/SKILL.md +140 -140
- package/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -94
- package/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -93
- package/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -93
- package/skills/coding-agent/SKILL.md +146 -146
- package/skills/communication/comm-business-writing/LICENSE +21 -21
- package/skills/communication/comm-business-writing/SKILL.md +67 -67
- package/skills/communication/comm-cross-cultural/LICENSE +21 -21
- package/skills/communication/comm-cross-cultural/SKILL.md +88 -88
- package/skills/communication/comm-journalism/LICENSE +21 -21
- package/skills/communication/comm-journalism/SKILL.md +81 -81
- package/skills/communication/comm-linguistics/LICENSE +21 -21
- package/skills/communication/comm-linguistics/SKILL.md +82 -82
- package/skills/communication/comm-negotiation/LICENSE +21 -21
- package/skills/communication/comm-negotiation/SKILL.md +120 -120
- package/skills/communication/comm-presentations/LICENSE +21 -21
- package/skills/communication/comm-presentations/SKILL.md +93 -93
- package/skills/communication/comm-public-speaking/LICENSE +21 -21
- package/skills/communication/comm-public-speaking/SKILL.md +68 -68
- package/skills/communication/comm-writing/LICENSE +21 -21
- package/skills/communication/comm-writing/SKILL.md +69 -69
- package/skills/craft/ai-engineering/LICENSE +21 -21
- package/skills/craft/ai-engineering/SKILL.md +828 -828
- package/skills/craft/app-builder-guide/LICENSE +21 -21
- package/skills/craft/app-builder-guide/SKILL.md +332 -332
- package/skills/craft/become-an-ai-engineer-26/CONTRIBUTING.md +46 -46
- package/skills/craft/become-an-ai-engineer-26/LICENSE +21 -21
- package/skills/craft/become-an-ai-engineer-26/README.md +270 -270
- package/skills/craft/become-an-ai-engineer-26/SKILL.md +667 -667
- package/skills/craft/become-an-ai-engineer-26/community/BUILDS.md +13 -13
- package/skills/craft/become-an-ai-engineer-26/community/DISCUSSIONS.md +8 -8
- package/skills/craft/become-an-ai-engineer-26/phases/phase-0-mental-models/README.md +14 -14
- package/skills/craft/become-an-ai-engineer-26/phases/phase-0-mental-models/project/TEMPLATE.md +33 -33
- package/skills/craft/become-an-ai-engineer-26/phases/phase-1-first-agent/README.md +25 -25
- package/skills/craft/become-an-ai-engineer-26/phases/phase-1-first-agent/code/raw_loop.py +126 -126
- package/skills/craft/become-an-ai-engineer-26/phases/phase-2-architecture/README.md +17 -17
- package/skills/craft/become-an-ai-engineer-26/phases/phase-3-harness/README.md +17 -17
- package/skills/craft/become-an-ai-engineer-26/phases/phase-4-evals/README.md +21 -21
- package/skills/craft/become-an-ai-engineer-26/phases/phase-4-evals/code/.github/workflows/eval.yml +40 -40
- package/skills/craft/become-an-ai-engineer-26/phases/phase-5-production/README.md +16 -16
- package/skills/craft/become-an-ai-engineer-26/projects/1-mobile-app-slm/README.md +11 -11
- package/skills/craft/become-an-ai-engineer-26/projects/2-self-improving-coder/README.md +11 -11
- package/skills/craft/become-an-ai-engineer-26/projects/3-video-editor-agent/README.md +11 -11
- package/skills/craft/become-an-ai-engineer-26/projects/4-personal-life-os/README.md +12 -12
- package/skills/craft/become-an-ai-engineer-26/projects/5-enterprise-workflow/README.md +12 -12
- package/skills/craft/become-an-ai-engineer-26/references/benchmark-numbers.md +41 -41
- package/skills/craft/become-an-ai-engineer-26/references/mhc-stable-training.md +73 -73
- package/skills/craft/become-an-ai-engineer-26/references/stack-decisions.md +37 -37
- package/skills/craft/become-an-ai-engineer-26/references/yarn-context-extension.md +123 -123
- package/skills/craft/codex-result-handling/LICENSE +21 -21
- package/skills/craft/codex-result-handling/SKILL.md +26 -26
- package/skills/craft/debug-and-build-methodology/LICENSE +21 -21
- package/skills/craft/debug-and-build-methodology/SKILL.md +432 -432
- package/skills/craft/design/LICENSE +21 -21
- package/skills/craft/design/SKILL.md +274 -274
- package/skills/craft/dev/LICENSE +21 -21
- package/skills/craft/dev/SKILL.md +12 -12
- package/skills/craft/dev/release.md +85 -85
- package/skills/craft/dev/roll.md +50 -50
- package/skills/craft/doc-coauthoring/LICENSE +21 -21
- package/skills/craft/doc-coauthoring/SKILL.md +397 -397
- package/skills/craft/focus/LICENSE +21 -21
- package/skills/craft/focus/SKILL.md +432 -432
- package/skills/craft/focus/UPSTREAM_README.md +233 -233
- package/skills/craft/gh/LICENSE +21 -21
- package/skills/craft/gh/SKILL.md +84 -84
- package/skills/craft/gh-skill/LICENSE +21 -21
- package/skills/craft/gh-skill/SKILL.md +121 -121
- package/skills/craft/godmode/LICENSE +21 -21
- package/skills/craft/godmode/SKILL.md +87 -87
- package/skills/craft/godmode/references/android-launch.md +680 -680
- package/skills/craft/godmode/references/data-gcp.md +1038 -1038
- package/skills/craft/godmode/references/expo-eas.md +816 -816
- package/skills/craft/godmode/references/ios-launch.md +734 -734
- package/skills/craft/google-ai-latest/LICENSE +21 -21
- package/skills/craft/google-ai-latest/SKILL.md +682 -682
- package/skills/craft/gpt-5-4-prompting/LICENSE +21 -21
- package/skills/craft/gpt-5-4-prompting/SKILL.md +63 -63
- package/skills/craft/gpt-5-4-prompting/references/codex-prompt-antipatterns.md +101 -101
- package/skills/craft/gpt-5-4-prompting/references/codex-prompt-recipes.md +150 -150
- package/skills/craft/gpt-5-4-prompting/references/prompt-blocks.md +172 -172
- package/skills/craft/grill-me/LICENSE +21 -21
- package/skills/craft/grill-me/SKILL.md +13 -13
- package/skills/craft/idea-to-deploy/LICENSE +21 -21
- package/skills/craft/idea-to-deploy/SKILL.md +292 -292
- package/skills/craft/idea-to-deploy/references/auth-playbook.md +195 -195
- package/skills/craft/idea-to-deploy/references/gcp-deployment.md +268 -268
- package/skills/craft/idea-to-deploy/references/stack-selection.md +117 -117
- package/skills/craft/image-generation-engineer/LICENSE +21 -21
- package/skills/craft/image-generation-engineer/SKILL.md +183 -183
- package/skills/craft/image-generation-engineer/references/architectures.md +260 -260
- package/skills/craft/image-generation-engineer/references/foundations.md +107 -107
- package/skills/craft/image-generation-engineer/references/inference-and-serving.md +253 -253
- package/skills/craft/image-generation-engineer/references/training.md +149 -149
- package/skills/craft/marketing/LICENSE +21 -21
- package/skills/craft/marketing/SKILL.md +1954 -1954
- package/skills/craft/master-architect/LICENSE +21 -21
- package/skills/craft/master-architect/SKILL.md +361 -361
- package/skills/craft/master-architect/references/ai-ml.md +317 -317
- package/skills/craft/master-architect/references/architecture.md +268 -268
- package/skills/craft/master-architect/references/auth-playbook.md +195 -195
- package/skills/craft/master-architect/references/cloud.md +323 -323
- package/skills/craft/master-architect/references/cyber.md +839 -839
- package/skills/craft/master-architect/references/data-eng.md +366 -366
- package/skills/craft/master-architect/references/devops.md +550 -550
- package/skills/craft/master-architect/references/gcp-deployment.md +268 -268
- package/skills/craft/master-architect/references/languages.md +748 -748
- package/skills/craft/master-architect/references/legacy.md +240 -240
- package/skills/craft/master-architect/references/mobile.md +447 -447
- package/skills/craft/master-architect/references/patterns.md +451 -451
- package/skills/craft/master-architect/references/saas-patterns.md +379 -379
- package/skills/craft/master-architect/references/sdlc.md +349 -349
- package/skills/craft/master-architect/references/stack-selection.md +117 -117
- package/skills/craft/oriro-ui-2026/LICENSE +21 -21
- package/skills/craft/oriro-ui-2026/SKILL.md +329 -329
- package/skills/craft/playwright-cli/LICENSE +21 -21
- package/skills/craft/playwright-cli/SKILL.md +393 -393
- package/skills/craft/playwright-cli/references/element-attributes.md +23 -23
- package/skills/craft/playwright-cli/references/playwright-tests.md +39 -39
- package/skills/craft/playwright-cli/references/request-mocking.md +87 -87
- package/skills/craft/playwright-cli/references/running-code.md +240 -240
- package/skills/craft/playwright-cli/references/session-management.md +226 -226
- package/skills/craft/playwright-cli/references/spec-driven-testing.md +312 -312
- package/skills/craft/playwright-cli/references/storage-state.md +275 -275
- package/skills/craft/playwright-cli/references/test-generation.md +134 -134
- package/skills/craft/playwright-cli/references/tracing.md +142 -142
- package/skills/craft/playwright-cli/references/video-recording.md +150 -150
- package/skills/craft/remotion-best-practices/LICENSE +21 -21
- package/skills/craft/remotion-best-practices/SKILL.md +345 -345
- package/skills/craft/remotion-best-practices/rules/3d.md +86 -86
- package/skills/craft/remotion-best-practices/rules/assets/charts-bar-chart.tsx +165 -165
- package/skills/craft/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +89 -89
- package/skills/craft/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +101 -101
- package/skills/craft/remotion-best-practices/rules/audio-visualization.md +195 -195
- package/skills/craft/remotion-best-practices/rules/audio.md +167 -167
- package/skills/craft/remotion-best-practices/rules/calculate-metadata.md +118 -118
- package/skills/craft/remotion-best-practices/rules/compositions.md +132 -132
- package/skills/craft/remotion-best-practices/rules/display-captions.md +176 -176
- package/skills/craft/remotion-best-practices/rules/ffmpeg.md +34 -34
- package/skills/craft/remotion-best-practices/rules/get-audio-duration.md +58 -58
- package/skills/craft/remotion-best-practices/rules/get-video-dimensions.md +68 -68
- package/skills/craft/remotion-best-practices/rules/get-video-duration.md +60 -60
- package/skills/craft/remotion-best-practices/rules/gifs.md +135 -135
- package/skills/craft/remotion-best-practices/rules/google-fonts.md +72 -72
- package/skills/craft/remotion-best-practices/rules/html-in-canvas.md +122 -122
- package/skills/craft/remotion-best-practices/rules/images.md +67 -67
- package/skills/craft/remotion-best-practices/rules/import-srt-captions.md +69 -69
- package/skills/craft/remotion-best-practices/rules/light-leaks.md +73 -73
- package/skills/craft/remotion-best-practices/rules/local-fonts.md +65 -65
- package/skills/craft/remotion-best-practices/rules/lottie.md +67 -67
- package/skills/craft/remotion-best-practices/rules/maplibre.md +441 -441
- package/skills/craft/remotion-best-practices/rules/measuring-dom-nodes.md +34 -34
- package/skills/craft/remotion-best-practices/rules/measuring-text.md +140 -140
- package/skills/craft/remotion-best-practices/rules/parameters.md +109 -109
- package/skills/craft/remotion-best-practices/rules/sequencing.md +144 -144
- package/skills/craft/remotion-best-practices/rules/sfx.md +30 -30
- package/skills/craft/remotion-best-practices/rules/silence-detection.md +73 -73
- package/skills/craft/remotion-best-practices/rules/subtitles.md +36 -36
- package/skills/craft/remotion-best-practices/rules/tailwind.md +11 -11
- package/skills/craft/remotion-best-practices/rules/text-animations.md +20 -20
- package/skills/craft/remotion-best-practices/rules/timing.md +130 -130
- package/skills/craft/remotion-best-practices/rules/transcribe-captions.md +70 -70
- package/skills/craft/remotion-best-practices/rules/transitions.md +193 -193
- package/skills/craft/remotion-best-practices/rules/transparent-videos.md +102 -102
- package/skills/craft/remotion-best-practices/rules/trimming.md +51 -51
- package/skills/craft/remotion-best-practices/rules/videos.md +169 -169
- package/skills/craft/remotion-best-practices/rules/voiceover.md +94 -94
- package/skills/craft/supabase-postgres-best-practices/CHANGELOG.md +25 -25
- package/skills/craft/supabase-postgres-best-practices/LICENSE +21 -21
- package/skills/craft/supabase-postgres-best-practices/SKILL.md +69 -69
- package/skills/craft/supabase-postgres-best-practices/references/_contributing.md +166 -166
- package/skills/craft/supabase-postgres-best-practices/references/_sections.md +47 -47
- package/skills/craft/supabase-postgres-best-practices/references/_template.md +34 -34
- package/skills/craft/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -55
- package/skills/craft/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -49
- package/skills/craft/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -46
- package/skills/craft/supabase-postgres-best-practices/references/conn-limits.md +44 -44
- package/skills/craft/supabase-postgres-best-practices/references/conn-pooling.md +41 -41
- package/skills/craft/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -46
- package/skills/craft/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -54
- package/skills/craft/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -53
- package/skills/craft/supabase-postgres-best-practices/references/data-pagination.md +50 -50
- package/skills/craft/supabase-postgres-best-practices/references/data-upsert.md +50 -50
- package/skills/craft/supabase-postgres-best-practices/references/lock-advisory.md +56 -56
- package/skills/craft/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -68
- package/skills/craft/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -50
- package/skills/craft/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -54
- package/skills/craft/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -45
- package/skills/craft/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -55
- package/skills/craft/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -55
- package/skills/craft/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -44
- package/skills/craft/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -40
- package/skills/craft/supabase-postgres-best-practices/references/query-index-types.md +48 -48
- package/skills/craft/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -43
- package/skills/craft/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -45
- package/skills/craft/supabase-postgres-best-practices/references/schema-constraints.md +80 -80
- package/skills/craft/supabase-postgres-best-practices/references/schema-data-types.md +46 -46
- package/skills/craft/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -59
- package/skills/craft/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -55
- package/skills/craft/supabase-postgres-best-practices/references/schema-partitioning.md +55 -55
- package/skills/craft/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -61
- package/skills/craft/supabase-postgres-best-practices/references/security-privileges.md +54 -54
- package/skills/craft/supabase-postgres-best-practices/references/security-rls-basics.md +50 -50
- package/skills/craft/supabase-postgres-best-practices/references/security-rls-performance.md +63 -63
- package/skills/craft/uipm-banner-design/LICENSE +21 -21
- package/skills/craft/uipm-banner-design/SKILL.md +201 -201
- package/skills/craft/uipm-banner-design/references/banner-sizes-and-styles.md +129 -129
- package/skills/craft/uipm-brand/LICENSE +21 -21
- package/skills/craft/uipm-brand/SKILL.md +104 -104
- package/skills/craft/uipm-brand/references/approval-checklist.md +184 -184
- package/skills/craft/uipm-brand/references/asset-organization.md +167 -167
- package/skills/craft/uipm-brand/references/brand-guideline-template.md +161 -161
- package/skills/craft/uipm-brand/references/color-palette-management.md +203 -203
- package/skills/craft/uipm-brand/references/consistency-checklist.md +105 -105
- package/skills/craft/uipm-brand/references/logo-usage-rules.md +204 -204
- package/skills/craft/uipm-brand/references/messaging-framework.md +91 -91
- package/skills/craft/uipm-brand/references/typography-specifications.md +265 -265
- package/skills/craft/uipm-brand/references/update.md +128 -128
- package/skills/craft/uipm-brand/references/visual-identity.md +109 -109
- package/skills/craft/uipm-brand/references/voice-framework.md +99 -99
- package/skills/craft/uipm-brand/scripts/extract-colors.cjs +333 -333
- package/skills/craft/uipm-brand/scripts/inject-brand-context.cjs +324 -324
- package/skills/craft/uipm-brand/scripts/sync-brand-to-tokens.cjs +269 -269
- package/skills/craft/uipm-brand/scripts/validate-asset.cjs +361 -361
- package/skills/craft/uipm-brand/templates/brand-guidelines-starter.md +280 -280
- package/skills/craft/uipm-design/LICENSE +21 -21
- package/skills/craft/uipm-design/SKILL.md +305 -305
- package/skills/craft/uipm-design/data/cip/deliverables.csv +50 -50
- package/skills/craft/uipm-design/data/cip/industries.csv +20 -20
- package/skills/craft/uipm-design/data/cip/mockup-contexts.csv +20 -20
- package/skills/craft/uipm-design/data/cip/styles.csv +20 -20
- package/skills/craft/uipm-design/data/icon/styles.csv +16 -16
- package/skills/craft/uipm-design/data/logo/colors.csv +56 -56
- package/skills/craft/uipm-design/data/logo/industries.csv +56 -56
- package/skills/craft/uipm-design/data/logo/styles.csv +56 -56
- package/skills/craft/uipm-design/references/banner-sizes-and-styles.md +129 -129
- package/skills/craft/uipm-design/references/cip-deliverable-guide.md +111 -111
- package/skills/craft/uipm-design/references/cip-design.md +121 -121
- package/skills/craft/uipm-design/references/cip-prompt-engineering.md +94 -94
- package/skills/craft/uipm-design/references/cip-style-guide.md +76 -76
- package/skills/craft/uipm-design/references/design-routing.md +226 -226
- package/skills/craft/uipm-design/references/icon-design.md +122 -122
- package/skills/craft/uipm-design/references/logo-color-psychology.md +113 -113
- package/skills/craft/uipm-design/references/logo-design.md +92 -92
- package/skills/craft/uipm-design/references/logo-prompt-engineering.md +176 -176
- package/skills/craft/uipm-design/references/logo-style-guide.md +129 -129
- package/skills/craft/uipm-design/references/slides-copywriting-formulas.md +92 -92
- package/skills/craft/uipm-design/references/slides-create.md +5 -5
- package/skills/craft/uipm-design/references/slides-html-template.md +374 -374
- package/skills/craft/uipm-design/references/slides-layout-patterns.md +155 -155
- package/skills/craft/uipm-design/references/slides-strategies.md +97 -97
- package/skills/craft/uipm-design/references/slides.md +42 -42
- package/skills/craft/uipm-design/references/social-photos-design.md +353 -353
- package/skills/craft/uipm-design/scripts/cip/core.py +215 -215
- package/skills/craft/uipm-design/scripts/cip/generate.py +484 -484
- package/skills/craft/uipm-design/scripts/cip/render-html.py +424 -424
- package/skills/craft/uipm-design/scripts/cip/search.py +127 -127
- package/skills/craft/uipm-design/scripts/icon/generate.py +487 -487
- package/skills/craft/uipm-design/scripts/logo/core.py +175 -175
- package/skills/craft/uipm-design/scripts/logo/generate.py +362 -362
- package/skills/craft/uipm-design/scripts/logo/search.py +114 -114
- package/skills/craft/uipm-design-system/LICENSE +21 -21
- package/skills/craft/uipm-design-system/SKILL.md +255 -255
- package/skills/craft/uipm-design-system/data/slide-backgrounds.csv +11 -11
- package/skills/craft/uipm-design-system/data/slide-charts.csv +26 -26
- package/skills/craft/uipm-design-system/data/slide-color-logic.csv +14 -14
- package/skills/craft/uipm-design-system/data/slide-copy.csv +26 -26
- package/skills/craft/uipm-design-system/data/slide-layout-logic.csv +16 -16
- package/skills/craft/uipm-design-system/data/slide-layouts.csv +26 -26
- package/skills/craft/uipm-design-system/data/slide-strategies.csv +16 -16
- package/skills/craft/uipm-design-system/data/slide-typography.csv +15 -15
- package/skills/craft/uipm-design-system/references/component-specs.md +236 -236
- package/skills/craft/uipm-design-system/references/component-tokens.md +214 -214
- package/skills/craft/uipm-design-system/references/primitive-tokens.md +199 -199
- package/skills/craft/uipm-design-system/references/semantic-tokens.md +215 -215
- package/skills/craft/uipm-design-system/references/states-and-variants.md +243 -243
- package/skills/craft/uipm-design-system/references/tailwind-integration.md +257 -257
- package/skills/craft/uipm-design-system/references/token-architecture.md +226 -226
- package/skills/craft/uipm-design-system/scripts/embed-tokens.cjs +97 -97
- package/skills/craft/uipm-design-system/scripts/fetch-background.py +317 -317
- package/skills/craft/uipm-design-system/scripts/generate-slide.py +753 -753
- package/skills/craft/uipm-design-system/scripts/generate-tokens.cjs +213 -213
- package/skills/craft/uipm-design-system/scripts/html-token-validator.py +327 -327
- package/skills/craft/uipm-design-system/scripts/search-slides.py +218 -218
- package/skills/craft/uipm-design-system/scripts/slide-token-validator.py +35 -35
- package/skills/craft/uipm-design-system/scripts/slide_search_core.py +453 -453
- package/skills/craft/uipm-design-system/scripts/validate-tokens.cjs +254 -254
- package/skills/craft/uipm-design-system/templates/design-tokens-starter.json +143 -143
- package/skills/craft/uipm-slides/LICENSE +21 -21
- package/skills/craft/uipm-slides/SKILL.md +45 -45
- package/skills/craft/uipm-slides/references/copywriting-formulas.md +92 -92
- package/skills/craft/uipm-slides/references/create.md +5 -5
- package/skills/craft/uipm-slides/references/html-template.md +374 -374
- package/skills/craft/uipm-slides/references/layout-patterns.md +155 -155
- package/skills/craft/uipm-slides/references/slide-strategies.md +97 -97
- package/skills/craft/uipm-ui-ux-pro-max/LICENSE +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/SKILL.md +678 -678
- package/skills/craft/uipm-ui-ux-pro-max/data/_sync_all.py +414 -414
- package/skills/craft/uipm-ui-ux-pro-max/data/app-interface.csv +30 -30
- package/skills/craft/uipm-ui-ux-pro-max/data/charts.csv +26 -26
- package/skills/craft/uipm-ui-ux-pro-max/data/colors.csv +161 -161
- package/skills/craft/uipm-ui-ux-pro-max/data/design.csv +1775 -1775
- package/skills/craft/uipm-ui-ux-pro-max/data/draft.csv +1778 -1778
- package/skills/craft/uipm-ui-ux-pro-max/data/google-fonts.csv +1924 -1924
- package/skills/craft/uipm-ui-ux-pro-max/data/icons.csv +105 -105
- package/skills/craft/uipm-ui-ux-pro-max/data/landing.csv +35 -35
- package/skills/craft/uipm-ui-ux-pro-max/data/products.csv +162 -162
- package/skills/craft/uipm-ui-ux-pro-max/data/react-performance.csv +45 -45
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/angular.csv +51 -51
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/astro.csv +54 -54
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/flutter.csv +53 -53
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -53
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/laravel.csv +51 -51
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -51
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -59
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/react-native.csv +52 -52
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/react.csv +54 -54
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/shadcn.csv +61 -61
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/svelte.csv +54 -54
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/threejs.csv +54 -54
- package/skills/craft/uipm-ui-ux-pro-max/data/stacks/vue.csv +50 -50
- package/skills/craft/uipm-ui-ux-pro-max/data/styles.csv +85 -85
- package/skills/craft/uipm-ui-ux-pro-max/data/typography.csv +74 -74
- package/skills/craft/uipm-ui-ux-pro-max/data/ui-reasoning.csv +162 -162
- package/skills/craft/uipm-ui-ux-pro-max/data/ux-guidelines.csv +99 -99
- package/skills/craft/uipm-ui-ux-pro-max/scripts/core.py +262 -262
- package/skills/craft/uipm-ui-ux-pro-max/scripts/design_system.py +1148 -1148
- package/skills/craft/uipm-ui-ux-pro-max/scripts/search.py +114 -114
- package/skills/craft/uipm-ui-ux-pro-max/templates/base/quick-reference.md +297 -297
- package/skills/craft/uipm-ui-ux-pro-max/templates/base/skill-content.md +375 -375
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/agent.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/augment.json +18 -18
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/claude.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/codebuddy.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/codex.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/continue.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/copilot.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/cursor.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/droid.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/gemini.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/kilocode.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/kiro.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/opencode.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/qoder.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/roocode.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/trae.json +21 -21
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/warp.json +18 -18
- package/skills/craft/uipm-ui-ux-pro-max/templates/platforms/windsurf.json +21 -21
- package/skills/craft/vercel-optimize/AGENTS.md +48 -48
- package/skills/craft/vercel-optimize/CONTRIBUTING.md +41 -41
- package/skills/craft/vercel-optimize/LICENSE +21 -21
- package/skills/craft/vercel-optimize/README.md +91 -91
- package/skills/craft/vercel-optimize/SKILL.md +325 -325
- package/skills/craft/vercel-optimize/lib/auth-route.mjs +23 -23
- package/skills/craft/vercel-optimize/lib/budget-summary.mjs +208 -208
- package/skills/craft/vercel-optimize/lib/citations.mjs +147 -147
- package/skills/craft/vercel-optimize/lib/cost-coverage.mjs +162 -162
- package/skills/craft/vercel-optimize/lib/dedup-recs.mjs +340 -340
- package/skills/craft/vercel-optimize/lib/deep-dive.mjs +371 -371
- package/skills/craft/vercel-optimize/lib/display-labels.mjs +219 -219
- package/skills/craft/vercel-optimize/lib/extract-claims.mjs +640 -640
- package/skills/craft/vercel-optimize/lib/framework-support.mjs +69 -69
- package/skills/craft/vercel-optimize/lib/gates/build-minutes-fanout.mjs +73 -73
- package/skills/craft/vercel-optimize/lib/gates/cold-start.mjs +72 -72
- package/skills/craft/vercel-optimize/lib/gates/contract.mjs +82 -82
- package/skills/craft/vercel-optimize/lib/gates/cwv-poor.mjs +95 -95
- package/skills/craft/vercel-optimize/lib/gates/external-api-slow.mjs +60 -60
- package/skills/craft/vercel-optimize/lib/gates/hard-gates.mjs +70 -70
- package/skills/craft/vercel-optimize/lib/gates/index.mjs +45 -45
- package/skills/craft/vercel-optimize/lib/gates/isr-overrevalidation.mjs +62 -62
- package/skills/craft/vercel-optimize/lib/gates/middleware-heavy.mjs +53 -53
- package/skills/craft/vercel-optimize/lib/gates/observability-events-attribution.mjs +58 -58
- package/skills/craft/vercel-optimize/lib/gates/platform-bot-protection.mjs +123 -123
- package/skills/craft/vercel-optimize/lib/gates/platform-fluid-compute.mjs +94 -94
- package/skills/craft/vercel-optimize/lib/gates/region-misconfig.mjs +71 -71
- package/skills/craft/vercel-optimize/lib/gates/route-errors.mjs +95 -95
- package/skills/craft/vercel-optimize/lib/gates/scanner-driven.mjs +150 -150
- package/skills/craft/vercel-optimize/lib/gates/select-candidates.mjs +137 -137
- package/skills/craft/vercel-optimize/lib/gates/slow-route.mjs +97 -97
- package/skills/craft/vercel-optimize/lib/gates/types.d.ts +38 -38
- package/skills/craft/vercel-optimize/lib/gates/uncached-route.mjs +103 -103
- package/skills/craft/vercel-optimize/lib/gates/usage-spike-triage.mjs +122 -122
- package/skills/craft/vercel-optimize/lib/grade-recommendation.mjs +170 -170
- package/skills/craft/vercel-optimize/lib/impact-label.mjs +128 -128
- package/skills/craft/vercel-optimize/lib/impact-magnitude.mjs +66 -66
- package/skills/craft/vercel-optimize/lib/investigation-brief.mjs +751 -751
- package/skills/craft/vercel-optimize/lib/observation-safety.mjs +217 -217
- package/skills/craft/vercel-optimize/lib/project-facts.mjs +101 -101
- package/skills/craft/vercel-optimize/lib/queries.mjs +333 -333
- package/skills/craft/vercel-optimize/lib/reconcile-candidates.mjs +388 -388
- package/skills/craft/vercel-optimize/lib/render-report.mjs +1065 -1065
- package/skills/craft/vercel-optimize/lib/repo-root.mjs +97 -97
- package/skills/craft/vercel-optimize/lib/route-normalize.mjs +224 -224
- package/skills/craft/vercel-optimize/lib/sanitizers/bot-protection-certainty.mjs +56 -56
- package/skills/craft/vercel-optimize/lib/sanitizers/cache-tag-invalidation-certainty.mjs +33 -33
- package/skills/craft/vercel-optimize/lib/sanitizers/count-correct.mjs +53 -53
- package/skills/craft/vercel-optimize/lib/sanitizers/function-duration-invocations.mjs +32 -32
- package/skills/craft/vercel-optimize/lib/sanitizers/index.mjs +87 -87
- package/skills/craft/vercel-optimize/lib/sanitizers/middleware-conflict.mjs +37 -37
- package/skills/craft/vercel-optimize/lib/sanitizers/missing-citation.mjs +16 -16
- package/skills/craft/vercel-optimize/lib/sanitizers/pre-release.mjs +75 -75
- package/skills/craft/vercel-optimize/lib/sanitizers/rate-limit.mjs +73 -73
- package/skills/craft/vercel-optimize/lib/sanitizers/rendering-mode-mislabel.mjs +42 -42
- package/skills/craft/vercel-optimize/lib/sanitizers/undeclared-dep.mjs +110 -110
- package/skills/craft/vercel-optimize/lib/sanitizers/vercel-directive-strip.mjs +37 -37
- package/skills/craft/vercel-optimize/lib/sanitizers/window-units.mjs +26 -26
- package/skills/craft/vercel-optimize/lib/scanners/cache-components-suspense-dedupe.mjs +114 -114
- package/skills/craft/vercel-optimize/lib/scanners/edge-heavy-import.mjs +102 -102
- package/skills/craft/vercel-optimize/lib/scanners/force-dynamic.mjs +39 -39
- package/skills/craft/vercel-optimize/lib/scanners/headers-in-page.mjs +43 -43
- package/skills/craft/vercel-optimize/lib/scanners/index.mjs +35 -35
- package/skills/craft/vercel-optimize/lib/scanners/large-static-asset.mjs +93 -93
- package/skills/craft/vercel-optimize/lib/scanners/max-age-without-s-maxage.mjs +47 -47
- package/skills/craft/vercel-optimize/lib/scanners/middleware-broad-matcher.mjs +53 -53
- package/skills/craft/vercel-optimize/lib/scanners/missing-cache-headers.mjs +97 -97
- package/skills/craft/vercel-optimize/lib/scanners/prisma-include-tree.mjs +39 -39
- package/skills/craft/vercel-optimize/lib/scanners/region-pin-in-config.mjs +89 -89
- package/skills/craft/vercel-optimize/lib/scanners/source-maps-production.mjs +33 -33
- package/skills/craft/vercel-optimize/lib/scanners/sveltekit-prerender-missing.mjs +47 -47
- package/skills/craft/vercel-optimize/lib/scanners/turbo-force-bypass.mjs +136 -136
- package/skills/craft/vercel-optimize/lib/scanners/unoptimized-image.mjs +127 -127
- package/skills/craft/vercel-optimize/lib/scanners/use-cache-date-stamp.mjs +112 -112
- package/skills/craft/vercel-optimize/lib/support-topics.mjs +365 -365
- package/skills/craft/vercel-optimize/lib/throttle.mjs +280 -280
- package/skills/craft/vercel-optimize/lib/util.mjs +17 -17
- package/skills/craft/vercel-optimize/lib/vercel.mjs +855 -855
- package/skills/craft/vercel-optimize/lib/verify-claim.mjs +1843 -1843
- package/skills/craft/vercel-optimize/lib/workspace-resolver.mjs +552 -552
- package/skills/craft/vercel-optimize/metadata.json +14 -14
- package/skills/craft/vercel-optimize/references/candidates.md +176 -176
- package/skills/craft/vercel-optimize/references/data-collection.md +224 -224
- package/skills/craft/vercel-optimize/references/docs-library.json +683 -683
- package/skills/craft/vercel-optimize/references/doctrine.md +108 -108
- package/skills/craft/vercel-optimize/references/observability-plus.md +109 -109
- package/skills/craft/vercel-optimize/references/playbooks/README.md +57 -57
- package/skills/craft/vercel-optimize/references/playbooks/ai-application.md +32 -32
- package/skills/craft/vercel-optimize/references/playbooks/api-service.md +30 -30
- package/skills/craft/vercel-optimize/references/playbooks/content-site.md +30 -30
- package/skills/craft/vercel-optimize/references/playbooks/ecommerce.md +30 -30
- package/skills/craft/vercel-optimize/references/playbooks/marketing.md +30 -30
- package/skills/craft/vercel-optimize/references/playbooks/saas.md +31 -31
- package/skills/craft/vercel-optimize/references/playbooks/sveltekit.md +75 -75
- package/skills/craft/vercel-optimize/references/recommendations.md +214 -214
- package/skills/craft/vercel-optimize/references/scanner-patterns.md +266 -266
- package/skills/craft/vercel-optimize/references/scoring.md +208 -208
- package/skills/craft/vercel-optimize/references/support-topics/README.md +50 -50
- package/skills/craft/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/auth-preserving-parallelization.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md +32 -32
- package/skills/craft/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md +32 -32
- package/skills/craft/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md +32 -32
- package/skills/craft/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md +33 -33
- package/skills/craft/vercel-optimize/references/support-topics/database-egress-pooling-region.md +32 -32
- package/skills/craft/vercel-optimize/references/support-topics/dynamic-rendering-traps.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/external-api-critical-path-platform.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/external-api-critical-path.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md +26 -26
- package/skills/craft/vercel-optimize/references/support-topics/fluid-compute-caveats.md +26 -26
- package/skills/craft/vercel-optimize/references/support-topics/function-duration-io-and-after.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/function-invocation-reduction.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/image-optimization-cost-control.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md +28 -28
- package/skills/craft/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/next-script-third-party-strategy.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md +33 -33
- package/skills/craft/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/observability-events-cost-attribution.md +27 -27
- package/skills/craft/vercel-optimize/references/support-topics/post-response-work-waituntil.md +26 -26
- package/skills/craft/vercel-optimize/references/support-topics/route-error-durable-offload.md +33 -33
- package/skills/craft/vercel-optimize/references/support-topics/route-error-runtime-limits.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/usage-spike-triage.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md +31 -31
- package/skills/craft/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md +30 -30
- package/skills/craft/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md +32 -32
- package/skills/craft/vercel-optimize/references/verification.md +102 -102
- package/skills/craft/vercel-optimize/references/voice.md +76 -76
- package/skills/craft/vercel-optimize/scripts/budget-summary.mjs +58 -58
- package/skills/craft/vercel-optimize/scripts/build-docs.mjs +76 -76
- package/skills/craft/vercel-optimize/scripts/check-citations.mjs +91 -91
- package/skills/craft/vercel-optimize/scripts/check-docs-fresh.mjs +100 -100
- package/skills/craft/vercel-optimize/scripts/collect-signals.mjs +638 -638
- package/skills/craft/vercel-optimize/scripts/collect-sub-agent-outputs.mjs +306 -306
- package/skills/craft/vercel-optimize/scripts/deep-dive.mjs +358 -358
- package/skills/craft/vercel-optimize/scripts/gate-investigations.mjs +178 -178
- package/skills/craft/vercel-optimize/scripts/merge-signals.mjs +203 -203
- package/skills/craft/vercel-optimize/scripts/prepare-investigation-brief.mjs +249 -249
- package/skills/craft/vercel-optimize/scripts/reconcile-candidates.mjs +69 -69
- package/skills/craft/vercel-optimize/scripts/render-report.mjs +462 -462
- package/skills/craft/vercel-optimize/scripts/scan-codebase.mjs +361 -361
- package/skills/craft/vercel-optimize/scripts/verify-and-regen.mjs +379 -379
- package/skills/craft/vercel-optimize/scripts/verify-finding.mjs +21 -21
- package/skills/craft/web-design-guidelines/LICENSE +21 -21
- package/skills/craft/web-design-guidelines/SKILL.md +43 -43
- package/skills/craft/zero-to-live/LICENSE +21 -21
- package/skills/craft/zero-to-live/SKILL.md +422 -422
- package/skills/creative/creative-3d-modeling/LICENSE +21 -21
- package/skills/creative/creative-3d-modeling/SKILL.md +70 -70
- package/skills/creative/creative-architecture/LICENSE +21 -21
- package/skills/creative/creative-architecture/SKILL.md +94 -94
- package/skills/creative/creative-design-principles/LICENSE +21 -21
- package/skills/creative/creative-design-principles/SKILL.md +95 -95
- package/skills/creative/creative-fashion-advanced/LICENSE +21 -21
- package/skills/creative/creative-fashion-advanced/SKILL.md +68 -68
- package/skills/creative/creative-fashion-design/LICENSE +21 -21
- package/skills/creative/creative-fashion-design/SKILL.md +66 -66
- package/skills/creative/creative-game-design/LICENSE +21 -21
- package/skills/creative/creative-game-design/SKILL.md +77 -77
- package/skills/creative/creative-industrial-design/LICENSE +21 -21
- package/skills/creative/creative-industrial-design/SKILL.md +57 -57
- package/skills/creative/creative-interior-design/LICENSE +21 -21
- package/skills/creative/creative-interior-design/SKILL.md +59 -59
- package/skills/creative/creative-music-theory/LICENSE +21 -21
- package/skills/creative/creative-music-theory/SKILL.md +98 -98
- package/skills/creative/creative-photography/LICENSE +21 -21
- package/skills/creative/creative-photography/SKILL.md +87 -87
- package/skills/creative/creative-textile-science/LICENSE +21 -21
- package/skills/creative/creative-textile-science/SKILL.md +67 -67
- package/skills/creative/creative-ux/LICENSE +21 -21
- package/skills/creative/creative-ux/SKILL.md +81 -81
- package/skills/creative/creative-video/LICENSE +21 -21
- package/skills/creative/creative-video/SKILL.md +84 -84
- package/skills/creative/creative-writing-craft/LICENSE +21 -21
- package/skills/creative/creative-writing-craft/SKILL.md +91 -91
- package/skills/diagram-maker/SKILL.md +56 -56
- package/skills/diagram-maker/references/excalidraw-patterns.md +85 -85
- package/skills/diagram-maker/references/svg-template.md +112 -112
- package/skills/discord/SKILL.md +140 -140
- package/skills/education/edu-adult-learning/LICENSE +21 -21
- package/skills/education/edu-adult-learning/SKILL.md +81 -81
- package/skills/education/edu-africa-multilingual/LICENSE +21 -21
- package/skills/education/edu-africa-multilingual/SKILL.md +55 -55
- package/skills/education/edu-arabic/LICENSE +21 -21
- package/skills/education/edu-arabic/SKILL.md +60 -60
- package/skills/education/edu-australia-nz/LICENSE +21 -21
- package/skills/education/edu-australia-nz/SKILL.md +48 -48
- package/skills/education/edu-china-mandarin/LICENSE +21 -21
- package/skills/education/edu-china-mandarin/SKILL.md +58 -58
- package/skills/education/edu-critical-thinking/LICENSE +21 -21
- package/skills/education/edu-critical-thinking/SKILL.md +86 -86
- package/skills/education/edu-curriculum/LICENSE +21 -21
- package/skills/education/edu-curriculum/SKILL.md +87 -87
- package/skills/education/edu-ed-tech/LICENSE +21 -21
- package/skills/education/edu-ed-tech/SKILL.md +73 -73
- package/skills/education/edu-france/LICENSE +21 -21
- package/skills/education/edu-france/SKILL.md +42 -42
- package/skills/education/edu-germany/LICENSE +21 -21
- package/skills/education/edu-germany/SKILL.md +46 -46
- package/skills/education/edu-india-competitive/LICENSE +21 -21
- package/skills/education/edu-india-competitive/SKILL.md +159 -159
- package/skills/education/edu-india-east/LICENSE +21 -21
- package/skills/education/edu-india-east/SKILL.md +60 -60
- package/skills/education/edu-india-hindi/LICENSE +21 -21
- package/skills/education/edu-india-hindi/SKILL.md +107 -107
- package/skills/education/edu-india-south/LICENSE +21 -21
- package/skills/education/edu-india-south/SKILL.md +64 -64
- package/skills/education/edu-india-west/LICENSE +21 -21
- package/skills/education/edu-india-west/SKILL.md +68 -68
- package/skills/education/edu-indonesia-malay/LICENSE +21 -21
- package/skills/education/edu-indonesia-malay/SKILL.md +57 -57
- package/skills/education/edu-international-ib/LICENSE +21 -21
- package/skills/education/edu-international-ib/SKILL.md +61 -61
- package/skills/education/edu-japan/LICENSE +21 -21
- package/skills/education/edu-japan/SKILL.md +48 -48
- package/skills/education/edu-korea/LICENSE +21 -21
- package/skills/education/edu-korea/SKILL.md +48 -48
- package/skills/education/edu-learning-science/LICENSE +21 -21
- package/skills/education/edu-learning-science/SKILL.md +76 -76
- package/skills/education/edu-portuguese-brazil/LICENSE +21 -21
- package/skills/education/edu-portuguese-brazil/SKILL.md +51 -51
- package/skills/education/edu-russia/LICENSE +21 -21
- package/skills/education/edu-russia/SKILL.md +50 -50
- package/skills/education/edu-spain-latam/LICENSE +21 -21
- package/skills/education/edu-spain-latam/SKILL.md +55 -55
- package/skills/education/edu-special/LICENSE +21 -21
- package/skills/education/edu-special/SKILL.md +76 -76
- package/skills/education/edu-thailand/LICENSE +21 -21
- package/skills/education/edu-thailand/SKILL.md +55 -55
- package/skills/education/edu-turkey/LICENSE +21 -21
- package/skills/education/edu-turkey/SKILL.md +58 -58
- package/skills/education/edu-uk-gcse-alevel/LICENSE +21 -21
- package/skills/education/edu-uk-gcse-alevel/SKILL.md +51 -51
- package/skills/education/edu-usa-graduate/LICENSE +21 -21
- package/skills/education/edu-usa-graduate/SKILL.md +57 -57
- package/skills/education/edu-usa-sat-act/LICENSE +21 -21
- package/skills/education/edu-usa-sat-act/SKILL.md +55 -55
- package/skills/education/edu-vietnam/LICENSE +21 -21
- package/skills/education/edu-vietnam/SKILL.md +53 -53
- package/skills/eightctl/SKILL.md +54 -54
- package/skills/engineering/eng-aerospace/LICENSE +21 -21
- package/skills/engineering/eng-aerospace/SKILL.md +117 -117
- package/skills/engineering/eng-chemical/LICENSE +21 -21
- package/skills/engineering/eng-chemical/SKILL.md +63 -63
- package/skills/engineering/eng-civil/LICENSE +21 -21
- package/skills/engineering/eng-civil/SKILL.md +223 -223
- package/skills/engineering/eng-control-systems/LICENSE +21 -21
- package/skills/engineering/eng-control-systems/SKILL.md +158 -158
- package/skills/engineering/eng-cryogenics/LICENSE +21 -21
- package/skills/engineering/eng-cryogenics/SKILL.md +151 -151
- package/skills/engineering/eng-electrical/LICENSE +21 -21
- package/skills/engineering/eng-electrical/SKILL.md +70 -70
- package/skills/engineering/eng-electronics-embedded/LICENSE +21 -21
- package/skills/engineering/eng-electronics-embedded/SKILL.md +89 -89
- package/skills/engineering/eng-environmental/LICENSE +21 -21
- package/skills/engineering/eng-environmental/SKILL.md +66 -66
- package/skills/engineering/eng-manufacturing/LICENSE +21 -21
- package/skills/engineering/eng-manufacturing/SKILL.md +78 -78
- package/skills/engineering/eng-mechanical/LICENSE +21 -21
- package/skills/engineering/eng-mechanical/SKILL.md +66 -66
- package/skills/engineering/eng-project/LICENSE +21 -21
- package/skills/engineering/eng-project/SKILL.md +72 -72
- package/skills/engineering/eng-propulsion/LICENSE +21 -21
- package/skills/engineering/eng-propulsion/SKILL.md +133 -133
- package/skills/engineering/eng-robotics/LICENSE +21 -21
- package/skills/engineering/eng-robotics/SKILL.md +92 -92
- package/skills/engineering/eng-systems/LICENSE +21 -21
- package/skills/engineering/eng-systems/SKILL.md +81 -81
- package/skills/environment/env-biodiversity/LICENSE +21 -21
- package/skills/environment/env-biodiversity/SKILL.md +66 -66
- package/skills/environment/env-circular-economy/LICENSE +21 -21
- package/skills/environment/env-circular-economy/SKILL.md +71 -71
- package/skills/environment/env-climate-action/LICENSE +21 -21
- package/skills/environment/env-climate-action/SKILL.md +55 -55
- package/skills/environment/env-energy/LICENSE +21 -21
- package/skills/environment/env-energy/SKILL.md +83 -83
- package/skills/environment/env-sustainability-biz/LICENSE +21 -21
- package/skills/environment/env-sustainability-biz/SKILL.md +65 -65
- package/skills/environment/env-water/LICENSE +21 -21
- package/skills/environment/env-water/SKILL.md +67 -67
- package/skills/finance/finance-accounting/LICENSE +21 -21
- package/skills/finance/finance-accounting/SKILL.md +239 -239
- package/skills/finance/finance-banking/LICENSE +21 -21
- package/skills/finance/finance-banking/SKILL.md +54 -54
- package/skills/finance/finance-corporate/LICENSE +21 -21
- package/skills/finance/finance-corporate/SKILL.md +105 -105
- package/skills/finance/finance-crypto/LICENSE +21 -21
- package/skills/finance/finance-crypto/SKILL.md +94 -94
- package/skills/finance/finance-debt-management/LICENSE +21 -21
- package/skills/finance/finance-debt-management/SKILL.md +87 -87
- package/skills/finance/finance-insurance/LICENSE +21 -21
- package/skills/finance/finance-insurance/SKILL.md +91 -91
- package/skills/finance/finance-investing/LICENSE +21 -21
- package/skills/finance/finance-investing/SKILL.md +269 -269
- package/skills/finance/finance-options-derivatives/LICENSE +21 -21
- package/skills/finance/finance-options-derivatives/SKILL.md +68 -68
- package/skills/finance/finance-personal/LICENSE +21 -21
- package/skills/finance/finance-personal/SKILL.md +268 -268
- package/skills/finance/finance-real-estate/LICENSE +21 -21
- package/skills/finance/finance-real-estate/SKILL.md +110 -110
- package/skills/finance/finance-startup/LICENSE +21 -21
- package/skills/finance/finance-startup/SKILL.md +253 -253
- package/skills/finance/finance-tax-planning/LICENSE +21 -21
- package/skills/finance/finance-tax-planning/SKILL.md +89 -89
- package/skills/finance/finance-trading/LICENSE +21 -21
- package/skills/finance/finance-trading/SKILL.md +112 -112
- package/skills/gemini/SKILL.md +51 -51
- package/skills/gh-issues/SKILL.md +216 -216
- package/skills/gifgrep/SKILL.md +89 -89
- package/skills/github/SKILL.md +87 -87
- package/skills/gog/SKILL.md +120 -120
- package/skills/goplaces/SKILL.md +56 -56
- package/skills/health/health-aging/LICENSE +21 -21
- package/skills/health/health-aging/SKILL.md +82 -82
- package/skills/health/health-chronic/LICENSE +21 -21
- package/skills/health/health-chronic/SKILL.md +202 -202
- package/skills/health/health-dental/LICENSE +21 -21
- package/skills/health/health-dental/SKILL.md +41 -41
- package/skills/health/health-eye-care/LICENSE +21 -21
- package/skills/health/health-eye-care/SKILL.md +56 -56
- package/skills/health/health-first-aid/LICENSE +21 -21
- package/skills/health/health-first-aid/SKILL.md +201 -201
- package/skills/health/health-fitness/LICENSE +21 -21
- package/skills/health/health-fitness/SKILL.md +111 -111
- package/skills/health/health-general/LICENSE +21 -21
- package/skills/health/health-general/SKILL.md +277 -277
- package/skills/health/health-mens/LICENSE +21 -21
- package/skills/health/health-mens/SKILL.md +53 -53
- package/skills/health/health-mental/LICENSE +21 -21
- package/skills/health/health-mental/SKILL.md +221 -221
- package/skills/health/health-naturopathy-ayurveda/LICENSE +21 -21
- package/skills/health/health-naturopathy-ayurveda/SKILL.md +60 -60
- package/skills/health/health-nutrition/LICENSE +21 -21
- package/skills/health/health-nutrition/SKILL.md +262 -262
- package/skills/health/health-pediatric/LICENSE +21 -21
- package/skills/health/health-pediatric/SKILL.md +94 -94
- package/skills/health/health-pharmacology/LICENSE +21 -21
- package/skills/health/health-pharmacology/SKILL.md +87 -87
- package/skills/health/health-pregnancy/LICENSE +21 -21
- package/skills/health/health-pregnancy/SKILL.md +71 -71
- package/skills/health/health-skin/LICENSE +21 -21
- package/skills/health/health-skin/SKILL.md +71 -71
- package/skills/health/health-sleep/LICENSE +21 -21
- package/skills/health/health-sleep/SKILL.md +81 -81
- package/skills/health/health-womens/LICENSE +21 -21
- package/skills/health/health-womens/SKILL.md +72 -72
- package/skills/health/health-yoga-wellness/LICENSE +21 -21
- package/skills/health/health-yoga-wellness/SKILL.md +58 -58
- package/skills/healthcare-systems/health-sys-global/LICENSE +21 -21
- package/skills/healthcare-systems/health-sys-global/SKILL.md +69 -69
- package/skills/healthcare-systems/health-sys-management/LICENSE +21 -21
- package/skills/healthcare-systems/health-sys-management/SKILL.md +71 -71
- package/skills/healthcare-systems/health-sys-navigation/LICENSE +21 -21
- package/skills/healthcare-systems/health-sys-navigation/SKILL.md +60 -60
- package/skills/healthcare-systems/health-sys-public/LICENSE +21 -21
- package/skills/healthcare-systems/health-sys-public/SKILL.md +71 -71
- package/skills/healthcheck/SKILL.md +109 -109
- package/skills/himalaya/SKILL.md +84 -84
- package/skills/himalaya/references/configuration.md +184 -184
- package/skills/himalaya/references/message-composition.md +199 -199
- package/skills/humanities/humanities-history-world/LICENSE +21 -21
- package/skills/humanities/humanities-history-world/SKILL.md +59 -59
- package/skills/humanities/humanities-indian-classical/LICENSE +21 -21
- package/skills/humanities/humanities-indian-classical/SKILL.md +104 -104
- package/skills/humanities/humanities-philosophy/LICENSE +21 -21
- package/skills/humanities/humanities-philosophy/SKILL.md +105 -105
- package/skills/humanities/humanities-world-religions/LICENSE +21 -21
- package/skills/humanities/humanities-world-religions/SKILL.md +67 -67
- package/skills/impeccable/SKILL.md +185 -185
- package/skills/imsg/SKILL.md +126 -126
- package/skills/industry/industry-construction/LICENSE +21 -21
- package/skills/industry/industry-construction/SKILL.md +81 -81
- package/skills/industry/industry-education-sector/LICENSE +21 -21
- package/skills/industry/industry-education-sector/SKILL.md +49 -49
- package/skills/industry/industry-fashion/LICENSE +21 -21
- package/skills/industry/industry-fashion/SKILL.md +82 -82
- package/skills/industry/industry-food/LICENSE +21 -21
- package/skills/industry/industry-food/SKILL.md +79 -79
- package/skills/industry/industry-government/LICENSE +21 -21
- package/skills/industry/industry-government/SKILL.md +80 -80
- package/skills/industry/industry-hospitality/LICENSE +21 -21
- package/skills/industry/industry-hospitality/SKILL.md +73 -73
- package/skills/industry/industry-insurance-sector/LICENSE +21 -21
- package/skills/industry/industry-insurance-sector/SKILL.md +57 -57
- package/skills/industry/industry-logistics/LICENSE +21 -21
- package/skills/industry/industry-logistics/SKILL.md +80 -80
- package/skills/industry/industry-media/LICENSE +21 -21
- package/skills/industry/industry-media/SKILL.md +66 -66
- package/skills/industry/industry-nonprofit/LICENSE +21 -21
- package/skills/industry/industry-nonprofit/SKILL.md +77 -77
- package/skills/industry/industry-pharma/LICENSE +21 -21
- package/skills/industry/industry-pharma/SKILL.md +69 -69
- package/skills/industry/industry-real-estate/LICENSE +21 -21
- package/skills/industry/industry-real-estate/SKILL.md +61 -61
- package/skills/industry/industry-sports/LICENSE +21 -21
- package/skills/industry/industry-sports/SKILL.md +71 -71
- package/skills/industry/industry-tech-startup/LICENSE +21 -21
- package/skills/industry/industry-tech-startup/SKILL.md +82 -82
- package/skills/internal-comms/LICENSE +21 -21
- package/skills/internal-comms/SKILL.md +38 -38
- package/skills/internal-comms/examples/3p-updates.md +49 -49
- package/skills/internal-comms/examples/company-newsletter.md +76 -76
- package/skills/internal-comms/examples/faq-answers.md +35 -35
- package/skills/internal-comms/examples/general-comms.md +19 -19
- package/skills/legal/legal-business/LICENSE +21 -21
- package/skills/legal/legal-business/SKILL.md +227 -227
- package/skills/legal/legal-consumer/LICENSE +21 -21
- package/skills/legal/legal-consumer/SKILL.md +155 -155
- package/skills/legal/legal-contracts/LICENSE +21 -21
- package/skills/legal/legal-contracts/SKILL.md +268 -268
- package/skills/legal/legal-corporate-governance/LICENSE +21 -21
- package/skills/legal/legal-corporate-governance/SKILL.md +53 -53
- package/skills/legal/legal-employment/LICENSE +21 -21
- package/skills/legal/legal-employment/SKILL.md +291 -291
- package/skills/legal/legal-immigration/LICENSE +21 -21
- package/skills/legal/legal-immigration/SKILL.md +146 -146
- package/skills/legal/legal-international/LICENSE +21 -21
- package/skills/legal/legal-international/SKILL.md +51 -51
- package/skills/legal/legal-ip/LICENSE +21 -21
- package/skills/legal/legal-ip/SKILL.md +264 -264
- package/skills/legal/legal-privacy/LICENSE +21 -21
- package/skills/legal/legal-privacy/SKILL.md +161 -161
- package/skills/legal/legal-real-estate/LICENSE +21 -21
- package/skills/legal/legal-real-estate/SKILL.md +142 -142
- package/skills/legal/legal-startup/LICENSE +21 -21
- package/skills/legal/legal-startup/SKILL.md +182 -182
- package/skills/legal/legal-tax/LICENSE +21 -21
- package/skills/legal/legal-tax/SKILL.md +156 -156
- package/skills/mcp-builder/LICENSE +21 -21
- package/skills/mcp-builder/SKILL.md +257 -257
- package/skills/mcp-builder/reference/evaluation.md +630 -630
- package/skills/mcp-builder/reference/mcp_best_practices.md +269 -269
- package/skills/mcp-builder/reference/node_mcp_server.md +980 -980
- package/skills/mcp-builder/reference/python_mcp_server.md +737 -737
- package/skills/mcp-builder/scripts/connections.py +151 -151
- package/skills/mcp-builder/scripts/evaluation.py +373 -373
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -22
- package/skills/mcp-builder/scripts/requirements.txt +2 -2
- package/skills/mcporter/SKILL.md +65 -65
- package/skills/meme-maker/SKILL.md +46 -46
- package/skills/meme-maker/references/templates.json +358 -358
- package/skills/meme-maker/scripts/meme.mjs +398 -398
- package/skills/mental-health/mental-health-cbt/LICENSE +21 -21
- package/skills/mental-health/mental-health-cbt/SKILL.md +254 -254
- package/skills/mental-health/psych-addiction/LICENSE +21 -21
- package/skills/mental-health/psych-addiction/SKILL.md +79 -79
- package/skills/mental-health/psych-behavioral-econ/LICENSE +21 -21
- package/skills/mental-health/psych-behavioral-econ/SKILL.md +84 -84
- package/skills/mental-health/psych-child/LICENSE +21 -21
- package/skills/mental-health/psych-child/SKILL.md +84 -84
- package/skills/mental-health/psych-grief/LICENSE +21 -21
- package/skills/mental-health/psych-grief/SKILL.md +85 -85
- package/skills/mental-health/psych-mindfulness/LICENSE +21 -21
- package/skills/mental-health/psych-mindfulness/SKILL.md +71 -71
- package/skills/mental-health/psych-org/LICENSE +21 -21
- package/skills/mental-health/psych-org/SKILL.md +115 -115
- package/skills/mental-health/psych-positive/LICENSE +21 -21
- package/skills/mental-health/psych-positive/SKILL.md +86 -86
- package/skills/mental-health/psych-relationships/LICENSE +21 -21
- package/skills/mental-health/psych-relationships/SKILL.md +100 -100
- package/skills/mental-health/psych-trauma/LICENSE +21 -21
- package/skills/mental-health/psych-trauma/SKILL.md +109 -109
- package/skills/model-usage/SKILL.md +75 -75
- package/skills/model-usage/references/codexbar-cli.md +33 -33
- package/skills/model-usage/scripts/model_usage.py +319 -319
- package/skills/model-usage/scripts/test_model_usage.py +40 -40
- package/skills/nano-pdf/SKILL.md +42 -42
- package/skills/node-connect/SKILL.md +147 -147
- package/skills/node-inspect-debugger/SKILL.md +88 -88
- package/skills/notion/SKILL.md +154 -154
- package/skills/obsidian/SKILL.md +123 -123
- package/skills/openai-whisper/SKILL.md +42 -42
- package/skills/openai-whisper-api/SKILL.md +75 -75
- package/skills/openai-whisper-api/scripts/transcribe.sh +154 -154
- package/skills/openhue/SKILL.md +116 -116
- package/skills/oracle/SKILL.md +130 -130
- package/skills/ordercli/SKILL.md +82 -82
- package/skills/peekaboo/SKILL.md +217 -217
- package/skills/pyproject.toml +10 -10
- package/skills/python-debugpy/SKILL.md +76 -76
- package/skills/sag/SKILL.md +91 -91
- package/skills/science/sci-astronomy/LICENSE +21 -21
- package/skills/science/sci-astronomy/SKILL.md +80 -80
- package/skills/science/sci-biology/LICENSE +21 -21
- package/skills/science/sci-biology/SKILL.md +74 -74
- package/skills/science/sci-chemistry/LICENSE +21 -21
- package/skills/science/sci-chemistry/SKILL.md +89 -89
- package/skills/science/sci-climate/LICENSE +21 -21
- package/skills/science/sci-climate/SKILL.md +72 -72
- package/skills/science/sci-data-analysis/LICENSE +21 -21
- package/skills/science/sci-data-analysis/SKILL.md +87 -87
- package/skills/science/sci-environmental-science/LICENSE +21 -21
- package/skills/science/sci-environmental-science/SKILL.md +69 -69
- package/skills/science/sci-geology/LICENSE +21 -21
- package/skills/science/sci-geology/SKILL.md +56 -56
- package/skills/science/sci-method/LICENSE +21 -21
- package/skills/science/sci-method/SKILL.md +77 -77
- package/skills/science/sci-neuroscience/LICENSE +21 -21
- package/skills/science/sci-neuroscience/SKILL.md +79 -79
- package/skills/science/sci-physics/LICENSE +21 -21
- package/skills/science/sci-physics/SKILL.md +78 -78
- package/skills/science/sci-research-methods/LICENSE +21 -21
- package/skills/science/sci-research-methods/SKILL.md +83 -83
- package/skills/science/sci-statistics/LICENSE +21 -21
- package/skills/science/sci-statistics/SKILL.md +249 -249
- package/skills/session-logs/SKILL.md +155 -155
- package/skills/sherpa-onnx-tts/SKILL.md +113 -113
- package/skills/skill-creator/SKILL.md +81 -81
- package/skills/skill-creator/license.txt +202 -202
- package/skills/skill-creator/scripts/init_skill.py +378 -378
- package/skills/skill-creator/scripts/package_skill.py +144 -144
- package/skills/skill-creator/scripts/quick_validate.py +169 -169
- package/skills/skill-creator/scripts/test_init_skill.py +51 -51
- package/skills/skill-creator/scripts/test_package_skill.py +199 -199
- package/skills/skill-creator/scripts/test_quick_validate.py +116 -116
- package/skills/slack/SKILL.md +82 -82
- package/skills/slack-gif-creator/LICENSE +21 -21
- package/skills/slack-gif-creator/SKILL.md +293 -293
- package/skills/slack-gif-creator/requirements.txt +3 -3
- package/skills/social-sciences/social-anthropology/LICENSE +21 -21
- package/skills/social-sciences/social-anthropology/SKILL.md +62 -62
- package/skills/social-sciences/social-economics/LICENSE +21 -21
- package/skills/social-sciences/social-economics/SKILL.md +88 -88
- package/skills/social-sciences/social-geography/LICENSE +21 -21
- package/skills/social-sciences/social-geography/SKILL.md +61 -61
- package/skills/social-sciences/social-international-dev/LICENSE +21 -21
- package/skills/social-sciences/social-international-dev/SKILL.md +76 -76
- package/skills/social-sciences/social-political-science/LICENSE +21 -21
- package/skills/social-sciences/social-political-science/SKILL.md +70 -70
- package/skills/social-sciences/social-public-policy/LICENSE +21 -21
- package/skills/social-sciences/social-public-policy/SKILL.md +73 -73
- package/skills/social-sciences/social-sociology/LICENSE +21 -21
- package/skills/social-sciences/social-sociology/SKILL.md +78 -78
- package/skills/songsee/SKILL.md +53 -53
- package/skills/sonoscli/SKILL.md +69 -69
- package/skills/spike/SKILL.md +55 -55
- package/skills/spotify-player/SKILL.md +68 -68
- package/skills/summarize/SKILL.md +90 -90
- package/skills/taskflow/SKILL.md +153 -153
- package/skills/taskflow/examples/inbox-triage.lobster +33 -33
- package/skills/taskflow/examples/pr-intake.lobster +32 -32
- package/skills/taskflow-inbox-triage/SKILL.md +123 -123
- package/skills/technical/ai-ethics/LICENSE +21 -21
- package/skills/technical/ai-ethics/SKILL.md +92 -92
- package/skills/technical/ai-product-builder/LICENSE +21 -21
- package/skills/technical/ai-product-builder/SKILL.md +180 -180
- package/skills/technical/analytics-setup/LICENSE +21 -21
- package/skills/technical/analytics-setup/SKILL.md +125 -125
- package/skills/technical/api-builder/LICENSE +21 -21
- package/skills/technical/api-builder/SKILL.md +202 -202
- package/skills/technical/architecture-decisions/LICENSE +21 -21
- package/skills/technical/architecture-decisions/SKILL.md +120 -120
- package/skills/technical/auth-security/LICENSE +21 -21
- package/skills/technical/auth-security/SKILL.md +209 -209
- package/skills/technical/blockchain-web3/LICENSE +21 -21
- package/skills/technical/blockchain-web3/SKILL.md +84 -84
- package/skills/technical/cloud-architecture/LICENSE +21 -21
- package/skills/technical/cloud-architecture/SKILL.md +85 -85
- package/skills/technical/content-platform/LICENSE +21 -21
- package/skills/technical/content-platform/SKILL.md +134 -134
- package/skills/technical/cybersecurity-advanced/LICENSE +21 -21
- package/skills/technical/cybersecurity-advanced/SKILL.md +99 -99
- package/skills/technical/data-engineering/LICENSE +21 -21
- package/skills/technical/data-engineering/SKILL.md +117 -117
- package/skills/technical/database-design/LICENSE +21 -21
- package/skills/technical/database-design/SKILL.md +185 -185
- package/skills/technical/devops-cicd/LICENSE +21 -21
- package/skills/technical/devops-cicd/SKILL.md +181 -181
- package/skills/technical/ecommerce-builder/LICENSE +21 -21
- package/skills/technical/ecommerce-builder/SKILL.md +123 -123
- package/skills/technical/email-marketing/LICENSE +21 -21
- package/skills/technical/email-marketing/SKILL.md +128 -128
- package/skills/technical/fintech-builder/LICENSE +21 -21
- package/skills/technical/fintech-builder/SKILL.md +141 -141
- package/skills/technical/full-stack-web/LICENSE +21 -21
- package/skills/technical/full-stack-web/SKILL.md +173 -173
- package/skills/technical/gdpr-basics/LICENSE +21 -21
- package/skills/technical/gdpr-basics/SKILL.md +145 -145
- package/skills/technical/launch-playbook/LICENSE +21 -21
- package/skills/technical/launch-playbook/SKILL.md +95 -95
- package/skills/technical/marketing-copy/LICENSE +21 -21
- package/skills/technical/marketing-copy/SKILL.md +126 -126
- package/skills/technical/marketplace-builder/LICENSE +21 -21
- package/skills/technical/marketplace-builder/SKILL.md +105 -105
- package/skills/technical/mobile-pwa/LICENSE +21 -21
- package/skills/technical/mobile-pwa/SKILL.md +191 -191
- package/skills/technical/no-code-tools/LICENSE +21 -21
- package/skills/technical/no-code-tools/SKILL.md +80 -80
- package/skills/technical/open-source/LICENSE +21 -21
- package/skills/technical/open-source/SKILL.md +71 -71
- package/skills/technical/performance-optimization/LICENSE +21 -21
- package/skills/technical/performance-optimization/SKILL.md +155 -155
- package/skills/technical/pricing-design/LICENSE +21 -21
- package/skills/technical/pricing-design/SKILL.md +87 -87
- package/skills/technical/product-management/LICENSE +21 -21
- package/skills/technical/product-management/SKILL.md +94 -94
- package/skills/technical/saas-builder/LICENSE +21 -21
- package/skills/technical/saas-builder/SKILL.md +138 -138
- package/skills/technical/scope-estimation/LICENSE +21 -21
- package/skills/technical/scope-estimation/SKILL.md +99 -99
- package/skills/technical/secrets-management/LICENSE +21 -21
- package/skills/technical/secrets-management/SKILL.md +135 -135
- package/skills/technical/seo-technical/LICENSE +21 -21
- package/skills/technical/seo-technical/SKILL.md +136 -136
- package/skills/technical/technical-writing/LICENSE +21 -21
- package/skills/technical/technical-writing/SKILL.md +149 -149
- package/skills/technical/ux-research-tools/LICENSE +21 -21
- package/skills/technical/ux-research-tools/SKILL.md +54 -54
- package/skills/theme-factory/LICENSE +21 -21
- package/skills/theme-factory/SKILL.md +65 -65
- package/skills/theme-factory/themes/arctic-frost.md +19 -19
- package/skills/theme-factory/themes/botanical-garden.md +19 -19
- package/skills/theme-factory/themes/desert-rose.md +19 -19
- package/skills/theme-factory/themes/forest-canopy.md +19 -19
- package/skills/theme-factory/themes/golden-hour.md +19 -19
- package/skills/theme-factory/themes/midnight-galaxy.md +19 -19
- package/skills/theme-factory/themes/modern-minimalist.md +19 -19
- package/skills/theme-factory/themes/ocean-depths.md +19 -19
- package/skills/theme-factory/themes/sunset-boulevard.md +19 -19
- package/skills/theme-factory/themes/tech-innovation.md +19 -19
- package/skills/things-mac/SKILL.md +90 -90
- package/skills/tmux/SKILL.md +95 -95
- package/skills/tmux/scripts/find-sessions.sh +112 -112
- package/skills/tmux/scripts/wait-for-text.sh +83 -83
- package/skills/trades/trades-agriculture/LICENSE +21 -21
- package/skills/trades/trades-agriculture/SKILL.md +80 -80
- package/skills/trades/trades-automotive/LICENSE +21 -21
- package/skills/trades/trades-automotive/SKILL.md +84 -84
- package/skills/trades/trades-carpentry/LICENSE +21 -21
- package/skills/trades/trades-carpentry/SKILL.md +71 -71
- package/skills/trades/trades-cooking-pro/LICENSE +21 -21
- package/skills/trades/trades-cooking-pro/SKILL.md +90 -90
- package/skills/trades/trades-electrical/LICENSE +21 -21
- package/skills/trades/trades-electrical/SKILL.md +146 -146
- package/skills/trades/trades-hvac/LICENSE +21 -21
- package/skills/trades/trades-hvac/SKILL.md +80 -80
- package/skills/trades/trades-landscaping/LICENSE +21 -21
- package/skills/trades/trades-landscaping/SKILL.md +60 -60
- package/skills/trades/trades-metalworking/LICENSE +21 -21
- package/skills/trades/trades-metalworking/SKILL.md +64 -64
- package/skills/trades/trades-painting/LICENSE +21 -21
- package/skills/trades/trades-painting/SKILL.md +70 -70
- package/skills/trades/trades-plumbing/LICENSE +21 -21
- package/skills/trades/trades-plumbing/SKILL.md +160 -160
- package/skills/trades/trades-welding/LICENSE +21 -21
- package/skills/trades/trades-welding/SKILL.md +82 -82
- package/skills/trello/SKILL.md +112 -112
- package/skills/uipm-ui-styling/SKILL.md +328 -328
- package/skills/video-frames/SKILL.md +50 -50
- package/skills/video-frames/scripts/frame.sh +81 -81
- package/skills/voice-call/SKILL.md +49 -49
- package/skills/wacli/SKILL.md +76 -76
- package/skills/weather/SKILL.md +91 -91
- package/skills/web-artifacts-builder/LICENSE +21 -21
- package/skills/web-artifacts-builder/SKILL.md +82 -82
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +53 -53
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +322 -322
- package/skills/xurl/SKILL.md +124 -124
- package/skills/graphify/SKILL.md +0 -619
- package/skills/graphify/__init__.py +0 -28
- package/skills/graphify/__main__.py +0 -4582
- package/skills/graphify/affected.py +0 -154
- package/skills/graphify/always_on/agents-md.md +0 -12
- package/skills/graphify/always_on/antigravity-rules.md +0 -14
- package/skills/graphify/always_on/claude-md.md +0 -9
- package/skills/graphify/always_on/gemini-md.md +0 -9
- package/skills/graphify/always_on/kiro-steering.md +0 -5
- package/skills/graphify/always_on/vscode-instructions.md +0 -17
- package/skills/graphify/analyze.py +0 -724
- package/skills/graphify/benchmark.py +0 -155
- package/skills/graphify/build.py +0 -487
- package/skills/graphify/cache.py +0 -417
- package/skills/graphify/callflow_html.py +0 -2020
- package/skills/graphify/cluster.py +0 -272
- package/skills/graphify/command-kilo.md +0 -15
- package/skills/graphify/dedup.py +0 -429
- package/skills/graphify/detect.py +0 -1379
- package/skills/graphify/diagnostics.py +0 -390
- package/skills/graphify/export.py +0 -1408
- package/skills/graphify/extract.py +0 -11570
- package/skills/graphify/global_graph.py +0 -159
- package/skills/graphify/google_workspace.py +0 -223
- package/skills/graphify/hooks.py +0 -457
- package/skills/graphify/ingest.py +0 -331
- package/skills/graphify/llm.py +0 -1896
- package/skills/graphify/manifest.py +0 -4
- package/skills/graphify/mcp_ingest.py +0 -392
- package/skills/graphify/multigraph_compat.py +0 -212
- package/skills/graphify/pg_introspect.py +0 -142
- package/skills/graphify/prs.py +0 -748
- package/skills/graphify/querylog.py +0 -70
- package/skills/graphify/report.py +0 -218
- package/skills/graphify/scip_ingest.py +0 -363
- package/skills/graphify/security.py +0 -336
- package/skills/graphify/semantic_cleanup.py +0 -319
- package/skills/graphify/serve.py +0 -1309
- package/skills/graphify/skill-aider.md +0 -1246
- package/skills/graphify/skill-amp.md +0 -613
- package/skills/graphify/skill-claw.md +0 -616
- package/skills/graphify/skill-codex.md +0 -613
- package/skills/graphify/skill-copilot.md +0 -616
- package/skills/graphify/skill-devin.md +0 -1372
- package/skills/graphify/skill-droid.md +0 -613
- package/skills/graphify/skill-kilo.md +0 -625
- package/skills/graphify/skill-kiro.md +0 -615
- package/skills/graphify/skill-opencode.md +0 -608
- package/skills/graphify/skill-pi.md +0 -615
- package/skills/graphify/skill-trae.md +0 -614
- package/skills/graphify/skill-vscode.md +0 -612
- package/skills/graphify/skill-windows.md +0 -651
- package/skills/graphify/skills/amp/references/add-watch.md +0 -56
- package/skills/graphify/skills/amp/references/exports.md +0 -71
- package/skills/graphify/skills/amp/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/amp/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/amp/references/hooks.md +0 -33
- package/skills/graphify/skills/amp/references/query.md +0 -249
- package/skills/graphify/skills/amp/references/transcribe.md +0 -48
- package/skills/graphify/skills/amp/references/update.md +0 -179
- package/skills/graphify/skills/claude/references/add-watch.md +0 -56
- package/skills/graphify/skills/claude/references/exports.md +0 -71
- package/skills/graphify/skills/claude/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/claude/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/claude/references/hooks.md +0 -33
- package/skills/graphify/skills/claude/references/query.md +0 -103
- package/skills/graphify/skills/claude/references/transcribe.md +0 -48
- package/skills/graphify/skills/claude/references/update.md +0 -179
- package/skills/graphify/skills/claw/references/add-watch.md +0 -56
- package/skills/graphify/skills/claw/references/exports.md +0 -71
- package/skills/graphify/skills/claw/references/extraction-spec.md +0 -29
- package/skills/graphify/skills/claw/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/claw/references/hooks.md +0 -33
- package/skills/graphify/skills/claw/references/query.md +0 -249
- package/skills/graphify/skills/claw/references/transcribe.md +0 -48
- package/skills/graphify/skills/claw/references/update.md +0 -179
- package/skills/graphify/skills/codex/references/add-watch.md +0 -56
- package/skills/graphify/skills/codex/references/exports.md +0 -71
- package/skills/graphify/skills/codex/references/extraction-spec.md +0 -29
- package/skills/graphify/skills/codex/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/codex/references/hooks.md +0 -33
- package/skills/graphify/skills/codex/references/query.md +0 -249
- package/skills/graphify/skills/codex/references/transcribe.md +0 -48
- package/skills/graphify/skills/codex/references/update.md +0 -179
- package/skills/graphify/skills/copilot/references/add-watch.md +0 -56
- package/skills/graphify/skills/copilot/references/exports.md +0 -71
- package/skills/graphify/skills/copilot/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/copilot/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/copilot/references/hooks.md +0 -33
- package/skills/graphify/skills/copilot/references/query.md +0 -249
- package/skills/graphify/skills/copilot/references/transcribe.md +0 -48
- package/skills/graphify/skills/copilot/references/update.md +0 -179
- package/skills/graphify/skills/droid/references/add-watch.md +0 -56
- package/skills/graphify/skills/droid/references/exports.md +0 -71
- package/skills/graphify/skills/droid/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/droid/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/droid/references/hooks.md +0 -33
- package/skills/graphify/skills/droid/references/query.md +0 -249
- package/skills/graphify/skills/droid/references/transcribe.md +0 -48
- package/skills/graphify/skills/droid/references/update.md +0 -179
- package/skills/graphify/skills/kilo/references/add-watch.md +0 -56
- package/skills/graphify/skills/kilo/references/exports.md +0 -71
- package/skills/graphify/skills/kilo/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/kilo/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/kilo/references/hooks.md +0 -33
- package/skills/graphify/skills/kilo/references/query.md +0 -249
- package/skills/graphify/skills/kilo/references/transcribe.md +0 -48
- package/skills/graphify/skills/kilo/references/update.md +0 -179
- package/skills/graphify/skills/kiro/references/add-watch.md +0 -56
- package/skills/graphify/skills/kiro/references/exports.md +0 -71
- package/skills/graphify/skills/kiro/references/extraction-spec.md +0 -29
- package/skills/graphify/skills/kiro/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/kiro/references/hooks.md +0 -33
- package/skills/graphify/skills/kiro/references/query.md +0 -249
- package/skills/graphify/skills/kiro/references/transcribe.md +0 -48
- package/skills/graphify/skills/kiro/references/update.md +0 -179
- package/skills/graphify/skills/opencode/references/add-watch.md +0 -56
- package/skills/graphify/skills/opencode/references/exports.md +0 -71
- package/skills/graphify/skills/opencode/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/opencode/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/opencode/references/hooks.md +0 -33
- package/skills/graphify/skills/opencode/references/query.md +0 -249
- package/skills/graphify/skills/opencode/references/transcribe.md +0 -48
- package/skills/graphify/skills/opencode/references/update.md +0 -179
- package/skills/graphify/skills/pi/references/add-watch.md +0 -56
- package/skills/graphify/skills/pi/references/exports.md +0 -71
- package/skills/graphify/skills/pi/references/extraction-spec.md +0 -29
- package/skills/graphify/skills/pi/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/pi/references/hooks.md +0 -33
- package/skills/graphify/skills/pi/references/query.md +0 -249
- package/skills/graphify/skills/pi/references/transcribe.md +0 -48
- package/skills/graphify/skills/pi/references/update.md +0 -179
- package/skills/graphify/skills/trae/references/add-watch.md +0 -56
- package/skills/graphify/skills/trae/references/exports.md +0 -71
- package/skills/graphify/skills/trae/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/trae/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/trae/references/hooks.md +0 -35
- package/skills/graphify/skills/trae/references/query.md +0 -249
- package/skills/graphify/skills/trae/references/transcribe.md +0 -48
- package/skills/graphify/skills/trae/references/update.md +0 -179
- package/skills/graphify/skills/vscode/references/add-watch.md +0 -56
- package/skills/graphify/skills/vscode/references/exports.md +0 -71
- package/skills/graphify/skills/vscode/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/vscode/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/vscode/references/hooks.md +0 -33
- package/skills/graphify/skills/vscode/references/query.md +0 -249
- package/skills/graphify/skills/vscode/references/transcribe.md +0 -48
- package/skills/graphify/skills/vscode/references/update.md +0 -179
- package/skills/graphify/skills/windows/references/add-watch.md +0 -56
- package/skills/graphify/skills/windows/references/exports.md +0 -71
- package/skills/graphify/skills/windows/references/extraction-spec.md +0 -68
- package/skills/graphify/skills/windows/references/github-and-merge.md +0 -46
- package/skills/graphify/skills/windows/references/hooks.md +0 -33
- package/skills/graphify/skills/windows/references/query.md +0 -249
- package/skills/graphify/skills/windows/references/transcribe.md +0 -48
- package/skills/graphify/skills/windows/references/update.md +0 -179
- package/skills/graphify/symbol_resolution.py +0 -538
- package/skills/graphify/transcribe.py +0 -184
- package/skills/graphify/tree_html.py +0 -582
- package/skills/graphify/validate.py +0 -72
- package/skills/graphify/watch.py +0 -898
- package/skills/graphify/wiki.py +0 -282
|
@@ -1,2020 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
callflow_html.py — Generate call-flow architecture HTML from graphify knowledge graph outputs.
|
|
4
|
-
|
|
5
|
-
Reads graph.json plus optional GRAPH_REPORT.md, .graphify_labels.json, and sections JSON,
|
|
6
|
-
then produces a self-contained HTML file with:
|
|
7
|
-
- Dark-themed CSS (fixed template)
|
|
8
|
-
- Navigation bar from section list
|
|
9
|
-
- Architecture overview flowchart LR (aggregated section-level edges)
|
|
10
|
-
- Per-section flowchart LR (auto-generated representative intra-section edges)
|
|
11
|
-
- Call detail table scaffolding (headers + representative node rows)
|
|
12
|
-
- Auto-generated section intros and key-file cards
|
|
13
|
-
|
|
14
|
-
Usage:
|
|
15
|
-
python3 -m graphify export callflow-html
|
|
16
|
-
python3 -m graphify export callflow-html /path/to/project/graphify-out/graph.json
|
|
17
|
-
python3 -m graphify export callflow-html --graph /path/to/graph.json --output docs/architecture.html
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
from __future__ import annotations
|
|
21
|
-
|
|
22
|
-
import json
|
|
23
|
-
import argparse
|
|
24
|
-
import os
|
|
25
|
-
import re
|
|
26
|
-
import sys
|
|
27
|
-
import hashlib
|
|
28
|
-
from pathlib import Path
|
|
29
|
-
from collections import Counter, defaultdict
|
|
30
|
-
from datetime import datetime, timezone
|
|
31
|
-
from html import escape
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# ──────────────────────────────────────────────
|
|
35
|
-
# 1. CSS template (fixed, project-agnostic)
|
|
36
|
-
# ──────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
CSS = """:root {
|
|
39
|
-
--bg: #0f172a; --surface: #1e293b; --border: #334155;
|
|
40
|
-
--text: #e2e8f0; --muted: #94a3b8; --accent: #38bdf8;
|
|
41
|
-
--warn: #fbbf24; --err: #f87171; --ok: #34d399;
|
|
42
|
-
}
|
|
43
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
44
|
-
body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; background: var(--bg); color: var(--text); line-height: 1.7; }
|
|
45
|
-
.container { max-width: 1200px; margin: 0 auto; padding: 40px 24px; }
|
|
46
|
-
h1 { font-size: 2.4rem; margin-bottom: 8px; background: linear-gradient(135deg, var(--accent), #a78bfa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
|
47
|
-
h2 { font-size: 1.7rem; margin: 48px 0 16px; padding-bottom: 8px; border-bottom: 2px solid var(--accent); }
|
|
48
|
-
h3 { font-size: 1.25rem; margin: 32px 0 12px; color: var(--accent); }
|
|
49
|
-
h4 { font-size: 1.05rem; margin: 20px 0 8px; color: var(--warn); }
|
|
50
|
-
p { margin: 8px 0; color: var(--muted); }
|
|
51
|
-
.subtitle { color: var(--muted); font-size: 1.1rem; margin-bottom: 32px; }
|
|
52
|
-
.mermaid { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 24px; margin: 20px 0; overflow-x: auto; position: relative; }
|
|
53
|
-
.mermaid.is-enhanced { padding: 0; overflow: hidden; min-height: 260px; }
|
|
54
|
-
.mermaid-viewport { padding: 54px 24px 24px; overflow: hidden; cursor: grab; touch-action: none; min-height: 260px; }
|
|
55
|
-
.mermaid-viewport.is-dragging { cursor: grabbing; }
|
|
56
|
-
.mermaid-viewport svg { max-width: none !important; height: auto; transform-origin: 0 0; transition: transform 120ms ease; }
|
|
57
|
-
.mermaid-toolbar { position: absolute; top: 10px; right: 10px; z-index: 3; display: flex; align-items: center; gap: 6px; padding: 6px; background: rgba(15,23,42,0.92); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.28); }
|
|
58
|
-
.mermaid-toolbar button, .mermaid-toolbar .zoom-level { height: 28px; min-width: 32px; border: 1px solid var(--border); border-radius: 6px; background: #1e293b; color: var(--text); font: 600 0.78rem system-ui, sans-serif; display: inline-flex; align-items: center; justify-content: center; }
|
|
59
|
-
.mermaid-toolbar button { cursor: pointer; }
|
|
60
|
-
.mermaid-toolbar button:hover { border-color: var(--accent); color: var(--accent); }
|
|
61
|
-
.mermaid-toolbar .zoom-level { min-width: 52px; color: var(--muted); background: transparent; }
|
|
62
|
-
.call-table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 0.92rem; }
|
|
63
|
-
.call-table th { background: #1a2744; color: var(--accent); text-align: left; padding: 10px 14px; border: 1px solid var(--border); }
|
|
64
|
-
.call-table td { padding: 8px 14px; border: 1px solid var(--border); vertical-align: top; }
|
|
65
|
-
.call-table tr:nth-child(even) { background: rgba(255,255,255,0.02); }
|
|
66
|
-
.tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.8rem; font-weight: 600; }
|
|
67
|
-
.tag-async { background: #7c3aed33; color: #a78bfa; }
|
|
68
|
-
.tag-class { background: #05966933; color: var(--ok); }
|
|
69
|
-
.tag-func { background: #2563eb33; color: var(--accent); }
|
|
70
|
-
.tag-cmd { background: #d9770633; color: var(--warn); }
|
|
71
|
-
.tag-endpoint { background: #dc262633; color: var(--err); }
|
|
72
|
-
.tag-hook { background: #db277733; color: #f472b6; }
|
|
73
|
-
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 20px; margin: 16px 0; }
|
|
74
|
-
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 16px; margin: 16px 0; }
|
|
75
|
-
.arrow-chain { font-family: 'Fira Code', monospace; font-size: 0.85rem; color: var(--accent); padding: 10px; background: rgba(56,189,248,0.06); border-radius: 6px; }
|
|
76
|
-
code { font-family: 'Fira Code', 'Cascadia Code', monospace; background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 3px; font-size: 0.88em; }
|
|
77
|
-
ul, ol { margin: 8px 0 8px 24px; color: var(--muted); }
|
|
78
|
-
li { margin: 4px 0; }
|
|
79
|
-
a { color: var(--accent); }
|
|
80
|
-
hr { border: none; border-top: 1px solid var(--border); margin: 40px 0; }
|
|
81
|
-
.nav { position: sticky; top: 0; background: var(--bg); z-index: 10; padding: 12px 0; border-bottom: 1px solid var(--border); display: flex; gap: 20px; flex-wrap: wrap; font-size: 0.9rem; }
|
|
82
|
-
.nav a { text-decoration: none; }
|
|
83
|
-
.nav a:hover { text-decoration: underline; }
|
|
84
|
-
@media (max-width: 768px) { .container { padding: 16px; } h1 { font-size: 1.8rem; } }
|
|
85
|
-
"""
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# ──────────────────────────────────────────────
|
|
89
|
-
# 2. Data loading and normalization helpers
|
|
90
|
-
# ──────────────────────────────────────────────
|
|
91
|
-
|
|
92
|
-
def read_json(path: str | Path, default=None):
|
|
93
|
-
"""Read JSON with a useful error message."""
|
|
94
|
-
if not path:
|
|
95
|
-
return default
|
|
96
|
-
path = Path(path)
|
|
97
|
-
if not path.exists():
|
|
98
|
-
return default
|
|
99
|
-
try:
|
|
100
|
-
return json.loads(path.read_text(encoding="utf-8"))
|
|
101
|
-
except json.JSONDecodeError as exc:
|
|
102
|
-
raise SystemExit(f"ERROR: invalid JSON in {path}: {exc}") from exc
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def first_present(mapping: dict, *keys, default=None):
|
|
106
|
-
"""Return the first non-empty value for any candidate key."""
|
|
107
|
-
for key in keys:
|
|
108
|
-
if key in mapping and mapping[key] not in (None, ""):
|
|
109
|
-
return mapping[key]
|
|
110
|
-
return default
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def first_list(*values) -> list:
|
|
114
|
-
"""Return the first list from a set of possible schema locations."""
|
|
115
|
-
for value in values:
|
|
116
|
-
if isinstance(value, list):
|
|
117
|
-
return value
|
|
118
|
-
return []
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def to_float(value, default: float = 0.0) -> float:
|
|
122
|
-
"""Convert graph numeric fields that may be serialized as strings."""
|
|
123
|
-
try:
|
|
124
|
-
return float(value)
|
|
125
|
-
except (TypeError, ValueError):
|
|
126
|
-
return default
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def endpoint_id(value) -> str:
|
|
130
|
-
"""Normalize edge endpoints that may be strings or node-like objects."""
|
|
131
|
-
if isinstance(value, dict):
|
|
132
|
-
value = first_present(value, "id", "node_id", "key", "name", "qualified_name")
|
|
133
|
-
return str(value or "")
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def normalize_node(raw: dict, index: int) -> dict:
|
|
137
|
-
"""Normalize a graphify node across common graph.json schema variants."""
|
|
138
|
-
node = dict(raw)
|
|
139
|
-
node_id = first_present(
|
|
140
|
-
node,
|
|
141
|
-
"id",
|
|
142
|
-
"node_id",
|
|
143
|
-
"key",
|
|
144
|
-
"uid",
|
|
145
|
-
"name",
|
|
146
|
-
"qualified_name",
|
|
147
|
-
"fqname",
|
|
148
|
-
"symbol",
|
|
149
|
-
default=f"node_{index + 1}",
|
|
150
|
-
)
|
|
151
|
-
source_file = first_present(
|
|
152
|
-
node,
|
|
153
|
-
"source_file",
|
|
154
|
-
"file",
|
|
155
|
-
"file_path",
|
|
156
|
-
"filepath",
|
|
157
|
-
"path",
|
|
158
|
-
"module_path",
|
|
159
|
-
"defined_in",
|
|
160
|
-
default="",
|
|
161
|
-
)
|
|
162
|
-
label = first_present(
|
|
163
|
-
node,
|
|
164
|
-
"label",
|
|
165
|
-
"display_name",
|
|
166
|
-
"title",
|
|
167
|
-
"name",
|
|
168
|
-
"qualified_name",
|
|
169
|
-
"fqname",
|
|
170
|
-
"symbol",
|
|
171
|
-
default=node_id,
|
|
172
|
-
)
|
|
173
|
-
community = first_present(
|
|
174
|
-
node,
|
|
175
|
-
"community",
|
|
176
|
-
"community_id",
|
|
177
|
-
"cluster",
|
|
178
|
-
"cluster_id",
|
|
179
|
-
"group",
|
|
180
|
-
"group_id",
|
|
181
|
-
"modularity_class",
|
|
182
|
-
default="unknown",
|
|
183
|
-
)
|
|
184
|
-
node_type = first_present(node, "node_type", "kind", "type", "category", default="")
|
|
185
|
-
file_type = first_present(node, "file_type", "content_type", "artifact_type", default="")
|
|
186
|
-
if not file_type:
|
|
187
|
-
suffix = Path(str(source_file)).suffix.lower()
|
|
188
|
-
file_type = "document" if suffix in {".md", ".mdx", ".rst", ".txt"} else "code"
|
|
189
|
-
|
|
190
|
-
node["id"] = str(node_id)
|
|
191
|
-
node["label"] = str(label)
|
|
192
|
-
node["community"] = community
|
|
193
|
-
node["source_file"] = str(source_file or "")
|
|
194
|
-
node["node_type"] = str(node_type or "")
|
|
195
|
-
node["file_type"] = str(file_type or "code")
|
|
196
|
-
return node
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def normalize_edge(raw: dict, index: int) -> dict | None:
|
|
200
|
-
"""Normalize graphify edges while preserving original fields."""
|
|
201
|
-
edge = dict(raw)
|
|
202
|
-
source = endpoint_id(first_present(edge, "source", "src", "from", "from_id", "start", "u"))
|
|
203
|
-
target = endpoint_id(first_present(edge, "target", "dst", "to", "to_id", "end", "v"))
|
|
204
|
-
if not source or not target:
|
|
205
|
-
return None
|
|
206
|
-
|
|
207
|
-
relation = first_present(edge, "relation", "type", "kind", "label", "predicate", default="relates")
|
|
208
|
-
confidence = first_present(edge, "confidence", "evidence", "provenance", default="EXTRACTED")
|
|
209
|
-
score = first_present(edge, "confidence_score", "score", "weight", "probability", default=1.0)
|
|
210
|
-
|
|
211
|
-
edge["id"] = str(first_present(edge, "id", "edge_id", default=f"edge_{index + 1}"))
|
|
212
|
-
edge["source"] = source
|
|
213
|
-
edge["target"] = target
|
|
214
|
-
edge["relation"] = str(relation or "relates").lower()
|
|
215
|
-
edge["confidence"] = str(confidence or "EXTRACTED").upper()
|
|
216
|
-
edge["confidence_score"] = to_float(score, 1.0)
|
|
217
|
-
return edge
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
def _node_link_payload(data: dict) -> tuple[list, list] | None:
|
|
221
|
-
"""Read current graphify graph.json via NetworkX's node-link parser."""
|
|
222
|
-
if not isinstance(data.get("nodes"), list):
|
|
223
|
-
return None
|
|
224
|
-
if not isinstance(data.get("links"), list) and not isinstance(data.get("edges"), list):
|
|
225
|
-
return None
|
|
226
|
-
|
|
227
|
-
try:
|
|
228
|
-
from networkx.readwrite import json_graph
|
|
229
|
-
|
|
230
|
-
try:
|
|
231
|
-
graph = json_graph.node_link_graph(data, edges="links")
|
|
232
|
-
except TypeError:
|
|
233
|
-
graph = json_graph.node_link_graph(data)
|
|
234
|
-
except Exception:
|
|
235
|
-
return None
|
|
236
|
-
|
|
237
|
-
nodes = []
|
|
238
|
-
for node_id, attrs in graph.nodes(data=True):
|
|
239
|
-
node = dict(attrs)
|
|
240
|
-
node["id"] = node_id
|
|
241
|
-
nodes.append(node)
|
|
242
|
-
|
|
243
|
-
edges = []
|
|
244
|
-
for index, (source, target, attrs) in enumerate(graph.edges(data=True), 1):
|
|
245
|
-
edge = dict(attrs)
|
|
246
|
-
edge["source"] = edge.get("_src", edge.get("source", source))
|
|
247
|
-
edge["target"] = edge.get("_tgt", edge.get("target", target))
|
|
248
|
-
edge.setdefault("id", f"edge_{index}")
|
|
249
|
-
edges.append(edge)
|
|
250
|
-
return nodes, edges
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def load_graph(path: str | Path) -> tuple:
|
|
254
|
-
"""Load graph.json. Returns normalized (nodes, edges, hyperedges, metadata)."""
|
|
255
|
-
if path:
|
|
256
|
-
from graphify.security import check_graph_file_size_cap
|
|
257
|
-
try:
|
|
258
|
-
check_graph_file_size_cap(Path(path))
|
|
259
|
-
except ValueError as exc:
|
|
260
|
-
raise SystemExit(f"ERROR: {exc}") from exc
|
|
261
|
-
data = read_json(path)
|
|
262
|
-
if not isinstance(data, dict):
|
|
263
|
-
raise SystemExit(f"ERROR: graph file must contain a JSON object: {path}")
|
|
264
|
-
|
|
265
|
-
graph_block = data.get("graph") if isinstance(data.get("graph"), dict) else {}
|
|
266
|
-
meta_block = data.get("metadata") if isinstance(data.get("metadata"), dict) else {}
|
|
267
|
-
|
|
268
|
-
node_link = _node_link_payload(data)
|
|
269
|
-
if node_link:
|
|
270
|
-
raw_nodes, raw_edges = node_link
|
|
271
|
-
else:
|
|
272
|
-
raw_nodes = first_list(data.get("nodes"), data.get("vertices"), graph_block.get("nodes"), graph_block.get("vertices"))
|
|
273
|
-
raw_edges = first_list(data.get("links"), data.get("edges"), graph_block.get("links"), graph_block.get("edges"))
|
|
274
|
-
hyperedges = first_list(data.get("hyperedges"), graph_block.get("hyperedges"), data.get("groups"), graph_block.get("groups"))
|
|
275
|
-
|
|
276
|
-
nodes = [normalize_node(n, i) for i, n in enumerate(raw_nodes) if isinstance(n, dict)]
|
|
277
|
-
edges = []
|
|
278
|
-
for i, raw_edge in enumerate(raw_edges):
|
|
279
|
-
if not isinstance(raw_edge, dict):
|
|
280
|
-
continue
|
|
281
|
-
edge = normalize_edge(raw_edge, i)
|
|
282
|
-
if edge:
|
|
283
|
-
edges.append(edge)
|
|
284
|
-
|
|
285
|
-
meta = dict(graph_block)
|
|
286
|
-
meta.update(meta_block)
|
|
287
|
-
for key in ("built_at_commit", "commit", "project_name", "repo", "repository", "language_breakdown"):
|
|
288
|
-
if data.get(key) and not meta.get(key):
|
|
289
|
-
meta[key] = data.get(key)
|
|
290
|
-
if meta.get("commit") and not meta.get("built_at_commit"):
|
|
291
|
-
meta["built_at_commit"] = meta["commit"]
|
|
292
|
-
|
|
293
|
-
return nodes, edges, hyperedges, meta
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
def load_labels(path: str | Path | None) -> dict:
|
|
297
|
-
"""Load community labels from .graphify_labels.json, tolerating wrapper keys."""
|
|
298
|
-
data = read_json(path, default={})
|
|
299
|
-
if not isinstance(data, dict):
|
|
300
|
-
return {}
|
|
301
|
-
if isinstance(data.get("labels"), dict):
|
|
302
|
-
data = data["labels"]
|
|
303
|
-
if isinstance(data.get("communities"), dict):
|
|
304
|
-
data = data["communities"]
|
|
305
|
-
labels = {}
|
|
306
|
-
for key, value in data.items():
|
|
307
|
-
if isinstance(value, dict):
|
|
308
|
-
value = first_present(value, "label", "name", "title", default=key)
|
|
309
|
-
labels[str(key)] = str(value)
|
|
310
|
-
return labels
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def load_sections(path: str | Path | None) -> list:
|
|
314
|
-
"""Load section definitions from JSON file."""
|
|
315
|
-
data = read_json(path, default=[])
|
|
316
|
-
if isinstance(data, dict) and isinstance(data.get("sections"), list):
|
|
317
|
-
data = data["sections"]
|
|
318
|
-
if not isinstance(data, list):
|
|
319
|
-
raise SystemExit(f"ERROR: sections file must contain a JSON array: {path}")
|
|
320
|
-
return data
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def load_report(path: str | Path | None) -> str:
|
|
324
|
-
"""Load GRAPH_REPORT.md if it exists."""
|
|
325
|
-
if path and os.path.exists(path):
|
|
326
|
-
return Path(path).read_text(encoding="utf-8")
|
|
327
|
-
return ""
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
# ──────────────────────────────────────────────
|
|
331
|
-
# 3. Mermaid-safe label helpers
|
|
332
|
-
# ──────────────────────────────────────────────
|
|
333
|
-
|
|
334
|
-
def safe_mermaid_text(text: str) -> str:
|
|
335
|
-
"""Sanitize text for use inside a Mermaid node label.
|
|
336
|
-
|
|
337
|
-
Replaces characters that Mermaid interprets as syntax:
|
|
338
|
-
- -> (edge arrow) -> text
|
|
339
|
-
- # (comment) -> removed
|
|
340
|
-
- {} (shape syntax) -> removed
|
|
341
|
-
- backticks -> removed
|
|
342
|
-
- " -> '
|
|
343
|
-
- HTML metacharacters -> entities
|
|
344
|
-
"""
|
|
345
|
-
text = str(text or "")
|
|
346
|
-
text = text.replace('"', "'")
|
|
347
|
-
text = text.replace('`', '')
|
|
348
|
-
text = text.replace('#', '')
|
|
349
|
-
text = text.replace('|', ' ')
|
|
350
|
-
text = text.replace('{', '').replace('}', '')
|
|
351
|
-
text = text.replace("->>", " to ").replace("-->", " to ").replace("->", " to ")
|
|
352
|
-
text = " ".join(text.split())
|
|
353
|
-
return escape(text, quote=False)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def html_comment_text(text: str) -> str:
|
|
357
|
-
"""Keep generated HTML comments well-formed."""
|
|
358
|
-
return str(text or "").replace("--", "- -").replace("\n", " ")
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
def stable_ascii_id(raw: str, prefix: str = "node", limit: int = 48) -> str:
|
|
362
|
-
"""Build a Mermaid-safe ASCII identifier with a hash suffix to avoid collisions."""
|
|
363
|
-
raw = str(raw or "")
|
|
364
|
-
digest = hashlib.sha1(raw.encode("utf-8"), usedforsecurity=False).hexdigest()[:8]
|
|
365
|
-
slug = re.sub(r"[^A-Za-z0-9_]+", "_", raw)
|
|
366
|
-
slug = re.sub(r"_+", "_", slug).strip("_")
|
|
367
|
-
if not slug:
|
|
368
|
-
slug = prefix
|
|
369
|
-
if slug[0].isdigit():
|
|
370
|
-
slug = f"{prefix}_{slug}"
|
|
371
|
-
return f"{slug[:limit].rstrip('_')}_{digest}"
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
def node_mermaid_id(node: dict) -> str:
|
|
375
|
-
"""Generate a safe Mermaid node ID from a graph node.
|
|
376
|
-
|
|
377
|
-
Mermaid IDs must match [a-zA-Z][a-zA-Z0-9_]* — no dots, hyphens, slashes.
|
|
378
|
-
"""
|
|
379
|
-
return stable_ascii_id(node.get("id", "unknown"), "node")
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
def mermaid_section_id(section_id: str) -> str:
|
|
383
|
-
"""Convert a section ID (like 'cli-entry') to a safe Mermaid ID (like 'CLI_ENTRY')."""
|
|
384
|
-
return stable_ascii_id(section_id, "section").upper()
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
def safe_file_path(path: str) -> str:
|
|
388
|
-
"""Return a short, safe display path."""
|
|
389
|
-
# Truncate long paths for display
|
|
390
|
-
parts = path.split("/")
|
|
391
|
-
if len(parts) > 3:
|
|
392
|
-
return "/".join(parts[-3:])
|
|
393
|
-
return path
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
def safe_filename(text: str, fallback: str = "project") -> str:
|
|
397
|
-
"""Create a conservative filename stem from a project name."""
|
|
398
|
-
stem = re.sub(r"[^A-Za-z0-9._-]+", "-", str(text or "")).strip("-._")
|
|
399
|
-
return stem or fallback
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
def infer_project_name(graph_path: str, meta: dict) -> str:
|
|
403
|
-
"""Infer a display project name when graph metadata does not include one."""
|
|
404
|
-
if meta.get("project_name"):
|
|
405
|
-
return meta["project_name"]
|
|
406
|
-
path = Path(graph_path).resolve()
|
|
407
|
-
if path.parent.name == "graphify-out" and len(path.parents) > 1:
|
|
408
|
-
return path.parents[1].name
|
|
409
|
-
return path.parent.name or "Project"
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
def resolve_graphify_paths(args) -> dict:
|
|
413
|
-
"""Resolve project root, graphify output dir, and optional files."""
|
|
414
|
-
base = Path(args.project).expanduser() if args.project else Path.cwd()
|
|
415
|
-
if args.graphify_out:
|
|
416
|
-
graphify_out = Path(args.graphify_out).expanduser()
|
|
417
|
-
elif args.graph:
|
|
418
|
-
graphify_out = Path(args.graph).expanduser().parent
|
|
419
|
-
elif (base / "graph.json").exists():
|
|
420
|
-
graphify_out = base
|
|
421
|
-
else:
|
|
422
|
-
graphify_out = base / "graphify-out"
|
|
423
|
-
|
|
424
|
-
project_root = graphify_out.parent if graphify_out.name == "graphify-out" else base
|
|
425
|
-
graph = Path(args.graph).expanduser() if args.graph else graphify_out / "graph.json"
|
|
426
|
-
report = Path(args.report).expanduser() if args.report else graphify_out / "GRAPH_REPORT.md"
|
|
427
|
-
labels = Path(args.labels).expanduser() if args.labels else graphify_out / ".graphify_labels.json"
|
|
428
|
-
sections = Path(args.sections).expanduser() if args.sections else None
|
|
429
|
-
return {
|
|
430
|
-
"base": project_root,
|
|
431
|
-
"graphify_out": graphify_out,
|
|
432
|
-
"graph": graph,
|
|
433
|
-
"report": report,
|
|
434
|
-
"labels": labels,
|
|
435
|
-
"sections": sections,
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
def is_zh(lang: str) -> bool:
|
|
440
|
-
"""Return true when localized strings should be Chinese."""
|
|
441
|
-
return (lang or "").lower().startswith("zh")
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
def pick_text(lang: str, zh: str, en: str) -> str:
|
|
445
|
-
"""Small localization helper for generated copy."""
|
|
446
|
-
return zh if is_zh(lang) else en
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
def detect_lang(lang: str, nodes: list, labels: dict) -> str:
|
|
450
|
-
"""Resolve auto language from labels and node names."""
|
|
451
|
-
if lang and lang.lower() != "auto":
|
|
452
|
-
return lang
|
|
453
|
-
sample = " ".join(
|
|
454
|
-
list(labels.values())[:50]
|
|
455
|
-
+ [str(n.get("label", "")) for n in nodes[:200]]
|
|
456
|
-
+ [str(n.get("source_file", "")) for n in nodes[:100]]
|
|
457
|
-
)
|
|
458
|
-
return "zh-CN" if re.search(r"[\u4e00-\u9fff]", sample) else "en"
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def truncate_text(text: str, limit: int) -> str:
|
|
462
|
-
"""Truncate without splitting Mermaid syntax."""
|
|
463
|
-
text = " ".join(str(text or "").split())
|
|
464
|
-
if len(text) <= limit:
|
|
465
|
-
return text
|
|
466
|
-
return text[: max(0, limit - 3)].rstrip() + "..."
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
def humanize_label(label: str, source_file: str = "") -> str:
|
|
470
|
-
"""Convert graph labels into short labels people can scan in a diagram."""
|
|
471
|
-
label = str(label or "").strip()
|
|
472
|
-
if not label:
|
|
473
|
-
return Path(source_file).name if source_file else "Unknown"
|
|
474
|
-
if label.startswith(".") and label.endswith("()"):
|
|
475
|
-
return label[1:]
|
|
476
|
-
if label.endswith((".py", ".ts", ".tsx", ".js", ".jsx", ".go", ".rs", ".java", ".rb")):
|
|
477
|
-
return Path(label).name
|
|
478
|
-
if "_" in label and " " not in label and len(label) > 28:
|
|
479
|
-
parts = [p for p in label.split("_") if p]
|
|
480
|
-
if parts:
|
|
481
|
-
label = " ".join(parts[-3:])
|
|
482
|
-
return truncate_text(label, 42)
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
def node_kind(node: dict) -> str:
|
|
486
|
-
"""Classify a graph node for Mermaid styling and table tags."""
|
|
487
|
-
label = str(node.get("label") or node.get("id") or "").lower()
|
|
488
|
-
source_file = str(node.get("source_file") or "").lower()
|
|
489
|
-
file_type = str(node.get("file_type") or "").lower()
|
|
490
|
-
node_type = str(node.get("node_type") or "").lower()
|
|
491
|
-
if node_type in {"class", "klass", "struct", "interface", "enum", "trait", "model"}:
|
|
492
|
-
return "klass"
|
|
493
|
-
if node_type in {"module", "file", "package", "namespace"}:
|
|
494
|
-
return "module"
|
|
495
|
-
if node_type in {"endpoint", "route", "api", "handler", "controller"}:
|
|
496
|
-
return "api"
|
|
497
|
-
if node_type in {"test", "spec"}:
|
|
498
|
-
return "test"
|
|
499
|
-
if node_type in {"component", "hook", "view", "page"}:
|
|
500
|
-
return "ui"
|
|
501
|
-
if file_type in {"rationale", "document"}:
|
|
502
|
-
return "concept"
|
|
503
|
-
if "test" in source_file or label.startswith("test_") or "spec" in source_file:
|
|
504
|
-
return "test"
|
|
505
|
-
if any(word in label for word in ("endpoint", "router", "api", "route")):
|
|
506
|
-
return "api"
|
|
507
|
-
if any(word in label for word in ("cli", "command", "click", "typer")):
|
|
508
|
-
return "entry"
|
|
509
|
-
if any(word in label for word in ("async", "await", "stream", "sse")):
|
|
510
|
-
return "async"
|
|
511
|
-
raw_label = str(node.get("label") or "")
|
|
512
|
-
hook_like = raw_label.startswith("use") and len(raw_label) > 3 and (raw_label[3].isupper() or raw_label[3] in "_-")
|
|
513
|
-
if any(word in label for word in ("component", "props", "hook", "store")) or hook_like or source_file.endswith((".tsx", ".jsx", ".vue", ".svelte")):
|
|
514
|
-
return "ui"
|
|
515
|
-
raw = raw_label
|
|
516
|
-
if raw[:1].isupper() and not raw.endswith("()"):
|
|
517
|
-
return "klass"
|
|
518
|
-
if raw.endswith((".py", ".ts", ".tsx", ".js", ".jsx", ".go", ".rs", ".java", ".kt", ".rb", ".php", ".cs", ".swift", ".vue", ".svelte")):
|
|
519
|
-
return "module"
|
|
520
|
-
return "function"
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
def relation_label(relation: str, lang: str) -> str:
|
|
524
|
-
"""Map graph edge relation names to short diagram labels."""
|
|
525
|
-
relation = str(relation or "").strip()
|
|
526
|
-
zh = {
|
|
527
|
-
"calls": "调用",
|
|
528
|
-
"uses": "使用",
|
|
529
|
-
"imports": "导入",
|
|
530
|
-
"imports_from": "导入",
|
|
531
|
-
"method": "方法",
|
|
532
|
-
"contains": "包含",
|
|
533
|
-
"rationale_for": "说明",
|
|
534
|
-
"conceptually_related_to": "相关",
|
|
535
|
-
"participate_in": "参与",
|
|
536
|
-
"form": "组成",
|
|
537
|
-
}
|
|
538
|
-
en = {
|
|
539
|
-
"calls": "calls",
|
|
540
|
-
"uses": "uses",
|
|
541
|
-
"imports": "imports",
|
|
542
|
-
"imports_from": "imports",
|
|
543
|
-
"method": "method",
|
|
544
|
-
"contains": "contains",
|
|
545
|
-
"rationale_for": "explains",
|
|
546
|
-
"conceptually_related_to": "relates",
|
|
547
|
-
"participate_in": "joins",
|
|
548
|
-
"form": "forms",
|
|
549
|
-
}
|
|
550
|
-
mapped = (zh if is_zh(lang) else en).get(relation, relation.replace("_", " "))
|
|
551
|
-
return safe_mermaid_text(mapped)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
def preferred_edges(edges: list, allow_structure: bool = False) -> list:
|
|
555
|
-
"""Filter to edges that make a readable call-flow diagram."""
|
|
556
|
-
primary = {"calls", "uses", "method", "imports", "imports_from"}
|
|
557
|
-
secondary = {"contains", "rationale_for", "conceptually_related_to"}
|
|
558
|
-
selected = []
|
|
559
|
-
for edge in edges:
|
|
560
|
-
if not should_include_edge(edge):
|
|
561
|
-
continue
|
|
562
|
-
relation = edge.get("relation", "")
|
|
563
|
-
if relation in primary or (allow_structure and relation in secondary):
|
|
564
|
-
selected.append(edge)
|
|
565
|
-
if selected:
|
|
566
|
-
return selected
|
|
567
|
-
return [edge for edge in edges if should_include_edge(edge)]
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
def edge_score(edge: dict) -> float:
|
|
571
|
-
"""Rank edges by confidence and usefulness for diagrams."""
|
|
572
|
-
relation = edge.get("relation", "")
|
|
573
|
-
score = to_float(edge.get("confidence_score", 1.0), 1.0)
|
|
574
|
-
if str(edge.get("confidence", "")).upper() == "EXTRACTED":
|
|
575
|
-
score += 2.0
|
|
576
|
-
if relation in {"calls", "uses", "method"}:
|
|
577
|
-
score += 1.0
|
|
578
|
-
elif relation in {"imports", "imports_from"}:
|
|
579
|
-
score += 0.6
|
|
580
|
-
elif relation == "contains":
|
|
581
|
-
score -= 0.2
|
|
582
|
-
elif relation == "rationale_for":
|
|
583
|
-
score -= 0.6
|
|
584
|
-
return score
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
def mermaid_init(scale: float, direction: str = "LR") -> str:
|
|
588
|
-
"""Return a Mermaid init directive that scales diagrams using Mermaid config."""
|
|
589
|
-
scale = max(0.65, min(float(scale or 1.0), 1.8))
|
|
590
|
-
config = {
|
|
591
|
-
"theme": "dark",
|
|
592
|
-
"themeVariables": {
|
|
593
|
-
"fontSize": f"{round(15 * scale, 1)}px",
|
|
594
|
-
"fontFamily": "Segoe UI, system-ui, sans-serif",
|
|
595
|
-
"primaryColor": "#1e293b",
|
|
596
|
-
"primaryTextColor": "#e2e8f0",
|
|
597
|
-
"primaryBorderColor": "#38bdf8",
|
|
598
|
-
"secondaryColor": "#0f172a",
|
|
599
|
-
"tertiaryColor": "#334155",
|
|
600
|
-
"lineColor": "#64748b",
|
|
601
|
-
"textColor": "#e2e8f0",
|
|
602
|
-
},
|
|
603
|
-
"flowchart": {
|
|
604
|
-
"htmlLabels": True,
|
|
605
|
-
"curve": "basis",
|
|
606
|
-
"nodeSpacing": round(48 * scale),
|
|
607
|
-
"rankSpacing": round(64 * scale),
|
|
608
|
-
"padding": round(14 * scale),
|
|
609
|
-
"diagramPadding": round(10 * scale),
|
|
610
|
-
"useMaxWidth": True,
|
|
611
|
-
},
|
|
612
|
-
}
|
|
613
|
-
return f"%%{{init: {json.dumps(config, ensure_ascii=False)}}}%%\nflowchart {direction}"
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
def mermaid_class_defs() -> list:
|
|
617
|
-
"""Shared Mermaid-native styles for readable diagrams."""
|
|
618
|
-
return [
|
|
619
|
-
" classDef entry fill:#422006,stroke:#fbbf24,color:#fde68a,stroke-width:1px;",
|
|
620
|
-
" classDef api fill:#450a0a,stroke:#f87171,color:#fee2e2,stroke-width:1px;",
|
|
621
|
-
" classDef async fill:#2e1065,stroke:#a78bfa,color:#ede9fe,stroke-width:1px;",
|
|
622
|
-
" classDef klass fill:#064e3b,stroke:#34d399,color:#d1fae5,stroke-width:1px;",
|
|
623
|
-
" classDef ui fill:#831843,stroke:#f472b6,color:#fce7f3,stroke-width:1px;",
|
|
624
|
-
" classDef module fill:#172554,stroke:#60a5fa,color:#dbeafe,stroke-width:1px;",
|
|
625
|
-
" classDef test fill:#3f3f46,stroke:#a1a1aa,color:#f4f4f5,stroke-width:1px;",
|
|
626
|
-
" classDef concept fill:#292524,stroke:#a8a29e,color:#fafaf9,stroke-dasharray:4 3;",
|
|
627
|
-
" classDef function fill:#0f172a,stroke:#38bdf8,color:#e0f2fe,stroke-width:1px;",
|
|
628
|
-
]
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
# ──────────────────────────────────────────────
|
|
632
|
-
# 4. Community and section indexing
|
|
633
|
-
# ──────────────────────────────────────────────
|
|
634
|
-
|
|
635
|
-
def build_community_index(nodes: list) -> dict:
|
|
636
|
-
"""Map community_id (str) -> list of nodes."""
|
|
637
|
-
idx = defaultdict(list)
|
|
638
|
-
for n in nodes:
|
|
639
|
-
cid = str(n.get("community", "unknown"))
|
|
640
|
-
idx[cid].append(n)
|
|
641
|
-
return idx
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
def html_anchor_id(raw: str, fallback: str, used: set) -> str:
|
|
645
|
-
"""Generate a stable, unique HTML anchor ID."""
|
|
646
|
-
raw = str(raw or fallback or "")
|
|
647
|
-
base = re.sub(r"[^a-z0-9]+", "-", raw.lower()).strip("-")
|
|
648
|
-
if not base:
|
|
649
|
-
base = re.sub(r"[^a-z0-9]+", "-", str(fallback or "section").lower()).strip("-")
|
|
650
|
-
if not base:
|
|
651
|
-
base = "section"
|
|
652
|
-
base = base[:48].strip("-") or "section"
|
|
653
|
-
candidate = base
|
|
654
|
-
if candidate in used:
|
|
655
|
-
candidate = f"{base}-{hashlib.sha1(raw.encode('utf-8'), usedforsecurity=False).hexdigest()[:6]}"
|
|
656
|
-
suffix = 2
|
|
657
|
-
while candidate in used:
|
|
658
|
-
candidate = f"{base}-{suffix}"
|
|
659
|
-
suffix += 1
|
|
660
|
-
used.add(candidate)
|
|
661
|
-
return candidate
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
def normalize_communities(value) -> list:
|
|
665
|
-
"""Normalize section community lists from JSON or simple strings."""
|
|
666
|
-
if isinstance(value, list):
|
|
667
|
-
return value
|
|
668
|
-
if value in (None, ""):
|
|
669
|
-
return []
|
|
670
|
-
if isinstance(value, str):
|
|
671
|
-
return [part.strip() for part in value.split(",") if part.strip()]
|
|
672
|
-
return [value]
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
def normalize_sections(sections: list, lang: str) -> list:
|
|
676
|
-
"""Ensure sections have safe unique IDs and an overview section first."""
|
|
677
|
-
overview_name = pick_text(lang, "架构总览", "Architecture Overview")
|
|
678
|
-
normalized = [{"id": "overview", "name": overview_name, "communities": []}]
|
|
679
|
-
used = {"overview", "hyperedges", "stats"}
|
|
680
|
-
|
|
681
|
-
for index, raw in enumerate(sections or [], 1):
|
|
682
|
-
if not isinstance(raw, dict):
|
|
683
|
-
continue
|
|
684
|
-
raw_id = str(raw.get("id") or raw.get("key") or raw.get("name") or f"section-{index}")
|
|
685
|
-
raw_name = str(raw.get("name") or raw.get("label") or raw_id)
|
|
686
|
-
if raw_id.lower() == "overview":
|
|
687
|
-
normalized[0]["name"] = raw_name or overview_name
|
|
688
|
-
continue
|
|
689
|
-
|
|
690
|
-
sid = html_anchor_id(raw_id, f"section-{index}", used)
|
|
691
|
-
normalized.append({
|
|
692
|
-
"id": sid,
|
|
693
|
-
"name": raw_name,
|
|
694
|
-
"communities": normalize_communities(raw.get("communities", raw.get("community"))),
|
|
695
|
-
})
|
|
696
|
-
return normalized
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
def label_for_community(cid: str, labels: dict, nodes: list, lang: str) -> str:
|
|
700
|
-
"""Choose a readable section name for a community."""
|
|
701
|
-
if str(cid) in labels and labels[str(cid)]:
|
|
702
|
-
return labels[str(cid)]
|
|
703
|
-
keywords = section_keywords(nodes, 3)
|
|
704
|
-
if keywords:
|
|
705
|
-
return " ".join(word.title() for word in keywords[:3])
|
|
706
|
-
return pick_text(lang, f"社区 {cid}", f"Community {cid}")
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
SECTION_ARCHETYPES = [
|
|
710
|
-
(
|
|
711
|
-
"extract-pipeline",
|
|
712
|
-
"提取管线",
|
|
713
|
-
"Extraction Pipeline",
|
|
714
|
-
{
|
|
715
|
-
"extract", "extractor", "tree", "sitter", "parser", "language",
|
|
716
|
-
"python", "javascript", "typescript", "rust", "java", "go",
|
|
717
|
-
"ast", "calls", "imports", "multilang",
|
|
718
|
-
},
|
|
719
|
-
),
|
|
720
|
-
(
|
|
721
|
-
"build-graph",
|
|
722
|
-
"图谱构建",
|
|
723
|
-
"Graph Build",
|
|
724
|
-
{
|
|
725
|
-
"build", "graph", "merge", "dedup", "node", "edge", "hyperedge",
|
|
726
|
-
"json", "schema", "normalize", "confidence",
|
|
727
|
-
},
|
|
728
|
-
),
|
|
729
|
-
(
|
|
730
|
-
"analysis-clustering",
|
|
731
|
-
"分析聚类",
|
|
732
|
-
"Analysis & Clustering",
|
|
733
|
-
{
|
|
734
|
-
"cluster", "community", "leiden", "cohesion", "analyze", "god",
|
|
735
|
-
"surprise", "question", "query", "path", "explain", "benchmark",
|
|
736
|
-
},
|
|
737
|
-
),
|
|
738
|
-
(
|
|
739
|
-
"outputs-docs",
|
|
740
|
-
"输出文档",
|
|
741
|
-
"Outputs & Docs",
|
|
742
|
-
{
|
|
743
|
-
"export", "html", "wiki", "obsidian", "canvas", "svg", "graphml",
|
|
744
|
-
"report", "callflow", "mermaid", "tree", "documentation",
|
|
745
|
-
},
|
|
746
|
-
),
|
|
747
|
-
(
|
|
748
|
-
"cli-skills",
|
|
749
|
-
"CLI 与技能安装",
|
|
750
|
-
"CLI & Skill Installers",
|
|
751
|
-
{
|
|
752
|
-
"main", "install", "uninstall", "skill", "agent", "claude",
|
|
753
|
-
"codex", "opencode", "aider", "copilot", "kiro", "vscode",
|
|
754
|
-
"hook", "command",
|
|
755
|
-
},
|
|
756
|
-
),
|
|
757
|
-
(
|
|
758
|
-
"ingest-cache-update",
|
|
759
|
-
"摄取与增量更新",
|
|
760
|
-
"Ingestion & Updates",
|
|
761
|
-
{
|
|
762
|
-
"ingest", "fetch", "download", "url", "html", "markdown",
|
|
763
|
-
"cache", "manifest", "watch", "update", "incremental",
|
|
764
|
-
"transcribe", "video", "audio", "google",
|
|
765
|
-
},
|
|
766
|
-
),
|
|
767
|
-
(
|
|
768
|
-
"serve-api",
|
|
769
|
-
"服务 API",
|
|
770
|
-
"Serving API",
|
|
771
|
-
{
|
|
772
|
-
"serve", "api", "request", "response", "endpoint", "router",
|
|
773
|
-
"handle", "upload", "search", "delete", "enrich",
|
|
774
|
-
},
|
|
775
|
-
),
|
|
776
|
-
(
|
|
777
|
-
"security-global",
|
|
778
|
-
"安全与全局图",
|
|
779
|
-
"Security & Global Graph",
|
|
780
|
-
{
|
|
781
|
-
"security", "safe", "ssrf", "xss", "path", "traversal",
|
|
782
|
-
"global", "prefix", "prune", "repo", "clone",
|
|
783
|
-
},
|
|
784
|
-
),
|
|
785
|
-
(
|
|
786
|
-
"tests-fixtures",
|
|
787
|
-
"测试与样例",
|
|
788
|
-
"Tests & Fixtures",
|
|
789
|
-
{
|
|
790
|
-
"test", "tests", "fixture", "fixtures", "sample", "assert",
|
|
791
|
-
"pytest", "mock",
|
|
792
|
-
},
|
|
793
|
-
),
|
|
794
|
-
]
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
def _community_text(nodes: list, label: str = "") -> str:
|
|
798
|
-
parts = [label]
|
|
799
|
-
for node in nodes[:80]:
|
|
800
|
-
parts.append(str(node.get("label", "")))
|
|
801
|
-
parts.append(str(node.get("source_file", "")))
|
|
802
|
-
parts.append(str(node.get("node_type", "")))
|
|
803
|
-
parts.append(str(node.get("file_type", "")))
|
|
804
|
-
return " ".join(parts).lower()
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
def _keyword_score(text: str, keywords: set[str]) -> int:
|
|
808
|
-
score = 0
|
|
809
|
-
for keyword in keywords:
|
|
810
|
-
score += len(re.findall(rf"(?<![a-z0-9]){re.escape(keyword)}(?![a-z0-9])", text))
|
|
811
|
-
return score
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
def _rank_grouped_sections(grouped: dict, max_sections: int) -> tuple[list, list]:
|
|
815
|
-
"""Return selected grouped sections and overflow communities."""
|
|
816
|
-
ranked = sorted(
|
|
817
|
-
grouped.values(),
|
|
818
|
-
key=lambda sec: (sec["priority"], -sec["node_count"], sec["id"]),
|
|
819
|
-
)
|
|
820
|
-
cap = max(1, int(max_sections or 15))
|
|
821
|
-
selected = ranked[:cap]
|
|
822
|
-
overflow = ranked[cap:]
|
|
823
|
-
overflow_communities = []
|
|
824
|
-
for sec in overflow:
|
|
825
|
-
overflow_communities.extend(sec["communities"])
|
|
826
|
-
return selected, overflow_communities
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
def derive_sections_from_communities(nodes: list, labels: dict, lang: str, max_sections: int) -> list:
|
|
830
|
-
"""Derive architecture-oriented sections when no sections JSON is supplied."""
|
|
831
|
-
comm_idx = build_community_index(nodes)
|
|
832
|
-
sections = [{"id": "overview", "name": pick_text(lang, "架构总览", "Architecture Overview"), "communities": []}]
|
|
833
|
-
grouped = {}
|
|
834
|
-
unassigned = []
|
|
835
|
-
|
|
836
|
-
for cid, community_nodes in sorted(comm_idx.items(), key=lambda item: (-len(item[1]), str(item[0]))):
|
|
837
|
-
label = label_for_community(cid, labels, community_nodes, lang)
|
|
838
|
-
text = _community_text(community_nodes, label)
|
|
839
|
-
best = None
|
|
840
|
-
best_score = 0
|
|
841
|
-
for priority, (sid, zh_name, en_name, keywords) in enumerate(SECTION_ARCHETYPES):
|
|
842
|
-
score = _keyword_score(text, keywords)
|
|
843
|
-
if score > best_score:
|
|
844
|
-
best = (priority, sid, zh_name, en_name)
|
|
845
|
-
best_score = score
|
|
846
|
-
|
|
847
|
-
if best and best_score >= 2:
|
|
848
|
-
priority, sid, zh_name, en_name = best
|
|
849
|
-
sec = grouped.setdefault(
|
|
850
|
-
sid,
|
|
851
|
-
{
|
|
852
|
-
"id": sid,
|
|
853
|
-
"name": pick_text(lang, zh_name, en_name),
|
|
854
|
-
"communities": [],
|
|
855
|
-
"node_count": 0,
|
|
856
|
-
"priority": priority,
|
|
857
|
-
},
|
|
858
|
-
)
|
|
859
|
-
sec["communities"].append(cid)
|
|
860
|
-
sec["node_count"] += len(community_nodes)
|
|
861
|
-
else:
|
|
862
|
-
unassigned.append((cid, community_nodes, label))
|
|
863
|
-
|
|
864
|
-
selected, overflow_communities = _rank_grouped_sections(grouped, max(1, int(max_sections or 15)) - 1)
|
|
865
|
-
sections.extend(
|
|
866
|
-
{"id": sec["id"], "name": sec["name"], "communities": sec["communities"]}
|
|
867
|
-
for sec in selected
|
|
868
|
-
)
|
|
869
|
-
|
|
870
|
-
remaining_slots = max(0, int(max_sections or 15) - (len(sections) - 1) - 1)
|
|
871
|
-
for cid, community_nodes, label in unassigned[:remaining_slots]:
|
|
872
|
-
sections.append({"id": str(label or f"community-{cid}"), "name": label, "communities": [cid]})
|
|
873
|
-
|
|
874
|
-
other_communities = overflow_communities + [cid for cid, _, _ in unassigned[remaining_slots:]]
|
|
875
|
-
if other_communities:
|
|
876
|
-
sections.append({
|
|
877
|
-
"id": "other",
|
|
878
|
-
"name": pick_text(lang, "其他", "Other"),
|
|
879
|
-
"communities": other_communities,
|
|
880
|
-
})
|
|
881
|
-
return sections
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
def build_section_node_map(sections: list, comm_idx: dict) -> dict:
|
|
885
|
-
"""Map section_id -> list of nodes belonging to its communities."""
|
|
886
|
-
section_nodes = {}
|
|
887
|
-
for sec in sections:
|
|
888
|
-
sid = sec["id"]
|
|
889
|
-
if sid == "overview":
|
|
890
|
-
section_nodes[sid] = []
|
|
891
|
-
continue
|
|
892
|
-
nodes = []
|
|
893
|
-
for cid in sec.get("communities", []):
|
|
894
|
-
nodes.extend(comm_idx.get(str(cid), []))
|
|
895
|
-
section_nodes[sid] = nodes
|
|
896
|
-
return section_nodes
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
def node_in_section(node_id: str, section_node_ids: set) -> bool:
|
|
900
|
-
"""Check if a node belongs to a section."""
|
|
901
|
-
return node_id in section_node_ids
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
# ──────────────────────────────────────────────
|
|
905
|
-
# 5. Edge analysis
|
|
906
|
-
# ──────────────────────────────────────────────
|
|
907
|
-
|
|
908
|
-
def classify_edges(edges: list, section_nodes_map: dict) -> dict:
|
|
909
|
-
"""Classify edges as intra-section or inter-section.
|
|
910
|
-
|
|
911
|
-
Returns:
|
|
912
|
-
{
|
|
913
|
-
"intra": {section_id: [edges]},
|
|
914
|
-
"inter": [edges],
|
|
915
|
-
"orphan": [edges] # one endpoint not in any section
|
|
916
|
-
}
|
|
917
|
-
"""
|
|
918
|
-
# Build node -> section lookup
|
|
919
|
-
node_section = {}
|
|
920
|
-
for sid, nodes in section_nodes_map.items():
|
|
921
|
-
for n in nodes:
|
|
922
|
-
node_section[n.get("id")] = sid
|
|
923
|
-
|
|
924
|
-
intra = defaultdict(list)
|
|
925
|
-
inter = []
|
|
926
|
-
orphan = []
|
|
927
|
-
|
|
928
|
-
for e in edges:
|
|
929
|
-
src = e.get("source", "")
|
|
930
|
-
tgt = e.get("target", "")
|
|
931
|
-
src_sec = node_section.get(src)
|
|
932
|
-
tgt_sec = node_section.get(tgt)
|
|
933
|
-
|
|
934
|
-
if src_sec is None or tgt_sec is None:
|
|
935
|
-
orphan.append(e)
|
|
936
|
-
elif src_sec == tgt_sec:
|
|
937
|
-
intra[src_sec].append(e)
|
|
938
|
-
else:
|
|
939
|
-
inter.append(e)
|
|
940
|
-
|
|
941
|
-
return {"intra": dict(intra), "inter": inter, "orphan": orphan, "node_section": node_section}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
def should_include_edge(edge: dict) -> bool:
|
|
945
|
-
"""Decide whether to auto-include an edge in Mermaid output."""
|
|
946
|
-
conf = str(edge.get("confidence", "EXTRACTED")).upper()
|
|
947
|
-
score = to_float(edge.get("confidence_score", 1.0), 1.0)
|
|
948
|
-
|
|
949
|
-
if conf == "EXTRACTED":
|
|
950
|
-
return True
|
|
951
|
-
if conf == "INFERRED" and score >= 0.85:
|
|
952
|
-
return True
|
|
953
|
-
# Low-confidence INFERRED or AMBIGUOUS: comment out for LLM review
|
|
954
|
-
return False
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
# ──────────────────────────────────────────────
|
|
958
|
-
# 6. Mermaid diagram generators
|
|
959
|
-
# ──────────────────────────────────────────────
|
|
960
|
-
|
|
961
|
-
def node_degree_scores(edges: list) -> Counter:
|
|
962
|
-
"""Score nodes by useful edge participation."""
|
|
963
|
-
scores = Counter()
|
|
964
|
-
for edge in edges:
|
|
965
|
-
score = edge_score(edge)
|
|
966
|
-
scores[edge.get("source", "")] += score
|
|
967
|
-
scores[edge.get("target", "")] += score
|
|
968
|
-
return scores
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
def node_importance(node: dict) -> float:
|
|
972
|
-
"""Use graphify centrality fields when available."""
|
|
973
|
-
for key in ("pagerank", "page_rank", "pageRank", "rank", "centrality", "score"):
|
|
974
|
-
if key in node:
|
|
975
|
-
return to_float(node.get(key), 0.0)
|
|
976
|
-
return 0.0
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
def select_diagram_nodes(nodes: list, edges: list, max_nodes: int) -> list:
|
|
980
|
-
"""Select a compact, connected subset of nodes for readable diagrams."""
|
|
981
|
-
node_by_id = {n.get("id"): n for n in nodes}
|
|
982
|
-
usable_edges = preferred_edges(edges, allow_structure=False)
|
|
983
|
-
if not usable_edges:
|
|
984
|
-
usable_edges = preferred_edges(edges, allow_structure=True)
|
|
985
|
-
scores = node_degree_scores(usable_edges)
|
|
986
|
-
outgoing = Counter(edge.get("source", "") for edge in usable_edges)
|
|
987
|
-
incoming = Counter(edge.get("target", "") for edge in usable_edges)
|
|
988
|
-
selected = []
|
|
989
|
-
seen = set()
|
|
990
|
-
|
|
991
|
-
def add_node(nid: str) -> bool:
|
|
992
|
-
node = node_by_id.get(nid)
|
|
993
|
-
if not node or nid in seen:
|
|
994
|
-
return False
|
|
995
|
-
kind = node_kind(node)
|
|
996
|
-
if kind == "concept" and len(selected) >= max(4, max_nodes // 3):
|
|
997
|
-
return False
|
|
998
|
-
selected.append(node)
|
|
999
|
-
seen.add(nid)
|
|
1000
|
-
return len(selected) >= max_nodes
|
|
1001
|
-
|
|
1002
|
-
# Start with likely entry points: nodes that call out more than they are called.
|
|
1003
|
-
entry_candidates = sorted(
|
|
1004
|
-
node_by_id,
|
|
1005
|
-
key=lambda nid: (-(outgoing[nid] - incoming[nid]), -outgoing[nid], str(nid)),
|
|
1006
|
-
)
|
|
1007
|
-
for nid in entry_candidates[: max(3, max_nodes // 3)]:
|
|
1008
|
-
if outgoing[nid] > 0 and add_node(nid):
|
|
1009
|
-
return selected
|
|
1010
|
-
|
|
1011
|
-
# Then pull in the most useful neighbors from the strongest edges.
|
|
1012
|
-
for edge in sorted(usable_edges, key=edge_score, reverse=True):
|
|
1013
|
-
for nid in (edge.get("source"), edge.get("target")):
|
|
1014
|
-
if add_node(nid):
|
|
1015
|
-
return selected
|
|
1016
|
-
|
|
1017
|
-
def fallback_key(node: dict) -> tuple:
|
|
1018
|
-
nid = node.get("id", "")
|
|
1019
|
-
kind_penalty = 1 if node_kind(node) == "concept" else 0
|
|
1020
|
-
return (
|
|
1021
|
-
kind_penalty,
|
|
1022
|
-
-scores.get(nid, 0),
|
|
1023
|
-
-node_importance(node),
|
|
1024
|
-
safe_file_path(node.get("source_file", "")),
|
|
1025
|
-
humanize_label(node.get("label", nid)),
|
|
1026
|
-
)
|
|
1027
|
-
|
|
1028
|
-
for node in sorted(nodes, key=fallback_key):
|
|
1029
|
-
nid = node.get("id")
|
|
1030
|
-
if nid not in seen:
|
|
1031
|
-
selected.append(node)
|
|
1032
|
-
seen.add(nid)
|
|
1033
|
-
if len(selected) >= max_nodes:
|
|
1034
|
-
break
|
|
1035
|
-
return selected
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
def node_label(node: dict) -> str:
|
|
1039
|
-
"""Build a readable Mermaid node label."""
|
|
1040
|
-
label = humanize_label(node.get("label") or node.get("id"), node.get("source_file", ""))
|
|
1041
|
-
source_file = safe_file_path(node.get("source_file", ""))
|
|
1042
|
-
if source_file and not label.endswith(Path(source_file).name):
|
|
1043
|
-
return f"{safe_mermaid_text(label)}<br/><small>{safe_mermaid_text(source_file)}</small>"
|
|
1044
|
-
return safe_mermaid_text(label)
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
def group_nodes_by_file(nodes: list) -> dict:
|
|
1048
|
-
"""Group selected nodes by source file for Mermaid subgraphs."""
|
|
1049
|
-
groups = defaultdict(list)
|
|
1050
|
-
for node in nodes:
|
|
1051
|
-
source_file = safe_file_path(node.get("source_file", "")) or "External / generated"
|
|
1052
|
-
groups[source_file].append(node)
|
|
1053
|
-
return dict(sorted(groups.items(), key=lambda item: (-len(item[1]), item[0])))
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
def section_edge_summary(classified_edges: dict) -> dict:
|
|
1057
|
-
"""Aggregate inter-section edge counts and relation names."""
|
|
1058
|
-
node_section = classified_edges.get("node_section", {})
|
|
1059
|
-
summary = defaultdict(lambda: {"count": 0, "relations": Counter()})
|
|
1060
|
-
for edge in classified_edges.get("inter", []):
|
|
1061
|
-
if not should_include_edge(edge):
|
|
1062
|
-
continue
|
|
1063
|
-
src_sec = node_section.get(edge.get("source"))
|
|
1064
|
-
tgt_sec = node_section.get(edge.get("target"))
|
|
1065
|
-
if not src_sec or not tgt_sec or src_sec == tgt_sec:
|
|
1066
|
-
continue
|
|
1067
|
-
key = (src_sec, tgt_sec)
|
|
1068
|
-
summary[key]["count"] += 1
|
|
1069
|
-
summary[key]["relations"][edge.get("relation", "relates")] += 1
|
|
1070
|
-
return summary
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
def generate_overview_graph(sections: list, section_nodes_map: dict,
|
|
1074
|
-
classified_edges: dict, labels: dict, lang: str,
|
|
1075
|
-
diagram_scale: float) -> str:
|
|
1076
|
-
"""Generate a readable section-level architecture overview."""
|
|
1077
|
-
lines = [mermaid_init(diagram_scale, "LR")]
|
|
1078
|
-
section_defs = [sec for sec in sections if sec["id"] != "overview"]
|
|
1079
|
-
|
|
1080
|
-
for sec in section_defs:
|
|
1081
|
-
sid = mermaid_section_id(sec["id"])
|
|
1082
|
-
node_count = len(section_nodes_map.get(sec["id"], []))
|
|
1083
|
-
label = (
|
|
1084
|
-
f"{safe_mermaid_text(sec.get('name', sec['id']))}"
|
|
1085
|
-
f"<br/><small>{node_count} {safe_mermaid_text('nodes')}</small>"
|
|
1086
|
-
)
|
|
1087
|
-
lines.append(f' {sid}("{label}")')
|
|
1088
|
-
lines.append(f" class {sid} module;")
|
|
1089
|
-
|
|
1090
|
-
aggregated = section_edge_summary(classified_edges)
|
|
1091
|
-
for (src, tgt), data in sorted(aggregated.items(), key=lambda item: item[1]["count"], reverse=True)[:12]:
|
|
1092
|
-
src_id = mermaid_section_id(src)
|
|
1093
|
-
tgt_id = mermaid_section_id(tgt)
|
|
1094
|
-
relation, _ = data["relations"].most_common(1)[0]
|
|
1095
|
-
label = relation_label(relation, lang)
|
|
1096
|
-
if data["count"] > 1:
|
|
1097
|
-
label = f"{label} x{data['count']}"
|
|
1098
|
-
lines.append(f" {src_id} -->|{label}| {tgt_id}")
|
|
1099
|
-
|
|
1100
|
-
if not aggregated and len(section_defs) > 1:
|
|
1101
|
-
for prev, cur in zip(section_defs, section_defs[1:]):
|
|
1102
|
-
lines.append(f" {mermaid_section_id(prev['id'])} -.-> {mermaid_section_id(cur['id'])}")
|
|
1103
|
-
|
|
1104
|
-
lines.extend(mermaid_class_defs())
|
|
1105
|
-
return "\n".join(lines)
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
def generate_section_flowchart(section_id: str, section_name: str,
|
|
1109
|
-
nodes: list, edges: list, lang: str,
|
|
1110
|
-
diagram_scale: float, max_nodes: int,
|
|
1111
|
-
max_edges: int) -> str:
|
|
1112
|
-
"""Generate a compact, human-readable call-flow chart for a section."""
|
|
1113
|
-
lines = [mermaid_init(diagram_scale, "LR")]
|
|
1114
|
-
lines.append(f" %% Section: {safe_mermaid_text(section_name)} ({len(nodes)} nodes, {len(edges)} edges)")
|
|
1115
|
-
|
|
1116
|
-
if not nodes:
|
|
1117
|
-
empty_label = pick_text(lang, f"{section_name} - 无节点", f"{section_name} - no nodes")
|
|
1118
|
-
lines.append(f' empty("{safe_mermaid_text(empty_label)}")')
|
|
1119
|
-
lines.extend(mermaid_class_defs())
|
|
1120
|
-
return "\n".join(lines)
|
|
1121
|
-
|
|
1122
|
-
selected_nodes = select_diagram_nodes(nodes, edges, max_nodes)
|
|
1123
|
-
selected_ids = {node.get("id") for node in selected_nodes}
|
|
1124
|
-
visible_edges = [
|
|
1125
|
-
edge for edge in preferred_edges(edges, allow_structure=False)
|
|
1126
|
-
if edge.get("source") in selected_ids and edge.get("target") in selected_ids
|
|
1127
|
-
]
|
|
1128
|
-
if not visible_edges:
|
|
1129
|
-
visible_edges = [
|
|
1130
|
-
edge for edge in preferred_edges(edges, allow_structure=True)
|
|
1131
|
-
if edge.get("source") in selected_ids and edge.get("target") in selected_ids
|
|
1132
|
-
]
|
|
1133
|
-
|
|
1134
|
-
groups = group_nodes_by_file(selected_nodes)
|
|
1135
|
-
class_lines = []
|
|
1136
|
-
for source_file, group in groups.items():
|
|
1137
|
-
group_id = node_mermaid_id({"id": f"{section_id}_{source_file}"})
|
|
1138
|
-
if len(groups) > 1 and len(group) > 1:
|
|
1139
|
-
lines.append(f' subgraph {group_id}["{safe_mermaid_text(source_file)}"]')
|
|
1140
|
-
indent = " "
|
|
1141
|
-
else:
|
|
1142
|
-
indent = " "
|
|
1143
|
-
for node in group:
|
|
1144
|
-
mid = node_mermaid_id(node)
|
|
1145
|
-
lines.append(f'{indent}{mid}("{node_label(node)}")')
|
|
1146
|
-
class_lines.append(f" class {mid} {node_kind(node)};")
|
|
1147
|
-
if len(groups) > 1 and len(group) > 1:
|
|
1148
|
-
lines.append(" end")
|
|
1149
|
-
|
|
1150
|
-
included = 0
|
|
1151
|
-
for edge in sorted(visible_edges, key=edge_score, reverse=True):
|
|
1152
|
-
if included >= max_edges:
|
|
1153
|
-
break
|
|
1154
|
-
src_id = node_mermaid_id({"id": edge.get("source", "")})
|
|
1155
|
-
tgt_id = node_mermaid_id({"id": edge.get("target", "")})
|
|
1156
|
-
rel = relation_label(edge.get("relation", ""), lang)
|
|
1157
|
-
lines.append(f" {src_id} -->|{rel}| {tgt_id}")
|
|
1158
|
-
included += 1
|
|
1159
|
-
|
|
1160
|
-
omitted_nodes = max(0, len(nodes) - len(selected_nodes))
|
|
1161
|
-
omitted_edges = max(0, len(visible_edges) - included)
|
|
1162
|
-
if omitted_nodes or omitted_edges:
|
|
1163
|
-
lines.append(f" %% Omitted for readability: {omitted_nodes} nodes, {omitted_edges} edges")
|
|
1164
|
-
lines.extend(class_lines)
|
|
1165
|
-
lines.extend(mermaid_class_defs())
|
|
1166
|
-
return "\n".join(lines)
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
# ──────────────────────────────────────────────
|
|
1170
|
-
# 7. HTML generators
|
|
1171
|
-
# ──────────────────────────────────────────────
|
|
1172
|
-
|
|
1173
|
-
def generate_nav(sections: list) -> str:
|
|
1174
|
-
"""Generate the sticky navigation bar."""
|
|
1175
|
-
links = []
|
|
1176
|
-
for sec in sections:
|
|
1177
|
-
links.append(f' <a href="#{escape(sec["id"], quote=True)}">{escape(sec["name"])}</a>')
|
|
1178
|
-
return '<div class="nav">\n' + "\n".join(links) + "\n</div>"
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
def node_display_name(node: dict | None, fallback: str = "") -> str:
|
|
1182
|
-
"""Readable node label for tables and summaries."""
|
|
1183
|
-
if not node:
|
|
1184
|
-
return str(fallback or "")
|
|
1185
|
-
label = str(node.get("label") or node.get("id") or fallback or "")
|
|
1186
|
-
return humanize_label(label, node.get("source_file", ""))
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
def format_node_refs(node_ids: set, node_by_id: dict, lang: str, empty_text: str, limit: int = 3) -> str:
|
|
1190
|
-
"""Render node references as readable labels instead of internal IDs."""
|
|
1191
|
-
if not node_ids:
|
|
1192
|
-
return escape(empty_text)
|
|
1193
|
-
parts = []
|
|
1194
|
-
for nid in sorted(node_ids, key=lambda item: node_display_name(node_by_id.get(item), item).lower())[:limit]:
|
|
1195
|
-
node = node_by_id.get(nid)
|
|
1196
|
-
label = node_display_name(node, nid)
|
|
1197
|
-
source = safe_file_path((node or {}).get("source_file", ""))
|
|
1198
|
-
if source:
|
|
1199
|
-
parts.append(f"<code>{escape(label)}</code><br><small style=\"color:var(--muted)\">{escape(source)}</small>")
|
|
1200
|
-
else:
|
|
1201
|
-
parts.append(f"<code>{escape(label)}</code>")
|
|
1202
|
-
if len(node_ids) > limit:
|
|
1203
|
-
parts.append(escape(pick_text(lang, f"+{len(node_ids) - limit} 个更多", f"+{len(node_ids) - limit} more")))
|
|
1204
|
-
return "<br>".join(parts)
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
def generate_call_table_rows(nodes: list, section_edges: list, lang: str) -> str:
|
|
1208
|
-
"""Generate call table row scaffolding for a section's nodes."""
|
|
1209
|
-
if not nodes:
|
|
1210
|
-
return ""
|
|
1211
|
-
|
|
1212
|
-
# Build source/target lookup from edges
|
|
1213
|
-
node_by_id = {n.get("id"): n for n in nodes}
|
|
1214
|
-
callers = defaultdict(set)
|
|
1215
|
-
callees = defaultdict(set)
|
|
1216
|
-
for e in section_edges:
|
|
1217
|
-
src = e.get("source", "")
|
|
1218
|
-
tgt = e.get("target", "")
|
|
1219
|
-
if e.get("relation") in ("calls", "imports", "imports_from", "uses", "method"):
|
|
1220
|
-
callers[tgt].add(src)
|
|
1221
|
-
callees[src].add(tgt)
|
|
1222
|
-
|
|
1223
|
-
rows = []
|
|
1224
|
-
for i, n in enumerate(nodes[:30], 1): # cap at 30 rows
|
|
1225
|
-
nid = n.get("id", "")
|
|
1226
|
-
label = n.get("label", nid)
|
|
1227
|
-
source_file = safe_file_path(n.get("source_file", ""))
|
|
1228
|
-
file_type = n.get("file_type", "code")
|
|
1229
|
-
|
|
1230
|
-
# Suggest a tag type based on file_type and label heuristics
|
|
1231
|
-
tag = _suggest_tag(label, file_type, lang, node_kind(n))
|
|
1232
|
-
|
|
1233
|
-
caller_text = format_node_refs(
|
|
1234
|
-
callers.get(nid, set()),
|
|
1235
|
-
node_by_id,
|
|
1236
|
-
lang,
|
|
1237
|
-
pick_text(lang, "外部入口 / 无直接入边", "External entry / no inbound edge"),
|
|
1238
|
-
)
|
|
1239
|
-
callee_text = format_node_refs(
|
|
1240
|
-
callees.get(nid, set()),
|
|
1241
|
-
node_by_id,
|
|
1242
|
-
lang,
|
|
1243
|
-
pick_text(lang, "无直接出边", "No direct outbound edge"),
|
|
1244
|
-
)
|
|
1245
|
-
|
|
1246
|
-
rows.append(f"""<tr>
|
|
1247
|
-
<td>{i}</td>
|
|
1248
|
-
<td><code>{escape(label)}</code><br><small style="color:var(--muted)">{escape(source_file)}</small></td>
|
|
1249
|
-
<td>{tag}</td>
|
|
1250
|
-
<td>{caller_text}</td>
|
|
1251
|
-
<td>{callee_text}</td>
|
|
1252
|
-
<td>{escape(_describe_node(label, source_file, file_type, lang))}</td>
|
|
1253
|
-
</tr>""")
|
|
1254
|
-
|
|
1255
|
-
return "\n".join(rows)
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
def _suggest_tag(label: str, file_type: str, lang: str, kind: str = "") -> str:
|
|
1259
|
-
"""Heuristic tag suggestion based on label name and file type."""
|
|
1260
|
-
lower = label.lower()
|
|
1261
|
-
names = {
|
|
1262
|
-
"concept": ("概念", "Concept", "tag-func"),
|
|
1263
|
-
"entry": ("入口", "Entry", "tag-cmd"),
|
|
1264
|
-
"api": ("API", "API", "tag-endpoint"),
|
|
1265
|
-
"async": ("异步", "Async", "tag-async"),
|
|
1266
|
-
"klass": ("类", "Class", "tag-class"),
|
|
1267
|
-
"ui": ("UI", "UI", "tag-hook"),
|
|
1268
|
-
"module": ("模块", "Module", "tag-class"),
|
|
1269
|
-
"test": ("测试", "Test", "tag-func"),
|
|
1270
|
-
"function": ("函数", "Function", "tag-func"),
|
|
1271
|
-
}
|
|
1272
|
-
if kind in names:
|
|
1273
|
-
zh, en, cls = names[kind]
|
|
1274
|
-
return f'<span class="tag {cls}">{pick_text(lang, zh, en)}</span>'
|
|
1275
|
-
if file_type == "rationale":
|
|
1276
|
-
return f'<span class="tag tag-func">{pick_text(lang, "概念", "Concept")}</span>'
|
|
1277
|
-
if any(kw in lower for kw in ("cli", "command", "scan", "serve", "chat", "config")):
|
|
1278
|
-
if "group" in lower or "command" in lower:
|
|
1279
|
-
return f'<span class="tag tag-cmd">{pick_text(lang, "CLI命令", "CLI")}</span>'
|
|
1280
|
-
if any(kw in lower for kw in ("router", "endpoint", "api", "/api/")):
|
|
1281
|
-
return f'<span class="tag tag-endpoint">{pick_text(lang, "API端点", "API")}</span>'
|
|
1282
|
-
if any(kw in lower for kw in ("async", "await", "stream")):
|
|
1283
|
-
return f'<span class="tag tag-async">{pick_text(lang, "异步", "Async")}</span>'
|
|
1284
|
-
if any(kw in lower for kw in ("class", "model", "schema", "dataclass", "pydantic")):
|
|
1285
|
-
return f'<span class="tag tag-class">{pick_text(lang, "类", "Class")}</span>'
|
|
1286
|
-
if any(kw in lower for kw in ("hook", "usestate", "useeffect", "store")):
|
|
1287
|
-
return '<span class="tag tag-hook">Hook</span>'
|
|
1288
|
-
if any(kw in lower for kw in ("component", "props", "tsx", "jsx", "render")):
|
|
1289
|
-
return f'<span class="tag tag-class">{pick_text(lang, "组件", "Component")}</span>'
|
|
1290
|
-
return f'<span class="tag tag-func">{pick_text(lang, "函数", "Function")}</span>'
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
def _describe_node(label: str, source_file: str, file_type: str, lang: str) -> str:
|
|
1294
|
-
"""Generate a compact human-readable description for a graph node."""
|
|
1295
|
-
lower = label.lower()
|
|
1296
|
-
source = source_file or pick_text(lang, "项目", "project")
|
|
1297
|
-
if file_type == "rationale":
|
|
1298
|
-
return pick_text(lang, f"设计说明:{label}", f"Design note for {label}.")
|
|
1299
|
-
if file_type == "document":
|
|
1300
|
-
return pick_text(lang, f"文档入口,描述 {label} 相关能力。", f"Documentation node describing {label}.")
|
|
1301
|
-
if label.endswith(".py") or label.endswith(".tsx") or label.endswith(".ts"):
|
|
1302
|
-
return pick_text(lang, f"{source} 中的模块文件,承载该层主要实现。", f"Module file in {source}.")
|
|
1303
|
-
if "config" in lower:
|
|
1304
|
-
return pick_text(lang, "读取、解析或持久化项目配置。", "Reads, resolves, or persists project configuration.")
|
|
1305
|
-
if "scan" in lower:
|
|
1306
|
-
return pick_text(lang, "触发项目扫描或处理扫描状态。", "Starts scanning or handles scan status.")
|
|
1307
|
-
if "ingest" in lower or "clone" in lower or "git" in lower:
|
|
1308
|
-
return pick_text(lang, "把本地目录或远程仓库转换为分析上下文。", "Turns a local path or remote repository into analysis context.")
|
|
1309
|
-
if "prompt" in lower:
|
|
1310
|
-
return pick_text(lang, "构造发送给 LLM 的结构化提示。", "Builds structured prompts for model calls.")
|
|
1311
|
-
if "analy" in lower:
|
|
1312
|
-
return pick_text(lang, "编排分析流程并产出结构化文档数据。", "Orchestrates analysis and returns structured documentation data.")
|
|
1313
|
-
if "graph" in lower or "dependency" in lower:
|
|
1314
|
-
return pick_text(lang, "构建依赖关系并提供排序或图形化数据。", "Builds dependency relationships and graph data.")
|
|
1315
|
-
if "export" in lower or "markdown" in lower or "html" in lower:
|
|
1316
|
-
return pick_text(lang, "将文档数据导出为目标格式。", "Exports documentation data to a target format.")
|
|
1317
|
-
if "chat" in lower or "rag" in lower or "retrieve" in lower:
|
|
1318
|
-
return pick_text(lang, "支撑检索增强问答或流式聊天。", "Supports retrieval-augmented Q&A or streaming chat.")
|
|
1319
|
-
if "wiki" in lower or "page" in lower or "sidebar" in lower:
|
|
1320
|
-
return pick_text(lang, "组织文档页面、侧边栏或内容读取。", "Organizes documentation pages, navigation, or content lookup.")
|
|
1321
|
-
if "cache" in lower or "hash" in lower:
|
|
1322
|
-
return pick_text(lang, "缓存分析结果或生成缓存键。", "Caches analysis results or computes cache keys.")
|
|
1323
|
-
if "test" in lower:
|
|
1324
|
-
return pick_text(lang, "验证导入、入口点或版本等基础行为。", "Verifies imports, entry points, or version behavior.")
|
|
1325
|
-
return pick_text(lang, f"{source} 中的 {label} 节点。", f"{label} node in {source}.")
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
def generate_header(sections: list, meta: dict, lang: str) -> str:
|
|
1329
|
-
"""Generate the HTML header, title, subtitle, and nav."""
|
|
1330
|
-
project_name = str(meta.get("project_name", "Project"))
|
|
1331
|
-
commit = str(meta.get("built_at_commit", "unknown"))[:7]
|
|
1332
|
-
|
|
1333
|
-
if lang.startswith("zh"):
|
|
1334
|
-
title = f"{project_name} — 完整调用流程与架构文档"
|
|
1335
|
-
subtitle = (
|
|
1336
|
-
f"由 graphify 知识图谱生成:{meta.get('node_count', '?')} 个节点、"
|
|
1337
|
-
f"{meta.get('edge_count', '?')} 条边、{meta.get('community_count', '?')} 个社区。"
|
|
1338
|
-
f"Commit: {commit}"
|
|
1339
|
-
)
|
|
1340
|
-
else:
|
|
1341
|
-
title = f"{project_name} — Complete Call Flow & Architecture Documentation"
|
|
1342
|
-
subtitle = (
|
|
1343
|
-
f"Generated from graphify knowledge graph: {meta.get('node_count', '?')} nodes, "
|
|
1344
|
-
f"{meta.get('edge_count', '?')} edges, {meta.get('community_count', '?')} communities. "
|
|
1345
|
-
f"Commit: {commit}"
|
|
1346
|
-
)
|
|
1347
|
-
|
|
1348
|
-
return f"""<h1>{escape(title)}</h1>
|
|
1349
|
-
<p class="subtitle">{escape(subtitle)}</p>
|
|
1350
|
-
|
|
1351
|
-
{generate_nav(sections)}
|
|
1352
|
-
"""
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
def derive_flow_chain(sections: list, classified_edges: dict) -> str:
|
|
1356
|
-
"""Derive a readable section flow from inter-section edges."""
|
|
1357
|
-
section_names = {sec["id"]: sec.get("name", sec["id"]) for sec in sections}
|
|
1358
|
-
order = [sec["id"] for sec in sections if sec["id"] != "overview"]
|
|
1359
|
-
if not order:
|
|
1360
|
-
return "Graph nodes -> documentation"
|
|
1361
|
-
|
|
1362
|
-
outgoing = defaultdict(Counter)
|
|
1363
|
-
incoming = Counter()
|
|
1364
|
-
for (src, tgt), data in section_edge_summary(classified_edges).items():
|
|
1365
|
-
outgoing[src][tgt] += data["count"]
|
|
1366
|
-
incoming[tgt] += data["count"]
|
|
1367
|
-
|
|
1368
|
-
start = min(order, key=lambda sid: (incoming.get(sid, 0), order.index(sid)))
|
|
1369
|
-
chain = [start]
|
|
1370
|
-
seen = {start}
|
|
1371
|
-
current = start
|
|
1372
|
-
while len(chain) < min(7, len(order)):
|
|
1373
|
-
candidates = [(count, tgt) for tgt, count in outgoing.get(current, {}).items() if tgt not in seen]
|
|
1374
|
-
if candidates:
|
|
1375
|
-
_, nxt = max(candidates)
|
|
1376
|
-
else:
|
|
1377
|
-
remaining = [sid for sid in order if sid not in seen]
|
|
1378
|
-
if not remaining:
|
|
1379
|
-
break
|
|
1380
|
-
nxt = remaining[0]
|
|
1381
|
-
chain.append(nxt)
|
|
1382
|
-
seen.add(nxt)
|
|
1383
|
-
current = nxt
|
|
1384
|
-
return " -> ".join(section_names.get(sid, sid) for sid in chain)
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
def generate_overview_cards(meta: dict, report_text: str, sections: list,
|
|
1388
|
-
section_nodes_map: dict, classified_edges: dict,
|
|
1389
|
-
lang: str) -> str:
|
|
1390
|
-
"""Generate generic overview cards."""
|
|
1391
|
-
rows = []
|
|
1392
|
-
for sec in sections:
|
|
1393
|
-
if sec["id"] == "overview":
|
|
1394
|
-
continue
|
|
1395
|
-
communities = ", ".join(str(c) for c in sec.get("communities", []))
|
|
1396
|
-
node_count = len(section_nodes_map.get(sec["id"], []))
|
|
1397
|
-
rows.append(
|
|
1398
|
-
f"<tr><td>{escape(sec['name'])}</td><td>{node_count}</td><td><code>{escape(communities)}</code></td></tr>"
|
|
1399
|
-
)
|
|
1400
|
-
|
|
1401
|
-
flow = derive_flow_chain(sections, classified_edges)
|
|
1402
|
-
layer_title = pick_text(lang, "架构层次", "Architecture Layers")
|
|
1403
|
-
layer_cols = pick_text(lang, "<tr><th>层</th><th>节点</th><th>社区</th></tr>", "<tr><th>Layer</th><th>Nodes</th><th>Communities</th></tr>")
|
|
1404
|
-
flow_title = pick_text(lang, "核心数据流", "Core Flow")
|
|
1405
|
-
return f"""<div class="grid">
|
|
1406
|
-
<div class="card">
|
|
1407
|
-
<h4>{layer_title}</h4>
|
|
1408
|
-
<table style="width:100%;font-size:0.85rem;">
|
|
1409
|
-
{layer_cols}
|
|
1410
|
-
{''.join(rows)}
|
|
1411
|
-
</table>
|
|
1412
|
-
</div>
|
|
1413
|
-
<div class="card">
|
|
1414
|
-
<h4>{flow_title}</h4>
|
|
1415
|
-
<div class="arrow-chain">{escape(flow)}</div>
|
|
1416
|
-
</div>
|
|
1417
|
-
</div>"""
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
def section_keywords(nodes: list, limit: int = 5) -> list:
|
|
1421
|
-
"""Pick representative words from labels and file names."""
|
|
1422
|
-
counts = Counter()
|
|
1423
|
-
stopwords = {
|
|
1424
|
-
"the", "and", "for", "with", "from", "this", "that", "class", "function",
|
|
1425
|
-
"method", "file", "src", "lib", "core", "index", "main", "init", "py",
|
|
1426
|
-
"ts", "tsx", "js", "jsx", "go", "rs", "java", "html", "css",
|
|
1427
|
-
}
|
|
1428
|
-
for node in nodes:
|
|
1429
|
-
text = f"{node.get('label', '')} {node.get('source_file', '')}".replace("/", " ").replace("_", " ").replace("-", " ")
|
|
1430
|
-
for raw in text.split():
|
|
1431
|
-
word = "".join(ch for ch in raw.lower() if ch.isalnum())
|
|
1432
|
-
if len(word) < 3 or word in stopwords:
|
|
1433
|
-
continue
|
|
1434
|
-
counts[word] += 1
|
|
1435
|
-
return [word for word, _ in counts.most_common(limit)]
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
def generate_section_intro(sec: dict, nodes: list, edge_count: int, lang: str) -> str:
|
|
1439
|
-
"""Generate the section introductory paragraph."""
|
|
1440
|
-
file_counts = Counter(n.get("source_file") for n in nodes if n.get("source_file"))
|
|
1441
|
-
files = [safe_file_path(path) for path, _ in file_counts.most_common(3)]
|
|
1442
|
-
keywords = section_keywords(nodes, 4)
|
|
1443
|
-
if is_zh(lang):
|
|
1444
|
-
file_text = "、".join(files) if files else "未标注源文件"
|
|
1445
|
-
keyword_text = "、".join(keywords) if keywords else sec.get("name", sec["id"])
|
|
1446
|
-
text = (
|
|
1447
|
-
f"{sec.get('name', sec['id'])} 汇集了与 {keyword_text} 相关的实现,"
|
|
1448
|
-
f"主要分布在 {file_text}。本节覆盖 {len(nodes)} 个节点、{edge_count} 条内部边,"
|
|
1449
|
-
"图中只展示最有代表性的调用关系以保持可读性。"
|
|
1450
|
-
)
|
|
1451
|
-
else:
|
|
1452
|
-
file_text = ", ".join(files) if files else "unmapped files"
|
|
1453
|
-
keyword_text = ", ".join(keywords) if keywords else sec.get("name", sec["id"])
|
|
1454
|
-
text = (
|
|
1455
|
-
f"{sec.get('name', sec['id'])} groups implementation around {keyword_text}, "
|
|
1456
|
-
f"mostly in {file_text}. This section covers {len(nodes)} nodes and {edge_count} internal edges; "
|
|
1457
|
-
"the diagram shows only representative relationships to stay readable."
|
|
1458
|
-
)
|
|
1459
|
-
return f"<p>{escape(text)}</p>"
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
def generate_section_cards(sec: dict, nodes: list, section_edges: list, lang: str) -> str:
|
|
1463
|
-
"""Generate key file and design-note cards for a section."""
|
|
1464
|
-
file_counts = defaultdict(int)
|
|
1465
|
-
for n in nodes:
|
|
1466
|
-
source_file = n.get("source_file") or ""
|
|
1467
|
-
if source_file:
|
|
1468
|
-
file_counts[source_file] += 1
|
|
1469
|
-
top_files = sorted(file_counts.items(), key=lambda item: (-item[1], item[0]))[:8]
|
|
1470
|
-
if top_files:
|
|
1471
|
-
file_rows = "\n".join(
|
|
1472
|
-
f"<tr><td><code>{escape(safe_file_path(path))}</code></td><td>{count} {escape(pick_text(lang, '个节点', 'nodes'))}</td></tr>"
|
|
1473
|
-
for path, count in top_files
|
|
1474
|
-
)
|
|
1475
|
-
else:
|
|
1476
|
-
file_rows = f'<tr><td colspan="2">{escape(pick_text(lang, "无源文件映射", "No source file mapping"))}</td></tr>'
|
|
1477
|
-
|
|
1478
|
-
relation_counts = Counter(edge.get("relation", "relates") for edge in section_edges if should_include_edge(edge))
|
|
1479
|
-
relation_text = ", ".join(f"{relation_label(rel, lang)} x{count}" for rel, count in relation_counts.most_common(4))
|
|
1480
|
-
if not relation_text:
|
|
1481
|
-
relation_text = pick_text(lang, "未检测到高置信调用边", "No high-confidence call edges detected")
|
|
1482
|
-
note = pick_text(
|
|
1483
|
-
lang,
|
|
1484
|
-
f"本节由 graphify 社区聚类生成。关系概况:{relation_text}。图表优先展示高置信、跨节点调用或使用关系,完整节点清单位于表格中。",
|
|
1485
|
-
f"This section comes from graphify community clustering. Relationship summary: {relation_text}. The diagram prioritizes high-confidence calls or usage relationships; the table keeps the broader node inventory.",
|
|
1486
|
-
)
|
|
1487
|
-
key_files = pick_text(lang, "关键文件", "Key Files")
|
|
1488
|
-
role = pick_text(lang, "覆盖节点", "Coverage")
|
|
1489
|
-
design_notes = pick_text(lang, "设计备注", "Design Notes")
|
|
1490
|
-
return f"""<div class="grid">
|
|
1491
|
-
<div class="card">
|
|
1492
|
-
<h4>{key_files}</h4>
|
|
1493
|
-
<table style="width:100%;font-size:0.85rem;">
|
|
1494
|
-
<tr><th>File</th><th>{role}</th></tr>
|
|
1495
|
-
{file_rows}
|
|
1496
|
-
</table>
|
|
1497
|
-
</div>
|
|
1498
|
-
<div class="card">
|
|
1499
|
-
<h4>{design_notes}</h4>
|
|
1500
|
-
<p>{escape(note)}</p>
|
|
1501
|
-
</div>
|
|
1502
|
-
</div>"""
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
# ──────────────────────────────────────────────
|
|
1506
|
-
# 8. Main entry point
|
|
1507
|
-
# ──────────────────────────────────────────────
|
|
1508
|
-
|
|
1509
|
-
class CallflowOptions:
|
|
1510
|
-
"""Options for call-flow architecture HTML generation."""
|
|
1511
|
-
|
|
1512
|
-
def __init__(
|
|
1513
|
-
self,
|
|
1514
|
-
project: str | Path | None = None,
|
|
1515
|
-
*,
|
|
1516
|
-
graphify_out: str | Path | None = None,
|
|
1517
|
-
graph: str | Path | None = None,
|
|
1518
|
-
report: str | Path | None = None,
|
|
1519
|
-
labels: str | Path | None = None,
|
|
1520
|
-
sections: str | Path | None = None,
|
|
1521
|
-
output: str | Path | None = None,
|
|
1522
|
-
lang: str = "auto",
|
|
1523
|
-
max_sections: int = 15,
|
|
1524
|
-
diagram_scale: float = 1.0,
|
|
1525
|
-
max_diagram_nodes: int = 18,
|
|
1526
|
-
max_diagram_edges: int = 24,
|
|
1527
|
-
):
|
|
1528
|
-
self.project = str(project) if project is not None else None
|
|
1529
|
-
self.graphify_out = str(graphify_out) if graphify_out is not None else None
|
|
1530
|
-
self.graph = str(graph) if graph is not None else None
|
|
1531
|
-
self.report = str(report) if report is not None else None
|
|
1532
|
-
self.labels = str(labels) if labels is not None else None
|
|
1533
|
-
self.sections = str(sections) if sections is not None else None
|
|
1534
|
-
self.output = str(output) if output is not None else None
|
|
1535
|
-
self.lang = lang
|
|
1536
|
-
self.max_sections = max_sections
|
|
1537
|
-
self.diagram_scale = diagram_scale
|
|
1538
|
-
self.max_diagram_nodes = max_diagram_nodes
|
|
1539
|
-
self.max_diagram_edges = max_diagram_edges
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
def _report_highlights(report_text: str, lang: str) -> str:
|
|
1543
|
-
"""Extract a compact highlights card from GRAPH_REPORT.md."""
|
|
1544
|
-
if not report_text.strip():
|
|
1545
|
-
return ""
|
|
1546
|
-
|
|
1547
|
-
lines = report_text.splitlines()
|
|
1548
|
-
keep: list[str] = []
|
|
1549
|
-
in_gods = False
|
|
1550
|
-
in_summary = False
|
|
1551
|
-
for line in lines:
|
|
1552
|
-
stripped = line.strip()
|
|
1553
|
-
if stripped.startswith("## "):
|
|
1554
|
-
in_summary = stripped == "## Summary"
|
|
1555
|
-
in_gods = stripped.startswith("## God Nodes")
|
|
1556
|
-
continue
|
|
1557
|
-
if in_summary and stripped.startswith("- "):
|
|
1558
|
-
keep.append(stripped[2:])
|
|
1559
|
-
elif in_gods and re.match(r"^\d+\.", stripped):
|
|
1560
|
-
keep.append(stripped)
|
|
1561
|
-
if len(keep) >= 6:
|
|
1562
|
-
break
|
|
1563
|
-
|
|
1564
|
-
if not keep:
|
|
1565
|
-
return ""
|
|
1566
|
-
|
|
1567
|
-
title = pick_text(lang, "图谱报告摘要", "Graph Report Highlights")
|
|
1568
|
-
items = "\n".join(f" <li>{escape(item)}</li>" for item in keep)
|
|
1569
|
-
return f"""<div class="card">
|
|
1570
|
-
<h4>{title}</h4>
|
|
1571
|
-
<ul>
|
|
1572
|
-
{items}
|
|
1573
|
-
</ul>
|
|
1574
|
-
</div>"""
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
def write_callflow_html(
|
|
1578
|
-
project: str | Path | None = None,
|
|
1579
|
-
*,
|
|
1580
|
-
graphify_out: str | Path | None = None,
|
|
1581
|
-
graph: str | Path | None = None,
|
|
1582
|
-
report: str | Path | None = None,
|
|
1583
|
-
labels: str | Path | None = None,
|
|
1584
|
-
sections: str | Path | None = None,
|
|
1585
|
-
output: str | Path | None = None,
|
|
1586
|
-
lang: str = "auto",
|
|
1587
|
-
max_sections: int = 15,
|
|
1588
|
-
diagram_scale: float = 1.0,
|
|
1589
|
-
max_diagram_nodes: int = 18,
|
|
1590
|
-
max_diagram_edges: int = 24,
|
|
1591
|
-
verbose: bool = False,
|
|
1592
|
-
) -> Path:
|
|
1593
|
-
"""Generate call-flow architecture HTML from graphify output files."""
|
|
1594
|
-
args = CallflowOptions(
|
|
1595
|
-
project,
|
|
1596
|
-
graphify_out=graphify_out,
|
|
1597
|
-
graph=graph,
|
|
1598
|
-
report=report,
|
|
1599
|
-
labels=labels,
|
|
1600
|
-
sections=sections,
|
|
1601
|
-
output=output,
|
|
1602
|
-
lang=lang,
|
|
1603
|
-
max_sections=max_sections,
|
|
1604
|
-
diagram_scale=diagram_scale,
|
|
1605
|
-
max_diagram_nodes=max_diagram_nodes,
|
|
1606
|
-
max_diagram_edges=max_diagram_edges,
|
|
1607
|
-
)
|
|
1608
|
-
|
|
1609
|
-
paths = resolve_graphify_paths(args)
|
|
1610
|
-
if not paths["graph"].exists():
|
|
1611
|
-
raise FileNotFoundError(
|
|
1612
|
-
f"graphify output not found: {paths['graph']}. "
|
|
1613
|
-
"Run graphify first or pass --graph /path/to/graph.json."
|
|
1614
|
-
)
|
|
1615
|
-
|
|
1616
|
-
# Load data
|
|
1617
|
-
nodes, edges, hyperedges, meta = load_graph(paths["graph"])
|
|
1618
|
-
labels = load_labels(paths["labels"])
|
|
1619
|
-
lang = detect_lang(args.lang, nodes, labels)
|
|
1620
|
-
if paths["sections"]:
|
|
1621
|
-
sections = load_sections(paths["sections"])
|
|
1622
|
-
else:
|
|
1623
|
-
sections = derive_sections_from_communities(nodes, labels, lang, args.max_sections)
|
|
1624
|
-
sections = normalize_sections(sections, lang)
|
|
1625
|
-
report_text = load_report(paths["report"])
|
|
1626
|
-
|
|
1627
|
-
if not nodes:
|
|
1628
|
-
raise ValueError("graph.json contains 0 nodes")
|
|
1629
|
-
if len(sections) <= 1:
|
|
1630
|
-
raise ValueError("no sections defined")
|
|
1631
|
-
|
|
1632
|
-
if verbose and len(nodes) >= 5000:
|
|
1633
|
-
print("WARNING: Large graph -- Mermaid rendering may be slow. Consider --max-sections 5.", file=sys.stderr)
|
|
1634
|
-
|
|
1635
|
-
node_ids = {node.get("id") for node in nodes}
|
|
1636
|
-
missing_endpoint_edges = [edge for edge in edges if edge.get("source") not in node_ids or edge.get("target") not in node_ids]
|
|
1637
|
-
if verbose and missing_endpoint_edges:
|
|
1638
|
-
print(f"WARNING: {len(missing_endpoint_edges)} edges reference nodes not present in graph.json.", file=sys.stderr)
|
|
1639
|
-
|
|
1640
|
-
meta["project_name"] = infer_project_name(str(paths["graph"]), meta)
|
|
1641
|
-
meta["node_count"] = len(nodes)
|
|
1642
|
-
meta["edge_count"] = len(edges)
|
|
1643
|
-
meta["hyperedge_count"] = len(hyperedges)
|
|
1644
|
-
|
|
1645
|
-
if args.output:
|
|
1646
|
-
output_path = Path(args.output).expanduser()
|
|
1647
|
-
if not output_path.is_absolute():
|
|
1648
|
-
output_path = paths["base"] / output_path
|
|
1649
|
-
else:
|
|
1650
|
-
output_path = paths["graphify_out"] / f"{safe_filename(meta['project_name'])}-callflow.html"
|
|
1651
|
-
|
|
1652
|
-
if verbose:
|
|
1653
|
-
print(f"Loaded: {len(nodes)} nodes, {len(edges)} edges, {len(sections)} sections")
|
|
1654
|
-
print(f"Graph: {paths['graph']}")
|
|
1655
|
-
|
|
1656
|
-
# Build index
|
|
1657
|
-
comm_idx = build_community_index(nodes)
|
|
1658
|
-
meta["community_count"] = len(comm_idx)
|
|
1659
|
-
section_nodes_map = build_section_node_map(sections, comm_idx)
|
|
1660
|
-
classified = classify_edges(edges, section_nodes_map)
|
|
1661
|
-
|
|
1662
|
-
# Build HTML
|
|
1663
|
-
html = []
|
|
1664
|
-
doc_title = (
|
|
1665
|
-
f"{meta.get('project_name', 'Project')} — 完整调用流程与架构文档"
|
|
1666
|
-
if lang.startswith("zh")
|
|
1667
|
-
else f"{meta.get('project_name', 'Project')} — Complete Call Flow & Architecture Documentation"
|
|
1668
|
-
)
|
|
1669
|
-
|
|
1670
|
-
# Doctype and head
|
|
1671
|
-
html.append(f"""<!DOCTYPE html>
|
|
1672
|
-
<html lang="{escape(lang, quote=True)}">
|
|
1673
|
-
<head>
|
|
1674
|
-
<meta charset="UTF-8">
|
|
1675
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1676
|
-
<title>{escape(doc_title)}</title>
|
|
1677
|
-
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
1678
|
-
<style>
|
|
1679
|
-
{CSS}
|
|
1680
|
-
</style>
|
|
1681
|
-
</head>
|
|
1682
|
-
<body>
|
|
1683
|
-
<div class="container">
|
|
1684
|
-
""")
|
|
1685
|
-
|
|
1686
|
-
# Header + nav
|
|
1687
|
-
html.append(generate_header(sections, meta, lang))
|
|
1688
|
-
|
|
1689
|
-
# ── Architecture Overview (Section "overview") ──
|
|
1690
|
-
overview_name = sections[0].get("name", "Architecture Overview") if sections else "Architecture Overview"
|
|
1691
|
-
html.append(f"""<!-- ====== Architecture Overview ====== -->
|
|
1692
|
-
<h2 id="overview">1. {escape(str(overview_name))}</h2>
|
|
1693
|
-
|
|
1694
|
-
<div class="mermaid">
|
|
1695
|
-
""")
|
|
1696
|
-
html.append(generate_overview_graph(sections, section_nodes_map, classified, labels, lang, args.diagram_scale))
|
|
1697
|
-
html.append("""</div>
|
|
1698
|
-
""")
|
|
1699
|
-
html.append(generate_overview_cards(meta, report_text, sections, section_nodes_map, classified, lang))
|
|
1700
|
-
report_card = _report_highlights(report_text, lang)
|
|
1701
|
-
if report_card:
|
|
1702
|
-
html.append(f'<div class="grid">\n {report_card}\n</div>')
|
|
1703
|
-
html.append("<hr>")
|
|
1704
|
-
|
|
1705
|
-
# ── Per-section content ──
|
|
1706
|
-
section_num = 1 # overview was #1
|
|
1707
|
-
for sec in sections:
|
|
1708
|
-
if sec["id"] == "overview":
|
|
1709
|
-
continue
|
|
1710
|
-
section_num += 1
|
|
1711
|
-
sid = sec["id"]
|
|
1712
|
-
name = sec.get("name", sid)
|
|
1713
|
-
sec_nodes = section_nodes_map.get(sid, [])
|
|
1714
|
-
sec_edges = classified.get("intra", {}).get(sid, [])
|
|
1715
|
-
|
|
1716
|
-
edge_count = len(sec_edges)
|
|
1717
|
-
h3_title = pick_text(lang, "调用明细", "Call Details")
|
|
1718
|
-
number_header = "#"
|
|
1719
|
-
function_header = pick_text(lang, "节点", "Node")
|
|
1720
|
-
type_header = pick_text(lang, "类型", "Type")
|
|
1721
|
-
caller_header = pick_text(lang, "调用方", "Caller")
|
|
1722
|
-
callee_header = pick_text(lang, "被调用/依赖", "Callees")
|
|
1723
|
-
desc_header = pick_text(lang, "说明", "Description")
|
|
1724
|
-
|
|
1725
|
-
html.append(f"""<!-- ====== {section_num}. {html_comment_text(name)} ====== -->
|
|
1726
|
-
<h2 id="{escape(str(sid), quote=True)}">{section_num}. {escape(str(name))}</h2>
|
|
1727
|
-
{generate_section_intro(sec, sec_nodes, edge_count, lang)}
|
|
1728
|
-
|
|
1729
|
-
<div class="mermaid">
|
|
1730
|
-
{generate_section_flowchart(sid, name, sec_nodes, sec_edges, lang, args.diagram_scale, args.max_diagram_nodes, args.max_diagram_edges)}
|
|
1731
|
-
</div>
|
|
1732
|
-
|
|
1733
|
-
<h3>{h3_title}</h3>
|
|
1734
|
-
<table class="call-table">
|
|
1735
|
-
<tr>
|
|
1736
|
-
<th style="width:5%">{number_header}</th>
|
|
1737
|
-
<th style="width:28%">{function_header}</th>
|
|
1738
|
-
<th style="width:10%">{type_header}</th>
|
|
1739
|
-
<th style="width:17%">{caller_header}</th>
|
|
1740
|
-
<th style="width:20%">{callee_header}</th>
|
|
1741
|
-
<th style="width:20%">{desc_header}</th>
|
|
1742
|
-
</tr>
|
|
1743
|
-
{generate_call_table_rows(sec_nodes, sec_edges, lang)}
|
|
1744
|
-
</table>
|
|
1745
|
-
|
|
1746
|
-
{generate_section_cards(sec, sec_nodes, sec_edges, lang)}
|
|
1747
|
-
<hr>
|
|
1748
|
-
""")
|
|
1749
|
-
|
|
1750
|
-
# ── Section: Hyperedges (if any) ──
|
|
1751
|
-
if hyperedges:
|
|
1752
|
-
html.append("""<h2 id="hyperedges">Group Relationships (Hyperedges)</h2>
|
|
1753
|
-
<div class="grid">
|
|
1754
|
-
""")
|
|
1755
|
-
for he in hyperedges[:9]:
|
|
1756
|
-
hid = he.get("id", "?")
|
|
1757
|
-
hlabel = he.get("label", hid)
|
|
1758
|
-
hnodes = he.get("nodes", [])
|
|
1759
|
-
hrel = he.get("relation", "")
|
|
1760
|
-
html.append(f""" <div class="card">
|
|
1761
|
-
<h4>{escape(str(hlabel))}</h4>
|
|
1762
|
-
<p><code>{escape(str(hrel))}</code> — {len(hnodes)} participants</p>
|
|
1763
|
-
<ul>""")
|
|
1764
|
-
for hn in hnodes[:5]:
|
|
1765
|
-
html.append(f" <li><code>{escape(str(hn))}</code></li>")
|
|
1766
|
-
if len(hnodes) > 5:
|
|
1767
|
-
html.append(f" <li>... and {len(hnodes) - 5} more</li>")
|
|
1768
|
-
html.append(" </ul>\n </div>")
|
|
1769
|
-
html.append("</div>\n<hr>")
|
|
1770
|
-
|
|
1771
|
-
# ── Section: Statistics ──
|
|
1772
|
-
total_sections = sum(1 for s in sections if s["id"] != "overview")
|
|
1773
|
-
html.append(f"""<h2 id="stats">Project Statistics</h2>
|
|
1774
|
-
|
|
1775
|
-
<div class="grid">
|
|
1776
|
-
<div class="card">
|
|
1777
|
-
<h4>Graph</h4>
|
|
1778
|
-
<table style="width:100%;font-size:0.85rem;">
|
|
1779
|
-
<tr><td>Nodes</td><td>{len(nodes)}</td></tr>
|
|
1780
|
-
<tr><td>Edges</td><td>{len(edges)}</td></tr>
|
|
1781
|
-
<tr><td>Hyperedges</td><td>{len(hyperedges)}</td></tr>
|
|
1782
|
-
<tr><td>Communities</td><td>{len(comm_idx)}</td></tr>
|
|
1783
|
-
<tr><td>Documented Sections</td><td>{total_sections}</td></tr>
|
|
1784
|
-
</table>
|
|
1785
|
-
</div>
|
|
1786
|
-
<div class="card">
|
|
1787
|
-
<h4>Edge Confidence</h4>
|
|
1788
|
-
<table style="width:100%;font-size:0.85rem;">
|
|
1789
|
-
<tr><td>EXTRACTED</td><td>{sum(1 for e in edges if e.get('confidence') == 'EXTRACTED')}</td></tr>
|
|
1790
|
-
<tr><td>INFERRED</td><td>{sum(1 for e in edges if e.get('confidence') == 'INFERRED')}</td></tr>
|
|
1791
|
-
<tr><td>AMBIGUOUS</td><td>{sum(1 for e in edges if e.get('confidence') == 'AMBIGUOUS')}</td></tr>
|
|
1792
|
-
</table>
|
|
1793
|
-
</div>
|
|
1794
|
-
</div>
|
|
1795
|
-
""")
|
|
1796
|
-
|
|
1797
|
-
# ── Footer ──
|
|
1798
|
-
html.append(f"""<div style="text-align:center; padding:40px 0; color: var(--muted); font-size:0.9rem;">
|
|
1799
|
-
<p>{escape(str(meta.get('project_name', 'Project')))} — Architecture Documentation</p>
|
|
1800
|
-
<p>Generated: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')} · graphify callflow-html</p>
|
|
1801
|
-
</div>
|
|
1802
|
-
""")
|
|
1803
|
-
|
|
1804
|
-
# Close
|
|
1805
|
-
html.append("""</div><!-- .container -->
|
|
1806
|
-
|
|
1807
|
-
<script>
|
|
1808
|
-
(function () {
|
|
1809
|
-
const mermaidConfig = {
|
|
1810
|
-
startOnLoad: false,
|
|
1811
|
-
theme: 'dark',
|
|
1812
|
-
securityLevel: 'loose',
|
|
1813
|
-
flowchart: { htmlLabels: true, useMaxWidth: true },
|
|
1814
|
-
themeVariables: {
|
|
1815
|
-
primaryColor: '#1e293b',
|
|
1816
|
-
primaryTextColor: '#e2e8f0',
|
|
1817
|
-
primaryBorderColor: '#38bdf8',
|
|
1818
|
-
secondaryColor: '#0f172a',
|
|
1819
|
-
tertiaryColor: '#334155',
|
|
1820
|
-
lineColor: '#64748b',
|
|
1821
|
-
textColor: '#e2e8f0',
|
|
1822
|
-
}
|
|
1823
|
-
};
|
|
1824
|
-
|
|
1825
|
-
mermaid.initialize(mermaidConfig);
|
|
1826
|
-
|
|
1827
|
-
function clamp(value, min, max) {
|
|
1828
|
-
return Math.min(max, Math.max(min, value));
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
function enhanceMermaidDiagrams() {
|
|
1832
|
-
document.querySelectorAll('.mermaid').forEach((container) => {
|
|
1833
|
-
if (container.dataset.zoomReady === 'true') return;
|
|
1834
|
-
const svg = container.querySelector('svg');
|
|
1835
|
-
if (!svg) return;
|
|
1836
|
-
|
|
1837
|
-
container.dataset.zoomReady = 'true';
|
|
1838
|
-
container.classList.add('is-enhanced');
|
|
1839
|
-
|
|
1840
|
-
const viewport = document.createElement('div');
|
|
1841
|
-
viewport.className = 'mermaid-viewport';
|
|
1842
|
-
svg.parentNode.insertBefore(viewport, svg);
|
|
1843
|
-
viewport.appendChild(svg);
|
|
1844
|
-
|
|
1845
|
-
const toolbar = document.createElement('div');
|
|
1846
|
-
toolbar.className = 'mermaid-toolbar';
|
|
1847
|
-
toolbar.innerHTML = [
|
|
1848
|
-
'<button type="button" data-action="zoom-out" title="Zoom out">-</button>',
|
|
1849
|
-
'<span class="zoom-level" data-role="level">100%</span>',
|
|
1850
|
-
'<button type="button" data-action="zoom-in" title="Zoom in">+</button>',
|
|
1851
|
-
'<button type="button" data-action="fit" title="Fit width">Fit</button>',
|
|
1852
|
-
'<button type="button" data-action="reset" title="Reset view">Reset</button>'
|
|
1853
|
-
].join('');
|
|
1854
|
-
container.insertBefore(toolbar, viewport);
|
|
1855
|
-
|
|
1856
|
-
const state = { scale: 1, x: 0, y: 0, dragging: false, startX: 0, startY: 0, originX: 0, originY: 0 };
|
|
1857
|
-
const level = toolbar.querySelector('[data-role="level"]');
|
|
1858
|
-
|
|
1859
|
-
function applyTransform() {
|
|
1860
|
-
svg.style.transform = `translate(${state.x}px, ${state.y}px) scale(${state.scale})`;
|
|
1861
|
-
level.textContent = `${Math.round(state.scale * 100)}%`;
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
function zoomBy(delta) {
|
|
1865
|
-
state.scale = clamp(state.scale + delta, 0.25, 3);
|
|
1866
|
-
applyTransform();
|
|
1867
|
-
}
|
|
1868
|
-
|
|
1869
|
-
function reset() {
|
|
1870
|
-
state.scale = 1;
|
|
1871
|
-
state.x = 0;
|
|
1872
|
-
state.y = 0;
|
|
1873
|
-
applyTransform();
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
function fitWidth() {
|
|
1877
|
-
const rawWidth = svg.viewBox && svg.viewBox.baseVal && svg.viewBox.baseVal.width
|
|
1878
|
-
? svg.viewBox.baseVal.width
|
|
1879
|
-
: svg.getBoundingClientRect().width / state.scale;
|
|
1880
|
-
if (!rawWidth) {
|
|
1881
|
-
reset();
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
state.scale = clamp((viewport.clientWidth - 48) / rawWidth, 0.25, 1.4);
|
|
1885
|
-
state.x = 0;
|
|
1886
|
-
state.y = 0;
|
|
1887
|
-
applyTransform();
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
toolbar.addEventListener('click', (event) => {
|
|
1891
|
-
const button = event.target.closest('button[data-action]');
|
|
1892
|
-
if (!button) return;
|
|
1893
|
-
const action = button.dataset.action;
|
|
1894
|
-
if (action === 'zoom-in') zoomBy(0.15);
|
|
1895
|
-
if (action === 'zoom-out') zoomBy(-0.15);
|
|
1896
|
-
if (action === 'fit') fitWidth();
|
|
1897
|
-
if (action === 'reset') reset();
|
|
1898
|
-
});
|
|
1899
|
-
|
|
1900
|
-
viewport.addEventListener('wheel', (event) => {
|
|
1901
|
-
if (!event.ctrlKey && !event.metaKey) return;
|
|
1902
|
-
event.preventDefault();
|
|
1903
|
-
zoomBy(event.deltaY < 0 ? 0.1 : -0.1);
|
|
1904
|
-
}, { passive: false });
|
|
1905
|
-
|
|
1906
|
-
viewport.addEventListener('pointerdown', (event) => {
|
|
1907
|
-
if (event.button !== 0) return;
|
|
1908
|
-
state.dragging = true;
|
|
1909
|
-
state.startX = event.clientX;
|
|
1910
|
-
state.startY = event.clientY;
|
|
1911
|
-
state.originX = state.x;
|
|
1912
|
-
state.originY = state.y;
|
|
1913
|
-
viewport.classList.add('is-dragging');
|
|
1914
|
-
viewport.setPointerCapture(event.pointerId);
|
|
1915
|
-
});
|
|
1916
|
-
|
|
1917
|
-
viewport.addEventListener('pointermove', (event) => {
|
|
1918
|
-
if (!state.dragging) return;
|
|
1919
|
-
state.x = state.originX + event.clientX - state.startX;
|
|
1920
|
-
state.y = state.originY + event.clientY - state.startY;
|
|
1921
|
-
applyTransform();
|
|
1922
|
-
});
|
|
1923
|
-
|
|
1924
|
-
function endDrag(event) {
|
|
1925
|
-
if (!state.dragging) return;
|
|
1926
|
-
state.dragging = false;
|
|
1927
|
-
viewport.classList.remove('is-dragging');
|
|
1928
|
-
if (viewport.hasPointerCapture(event.pointerId)) {
|
|
1929
|
-
viewport.releasePointerCapture(event.pointerId);
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
viewport.addEventListener('pointerup', endDrag);
|
|
1934
|
-
viewport.addEventListener('pointercancel', endDrag);
|
|
1935
|
-
applyTransform();
|
|
1936
|
-
});
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
function renderMermaid() {
|
|
1940
|
-
const result = mermaid.run
|
|
1941
|
-
? mermaid.run({ querySelector: '.mermaid' })
|
|
1942
|
-
: Promise.resolve();
|
|
1943
|
-
Promise.resolve(result)
|
|
1944
|
-
.then(enhanceMermaidDiagrams)
|
|
1945
|
-
.catch((error) => {
|
|
1946
|
-
console.error('Mermaid render failed:', error);
|
|
1947
|
-
enhanceMermaidDiagrams();
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
if (document.readyState === 'loading') {
|
|
1952
|
-
document.addEventListener('DOMContentLoaded', renderMermaid);
|
|
1953
|
-
} else {
|
|
1954
|
-
renderMermaid();
|
|
1955
|
-
}
|
|
1956
|
-
})();
|
|
1957
|
-
</script>
|
|
1958
|
-
|
|
1959
|
-
</body>
|
|
1960
|
-
</html>""")
|
|
1961
|
-
|
|
1962
|
-
# Write output
|
|
1963
|
-
output = "\n".join(html)
|
|
1964
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1965
|
-
output_path.write_text(output, encoding="utf-8")
|
|
1966
|
-
|
|
1967
|
-
# Summary
|
|
1968
|
-
mermaid_count = output.count('<div class="mermaid">')
|
|
1969
|
-
table_count = output.count('<table class="call-table">')
|
|
1970
|
-
section_count = output.count('<h2 id=')
|
|
1971
|
-
|
|
1972
|
-
if verbose:
|
|
1973
|
-
print(f"Call-flow HTML written: {output_path}")
|
|
1974
|
-
print(f" Sections: {section_count} | Mermaid diagrams: {mermaid_count} | Call tables: {table_count}")
|
|
1975
|
-
print(" Diagrams use Mermaid init directives plus interactive zoom/pan controls.")
|
|
1976
|
-
|
|
1977
|
-
return output_path
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
def main():
|
|
1981
|
-
parser = argparse.ArgumentParser(
|
|
1982
|
-
description="Generate call-flow architecture HTML from graphify knowledge graph outputs"
|
|
1983
|
-
)
|
|
1984
|
-
parser.add_argument("project", nargs="?", default=None, help="Project root or graphify output directory")
|
|
1985
|
-
parser.add_argument("--graphify-out", default=None, help="Path to graphify output directory")
|
|
1986
|
-
parser.add_argument("--graph", default=None, help="Path to graph.json")
|
|
1987
|
-
parser.add_argument("--report", default=None, help="Path to GRAPH_REPORT.md")
|
|
1988
|
-
parser.add_argument("--labels", default=None, help="Path to .graphify_labels.json")
|
|
1989
|
-
parser.add_argument("--sections", default=None, help="Path to sections JSON file; auto-derived when omitted")
|
|
1990
|
-
parser.add_argument("--output", default=None, help="Output HTML path")
|
|
1991
|
-
parser.add_argument("--lang", default="auto", help="HTML language: auto, zh-CN, en, etc. (default: auto)")
|
|
1992
|
-
parser.add_argument("--max-sections", type=int, default=15, help="Maximum auto-derived sections, excluding overview")
|
|
1993
|
-
parser.add_argument("--diagram-scale", type=float, default=1.0, help="Mermaid-native diagram scale via init directive (0.65-1.8)")
|
|
1994
|
-
parser.add_argument("--max-diagram-nodes", type=int, default=18, help="Maximum representative nodes per section diagram")
|
|
1995
|
-
parser.add_argument("--max-diagram-edges", type=int, default=24, help="Maximum representative edges per section diagram")
|
|
1996
|
-
args = parser.parse_args()
|
|
1997
|
-
|
|
1998
|
-
try:
|
|
1999
|
-
write_callflow_html(
|
|
2000
|
-
args.project,
|
|
2001
|
-
graphify_out=args.graphify_out,
|
|
2002
|
-
graph=args.graph,
|
|
2003
|
-
report=args.report,
|
|
2004
|
-
labels=args.labels,
|
|
2005
|
-
sections=args.sections,
|
|
2006
|
-
output=args.output,
|
|
2007
|
-
lang=args.lang,
|
|
2008
|
-
max_sections=args.max_sections,
|
|
2009
|
-
diagram_scale=args.diagram_scale,
|
|
2010
|
-
max_diagram_nodes=args.max_diagram_nodes,
|
|
2011
|
-
max_diagram_edges=args.max_diagram_edges,
|
|
2012
|
-
verbose=True,
|
|
2013
|
-
)
|
|
2014
|
-
except (FileNotFoundError, ValueError, SystemExit) as exc:
|
|
2015
|
-
print(f"ERROR: {exc}", file=sys.stderr)
|
|
2016
|
-
sys.exit(1)
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
if __name__ == "__main__":
|
|
2020
|
-
main()
|