From e8437a9f0a92f3c2aaabe4a9d726f966f45b244c Mon Sep 17 00:00:00 2001 From: Nicholai Date: Wed, 25 Feb 2026 16:01:49 -0700 Subject: [PATCH] 2026-02-25T23-01-49_auto_memory/memories.db-wal --- .skill-lock.json | 27 + memory/memories.db-shm | Bin 32768 -> 32768 bytes memory/memories.db-wal | Bin 5285992 -> 5285992 bytes skills/codebase-cleanup-tech-debt/SKILL.md | 388 ++++++++++ skills/excalidraw-diagram-generator/SKILL.md | 613 ++++++++++++++++ .../references/element-types.md | 497 +++++++++++++ .../references/excalidraw-schema.md | 350 +++++++++ .../scripts/.gitignore | 34 + .../scripts/README.md | 193 +++++ .../scripts/add-arrow.py | 312 +++++++++ .../scripts/add-icon-to-diagram.py | 404 +++++++++++ .../scripts/split-excalidraw-library.py | 183 +++++ ...business-flow-swimlane-template.excalidraw | 334 +++++++++ .../class-diagram-template.excalidraw | 558 +++++++++++++++ .../data-flow-diagram-template.excalidraw | 279 ++++++++ .../templates/er-diagram-template.excalidraw | 662 ++++++++++++++++++ .../templates/flowchart-template.excalidraw | 179 +++++ .../templates/mindmap-template.excalidraw | 244 +++++++ .../relationship-template.excalidraw | 145 ++++ .../sequence-diagram-template.excalidraw | 509 ++++++++++++++ skills/monorepo-management/SKILL.md | 623 ++++++++++++++++ 21 files changed, 6534 insertions(+) create mode 100644 skills/codebase-cleanup-tech-debt/SKILL.md create mode 100644 skills/excalidraw-diagram-generator/SKILL.md create mode 100644 skills/excalidraw-diagram-generator/references/element-types.md create mode 100644 skills/excalidraw-diagram-generator/references/excalidraw-schema.md create mode 100644 skills/excalidraw-diagram-generator/scripts/.gitignore create mode 100644 skills/excalidraw-diagram-generator/scripts/README.md create mode 100644 skills/excalidraw-diagram-generator/scripts/add-arrow.py create mode 100644 skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py create mode 100644 skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py create mode 100644 skills/excalidraw-diagram-generator/templates/business-flow-swimlane-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/class-diagram-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/data-flow-diagram-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/er-diagram-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/flowchart-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/mindmap-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/relationship-template.excalidraw create mode 100644 skills/excalidraw-diagram-generator/templates/sequence-diagram-template.excalidraw create mode 100644 skills/monorepo-management/SKILL.md diff --git a/.skill-lock.json b/.skill-lock.json index a41be007c..02f714f9a 100644 --- a/.skill-lock.json +++ b/.skill-lock.json @@ -180,6 +180,33 @@ "skillFolderHash": "56f6e63863f927130b7f8db7b87b4f2861f3b108", "installedAt": "2026-02-25T18:33:45.457Z", "updatedAt": "2026-02-25T18:33:45.457Z" + }, + "codebase-cleanup-tech-debt": { + "source": "sickn33/antigravity-awesome-skills", + "sourceType": "github", + "sourceUrl": "https://github.com/sickn33/antigravity-awesome-skills.git", + "skillPath": "skills/codebase-cleanup-tech-debt/SKILL.md", + "skillFolderHash": "5b096fb4e7228df1496d54ad854cb15ba6fc0d0f", + "installedAt": "2026-02-25T22:58:27.563Z", + "updatedAt": "2026-02-25T22:58:27.563Z" + }, + "monorepo-management": { + "source": "wshobson/agents", + "sourceType": "github", + "sourceUrl": "https://github.com/wshobson/agents.git", + "skillPath": "plugins/developer-essentials/skills/monorepo-management/SKILL.md", + "skillFolderHash": "764048e2d04accd13a0b5864c83474f1744105e6", + "installedAt": "2026-02-25T22:59:19.478Z", + "updatedAt": "2026-02-25T22:59:19.478Z" + }, + "excalidraw-diagram-generator": { + "source": "github/awesome-copilot", + "sourceType": "github", + "sourceUrl": "https://github.com/github/awesome-copilot.git", + "skillPath": "skills/excalidraw-diagram-generator/SKILL.md", + "skillFolderHash": "ba20eb1474ed8c2123f2a98e36f822b9bf369834", + "installedAt": "2026-02-25T23:00:56.183Z", + "updatedAt": "2026-02-25T23:00:56.183Z" } }, "dismissed": { diff --git a/memory/memories.db-shm b/memory/memories.db-shm index 29cc497f41e5effb82db191c322119df9a9e57f9..32c7de3547655248893ae1c7fdd4bba69fd8c408 100644 GIT binary patch delta 579 zcmb7;O-Phc6o&74Kbsooa~iXvh7c){(nN`m0Ud*88iXWbT$Cn5(T|uG_){Rr@cDs) z3(3uG9W83p&%#bC!H5>M2&6@Xw1@~o-I&_HT6WIVqHPD>%X8lQ-g8e7AP6wL>wT@! z@&qU(8W~{!$^=lX+{b?JEYW&l@@RjK=!@X);v53p#&g6pRL|bke26yWrq@;s om1C>A6Ff5{`S0SkMKZX6G2FupK1pB3xAGSWg@r1(%6~(@0jy-di2wiq delta 463 zcmZo@U}|V!s+V}A%K!qkK+MR%An=xnfk9uJfx-K~!?P2$Y>eg4`JVo6?*27}af8L0 zk8XLSss|emBtYi=M*>jsjrDuEH(%zFH<|o^XZq$(4q8l;KRIe`c5~(fG1dS@T{gS8 ztpRa@HamG{F#{PQn-BPyFajA1Hc#<014+qEei6U}qPazwyqGeXdYLvdU77sRQFXIZ z;4Y9ByUkXiQ6MdKlZC=x0BNz!0ugl}0kz3nBISUz)n=Zk29Q~1n-9etV**+nvH3x~ z8AyrDW}`%~eJ-0Ws`4YSWo`!=sGzfb01J#Y_d+) zCLj&+fY0P>*(N}C$>zG8K9KIB&2{-VK)Urdw-m;J7*gypObtv+n2rP8Y`oc|xP}cV sSTI?k3gog0o6l8&RYh>RFeNj!Gp%Mi2UI7zc|lDINVC#rwYpo30DnWkWB>pF diff --git a/memory/memories.db-wal b/memory/memories.db-wal index 783b782e8fc85706f96ea1e66a22fbd6a1990dbd..9608a333df7a1b393daf27d9e887152fbbda2e04 100644 GIT binary patch delta 1878 zcmZ9M3s4hB7{_lfmvDL9C1?_!t%!Fyh#@9|p$37V^+81x89N1Oi*2nHit=bj>j;g_ zYEj1esB(@d4*^B06)SbA7Db^JDvmh#V3bkRiZ8Uzv??P$y1{XVusgq*z1{D={r>xX zyLUQHl6Npp2p|b1kb+VugI4u4-!^S-`%VXq`^uKanxk?TcYZde47I~<=MqZ{^uE?Mp6m6azX$~3-y~IZ` z+!942LCGkf0xD8W>NDxYtUlAn3YPqA@!%|jdSf{n0KNE+gY%L3Sia6%NFh}mH%Y49 z@TBd(eTSN=7%Z4n*GSY))HNt=8n;~?v5t*bP{b;|``~1n)$4o-ISGIi02u(-n;ZZo zzesTrYL zk>j2_*xvSlkDe|~??KsK{C?WgZ5|q{;pchL%?W{154DfhplpR;9d^dK=l~;}Q_)$o z_a!Bt?W#oO0{`JKMY%{B%2%^=%K3HIM%Wd#sN5)MH2RwpysP_zA737<2W2zCG$@

?pJQlQl*n%t}zz3 zd+K60q3&uS50GA$P|_aZ0>A|(eJNUXVb$n|8%8T2DFZrj(>T|?Kz4|Z3~q^J71tof zW^lEjzPQTl0(IirQsF3>u}_SvHZMhNF+ZN9fuy;wjqlK+^q+(hiAPlhphqSAG|x&4 zK=Tg@ItAUxFQ2_G>gfoSUgK7I#(3c7so>;NwDB}n$t+1cC`Eh{F}8`yJfBC|`Cp!kX|krktwR=z;yp*#mruB3M7 zYwZ%(oL14I3)}6MJ`PadCAK(Ui^r0i1|_3cmF9Y&n>aM9Ipurvzf4(n1NHoB)@ZZ? zTi0_X1;=aMQ2@0cq4r}YqfIu-#!#-=T~0J^J>LmL?+fO8x~QRUxR$3U4Uykz#f`1} zSkR4rZ{;tkb(4mgw4F_H$l2BYsQ(XhfEsj*na_k?gYnT&8!o_F64KxQ!aavuJ$_uH z2_m%ekn>zKiJw}+2oB#hk7Y!qDTJYy=4$u+yw4RiCO?-M{|5 zhuHnIIPwf>KZkyx=FO1&!b@1PFnGR?rC} zgdibU2oatW^g^g$5S|xC3PvGJ2p1xRNMV#<63jxB5G{-rVot`+2n;kw6GT+(8$T+q z9N9cEr}seS!il=u!L@G2AwwP8tey084gz0K7W~k8JZAOQwuKzNmrwK&u*XIf!ikUM zK@byQWMN;EK0u_R=9Dd&wFLN7bq;J!QJW#qLA?z*NosSMc#gTuzT6CIClwCKS|d|- z?{5!bb)x!hxN8)Lqf0I-iYu#F5eE_D)Fw1Dg))+X?3m#(c*l<4r@4Ex74C#iI&^h3n>4J*2&Sb2*L>i z{bVXcG4^n@r_1&&tv5@C?-Jq%ha-L{y);;@@kBWR4}a3S;oCg50a^z12GHlLd9gyy zHAp!Wlds-LLf$RXi6-9Do?xJ{U+aQ`KCJ_8Ue!Bc??de($Qrbo^NmjQ{e68J2{TDM zesWdoL>nLIuae-avxURut9n1!eFd*vd`F7|BXuuP0Z+;NX z)^a8|bj0w;^Zx0x-|LMraq_vuG)QmZ7Q%@ZE=@ki7C+kls;h8xyxR`5?o!bxx|LfY zBXeNoQA;%B7(-2vB3O*b(7{ECMhsQG#G6j^bDaE16}W6p2O&M~Y&hR$@lbL*I@04l zD}|W!P&-^sBNsuwn=FEoTkbB@c+t`>8aZLgg|?>GKAZbw2jUCz5amx7vh$^0PRHGn zUxZ`VEI!!N>&}2P;iMZ{u3IW#!*-PmZrVr(98;4nlxHVHC|IG!6|QoU?IJKt8RLS` zK5ic@E>Srl_Ge2e$W5dlBxHm-VP+y(3d-AVKUfuqU-|y7WiJT>SKU0kTrG+~D{pZ3 zpDF8%yOp_Xv$H*@rHKnJ#T~A^?Vf>0m=sc4Vqd^&@k|k1~*g# z-$kq1a67LIb68QwX}(aQ9+GZ`$+gr}AAN)%6$2TUi6I#*llk`&YT!-GthOSfbcXfmEiE3Ol- z7H5hBNwM^G_5$V4bc+X=Bjh1wnKFp-F&>6ht_8znSuxZ$vSE-M$g82HSCs)9P=FH> z`&5fy5@MZjxeqHkst#Kdu3_tFE$c*=r^=>Kut^(I3?YMSZZ!nBU{xjKfP$0k*C_d8 z_A3&Wq>SL<;dI$%n3jgEro)U26}_u3q9L;Zd#Gq>;l74rW%q?;cc)l2P%K8dQ<8@^m6$> z-s&^V9{6go3~{Uhs*!3|O>1kN1iYWnG$e1ahM`-{ss_n31N&ga+E>~aR&7W8fQMue zx!OXq!L(&~g@63~JQ>AFi#YiPA`fn?NUAS8XNZDLl@bOyw;3yBG+u2k>)#lGD%K_C zio$l4{pI=g_9X{6#d6{WoQQ0*EMNAR%U0fzFHp6qws5!b`PILl^cMA6G1~rqQn#E| zuEouxY&E8$<{e3sDMjI$u!5k*HTgrTLe_jn3yEp&m6Y-hY?#MnK*iT1TxjDtN529l zDV^c)c5RX$D0f(mU?|jRVB=JG8NAi)Q1Ss)VR>86G5(y?ON<+wpqZ6P9_Z?JJ#4K@qo?d8srtsGWi0zonhgZ z@0fAm{h3(^d+#$Y@Un?^D4rm313yX?hVoA*T$6%Y#=ZcGREYz8w;4C=`i60!*s}@2 zBAD=5l9H8Www%A7Nwgx}dP}Sn{HjG7SXG42RH0=Iq#Fy&eM01TV&Mrarzgs%lKsI@}ggu$p`rb82x5Bvk1Q6jQ{(lNq z4wR`9`?JyOBcJMxX`lYaJZOzi9MowlHJ5-fd6KxYk(0G3>QknXEbBJp4mBY1wln+x zoC&sa%d!2J@*WSiM=Df3Ncb!0eoZh?l1<3{YeFw>h9E2n|BfUTs;@G6=tD_jANln6 zM;88*jvKQsvc`LSo*4aJYTYRvNf!;zcu$&o(w&(0eokIT_^!O&s~z-2bDe2YA7znUC94eIvzC#1y^Xf3awGEKd|fJ zC621a+cm!ESi{o0=t8QW_yYMxdm<=Wk%F^dFXB?5s1zSsKF$-)p-1_S#%E<;cl3~d z`uaB?p|~kzIfJaF*7;nZ>CkX*bv0Q_A=03OT$RHIn-0=CQVHiOX$=${R63w>C+$Rw zd52E{pD)%qA#0cC5#;Zt{b1vJ^6+=-rdu#pj16PQI519(3**MbW5!?-Fo~EXj0cm9 p@nXheQZT8QG)y`s1M>oA9A-Rb0%js66O)C>#=MB?rcXMS{XYVG`+xud diff --git a/skills/codebase-cleanup-tech-debt/SKILL.md b/skills/codebase-cleanup-tech-debt/SKILL.md new file mode 100644 index 000000000..85b1d5535 --- /dev/null +++ b/skills/codebase-cleanup-tech-debt/SKILL.md @@ -0,0 +1,388 @@ +--- +name: codebase-cleanup-tech-debt +description: "You are a technical debt expert specializing in identifying, quantifying, and prioritizing technical debt in software projects. Analyze the codebase to uncover debt, assess its impact, and create acti" +risk: unknown +source: community +--- + +# Technical Debt Analysis and Remediation + +You are a technical debt expert specializing in identifying, quantifying, and prioritizing technical debt in software projects. Analyze the codebase to uncover debt, assess its impact, and create actionable remediation plans. + +## Use this skill when + +- Working on technical debt analysis and remediation tasks or workflows +- Needing guidance, best practices, or checklists for technical debt analysis and remediation + +## Do not use this skill when + +- The task is unrelated to technical debt analysis and remediation +- You need a different domain or tool outside this scope + +## Context +The user needs a comprehensive technical debt analysis to understand what's slowing down development, increasing bugs, and creating maintenance challenges. Focus on practical, measurable improvements with clear ROI. + +## Requirements +$ARGUMENTS + +## Instructions + +### 1. Technical Debt Inventory + +Conduct a thorough scan for all types of technical debt: + +**Code Debt** +- **Duplicated Code** + - Exact duplicates (copy-paste) + - Similar logic patterns + - Repeated business rules + - Quantify: Lines duplicated, locations + +- **Complex Code** + - High cyclomatic complexity (>10) + - Deeply nested conditionals (>3 levels) + - Long methods (>50 lines) + - God classes (>500 lines, >20 methods) + - Quantify: Complexity scores, hotspots + +- **Poor Structure** + - Circular dependencies + - Inappropriate intimacy between classes + - Feature envy (methods using other class data) + - Shotgun surgery patterns + - Quantify: Coupling metrics, change frequency + +**Architecture Debt** +- **Design Flaws** + - Missing abstractions + - Leaky abstractions + - Violated architectural boundaries + - Monolithic components + - Quantify: Component size, dependency violations + +- **Technology Debt** + - Outdated frameworks/libraries + - Deprecated API usage + - Legacy patterns (e.g., callbacks vs promises) + - Unsupported dependencies + - Quantify: Version lag, security vulnerabilities + +**Testing Debt** +- **Coverage Gaps** + - Untested code paths + - Missing edge cases + - No integration tests + - Lack of performance tests + - Quantify: Coverage %, critical paths untested + +- **Test Quality** + - Brittle tests (environment-dependent) + - Slow test suites + - Flaky tests + - No test documentation + - Quantify: Test runtime, failure rate + +**Documentation Debt** +- **Missing Documentation** + - No API documentation + - Undocumented complex logic + - Missing architecture diagrams + - No onboarding guides + - Quantify: Undocumented public APIs + +**Infrastructure Debt** +- **Deployment Issues** + - Manual deployment steps + - No rollback procedures + - Missing monitoring + - No performance baselines + - Quantify: Deployment time, failure rate + +### 2. Impact Assessment + +Calculate the real cost of each debt item: + +**Development Velocity Impact** +``` +Debt Item: Duplicate user validation logic +Locations: 5 files +Time Impact: +- 2 hours per bug fix (must fix in 5 places) +- 4 hours per feature change +- Monthly impact: ~20 hours +Annual Cost: 240 hours × $150/hour = $36,000 +``` + +**Quality Impact** +``` +Debt Item: No integration tests for payment flow +Bug Rate: 3 production bugs/month +Average Bug Cost: +- Investigation: 4 hours +- Fix: 2 hours +- Testing: 2 hours +- Deployment: 1 hour +Monthly Cost: 3 bugs × 9 hours × $150 = $4,050 +Annual Cost: $48,600 +``` + +**Risk Assessment** +- **Critical**: Security vulnerabilities, data loss risk +- **High**: Performance degradation, frequent outages +- **Medium**: Developer frustration, slow feature delivery +- **Low**: Code style issues, minor inefficiencies + +### 3. Debt Metrics Dashboard + +Create measurable KPIs: + +**Code Quality Metrics** +```yaml +Metrics: + cyclomatic_complexity: + current: 15.2 + target: 10.0 + files_above_threshold: 45 + + code_duplication: + percentage: 23% + target: 5% + duplication_hotspots: + - src/validation: 850 lines + - src/api/handlers: 620 lines + + test_coverage: + unit: 45% + integration: 12% + e2e: 5% + target: 80% / 60% / 30% + + dependency_health: + outdated_major: 12 + outdated_minor: 34 + security_vulnerabilities: 7 + deprecated_apis: 15 +``` + +**Trend Analysis** +```python +debt_trends = { + "2024_Q1": {"score": 750, "items": 125}, + "2024_Q2": {"score": 820, "items": 142}, + "2024_Q3": {"score": 890, "items": 156}, + "growth_rate": "18% quarterly", + "projection": "1200 by 2025_Q1 without intervention" +} +``` + +### 4. Prioritized Remediation Plan + +Create an actionable roadmap based on ROI: + +**Quick Wins (High Value, Low Effort)** +Week 1-2: +``` +1. Extract duplicate validation logic to shared module + Effort: 8 hours + Savings: 20 hours/month + ROI: 250% in first month + +2. Add error monitoring to payment service + Effort: 4 hours + Savings: 15 hours/month debugging + ROI: 375% in first month + +3. Automate deployment script + Effort: 12 hours + Savings: 2 hours/deployment × 20 deploys/month + ROI: 333% in first month +``` + +**Medium-Term Improvements (Month 1-3)** +``` +1. Refactor OrderService (God class) + - Split into 4 focused services + - Add comprehensive tests + - Create clear interfaces + Effort: 60 hours + Savings: 30 hours/month maintenance + ROI: Positive after 2 months + +2. Upgrade React 16 → 18 + - Update component patterns + - Migrate to hooks + - Fix breaking changes + Effort: 80 hours + Benefits: Performance +30%, Better DX + ROI: Positive after 3 months +``` + +**Long-Term Initiatives (Quarter 2-4)** +``` +1. Implement Domain-Driven Design + - Define bounded contexts + - Create domain models + - Establish clear boundaries + Effort: 200 hours + Benefits: 50% reduction in coupling + ROI: Positive after 6 months + +2. Comprehensive Test Suite + - Unit: 80% coverage + - Integration: 60% coverage + - E2E: Critical paths + Effort: 300 hours + Benefits: 70% reduction in bugs + ROI: Positive after 4 months +``` + +### 5. Implementation Strategy + +**Incremental Refactoring** +```python +# Phase 1: Add facade over legacy code +class PaymentFacade: + def __init__(self): + self.legacy_processor = LegacyPaymentProcessor() + + def process_payment(self, order): + # New clean interface + return self.legacy_processor.doPayment(order.to_legacy()) + +# Phase 2: Implement new service alongside +class PaymentService: + def process_payment(self, order): + # Clean implementation + pass + +# Phase 3: Gradual migration +class PaymentFacade: + def __init__(self): + self.new_service = PaymentService() + self.legacy = LegacyPaymentProcessor() + + def process_payment(self, order): + if feature_flag("use_new_payment"): + return self.new_service.process_payment(order) + return self.legacy.doPayment(order.to_legacy()) +``` + +**Team Allocation** +```yaml +Debt_Reduction_Team: + dedicated_time: "20% sprint capacity" + + roles: + - tech_lead: "Architecture decisions" + - senior_dev: "Complex refactoring" + - dev: "Testing and documentation" + + sprint_goals: + - sprint_1: "Quick wins completed" + - sprint_2: "God class refactoring started" + - sprint_3: "Test coverage >60%" +``` + +### 6. Prevention Strategy + +Implement gates to prevent new debt: + +**Automated Quality Gates** +```yaml +pre_commit_hooks: + - complexity_check: "max 10" + - duplication_check: "max 5%" + - test_coverage: "min 80% for new code" + +ci_pipeline: + - dependency_audit: "no high vulnerabilities" + - performance_test: "no regression >10%" + - architecture_check: "no new violations" + +code_review: + - requires_two_approvals: true + - must_include_tests: true + - documentation_required: true +``` + +**Debt Budget** +```python +debt_budget = { + "allowed_monthly_increase": "2%", + "mandatory_reduction": "5% per quarter", + "tracking": { + "complexity": "sonarqube", + "dependencies": "dependabot", + "coverage": "codecov" + } +} +``` + +### 7. Communication Plan + +**Stakeholder Reports** +```markdown +## Executive Summary +- Current debt score: 890 (High) +- Monthly velocity loss: 35% +- Bug rate increase: 45% +- Recommended investment: 500 hours +- Expected ROI: 280% over 12 months + +## Key Risks +1. Payment system: 3 critical vulnerabilities +2. Data layer: No backup strategy +3. API: Rate limiting not implemented + +## Proposed Actions +1. Immediate: Security patches (this week) +2. Short-term: Core refactoring (1 month) +3. Long-term: Architecture modernization (6 months) +``` + +**Developer Documentation** +```markdown +## Refactoring Guide +1. Always maintain backward compatibility +2. Write tests before refactoring +3. Use feature flags for gradual rollout +4. Document architectural decisions +5. Measure impact with metrics + +## Code Standards +- Complexity limit: 10 +- Method length: 20 lines +- Class length: 200 lines +- Test coverage: 80% +- Documentation: All public APIs +``` + +### 8. Success Metrics + +Track progress with clear KPIs: + +**Monthly Metrics** +- Debt score reduction: Target -5% +- New bug rate: Target -20% +- Deployment frequency: Target +50% +- Lead time: Target -30% +- Test coverage: Target +10% + +**Quarterly Reviews** +- Architecture health score +- Developer satisfaction survey +- Performance benchmarks +- Security audit results +- Cost savings achieved + +## Output Format + +1. **Debt Inventory**: Comprehensive list categorized by type with metrics +2. **Impact Analysis**: Cost calculations and risk assessments +3. **Prioritized Roadmap**: Quarter-by-quarter plan with clear deliverables +4. **Quick Wins**: Immediate actions for this sprint +5. **Implementation Guide**: Step-by-step refactoring strategies +6. **Prevention Plan**: Processes to avoid accumulating new debt +7. **ROI Projections**: Expected returns on debt reduction investment + +Focus on delivering measurable improvements that directly impact development velocity, system reliability, and team morale. diff --git a/skills/excalidraw-diagram-generator/SKILL.md b/skills/excalidraw-diagram-generator/SKILL.md new file mode 100644 index 000000000..e33fd9028 --- /dev/null +++ b/skills/excalidraw-diagram-generator/SKILL.md @@ -0,0 +1,613 @@ +--- +name: excalidraw-diagram-generator +description: 'Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.' +--- + +# Excalidraw Diagram Generator + +A skill for generating Excalidraw-format diagrams from natural language descriptions. This skill helps create visual representations of processes, systems, relationships, and ideas without manual drawing. + +## When to Use This Skill + +Use this skill when users request: + +- "Create a diagram showing..." +- "Make a flowchart for..." +- "Visualize the process of..." +- "Draw the system architecture of..." +- "Generate a mind map about..." +- "Create an Excalidraw file for..." +- "Show the relationship between..." +- "Diagram the workflow of..." + +**Supported diagram types:** +- 📊 **Flowcharts**: Sequential processes, workflows, decision trees +- 🔗 **Relationship Diagrams**: Entity relationships, system components, dependencies +- 🧠 **Mind Maps**: Concept hierarchies, brainstorming results, topic organization +- 🏗️ **Architecture Diagrams**: System design, module interactions, data flow +- 📈 **Data Flow Diagrams (DFD)**: Data flow visualization, data transformation processes +- 🏊 **Business Flow (Swimlane)**: Cross-functional workflows, actor-based process flows +- 📦 **Class Diagrams**: Object-oriented design, class structures and relationships +- 🔄 **Sequence Diagrams**: Object interactions over time, message flows +- 🗃️ **ER Diagrams**: Database entity relationships, data models + +## Prerequisites + +- Clear description of what should be visualized +- Identification of key entities, steps, or concepts +- Understanding of relationships or flow between elements + +## Step-by-Step Workflow + +### Step 1: Understand the Request + +Analyze the user's description to determine: +1. **Diagram type** (flowchart, relationship, mind map, architecture) +2. **Key elements** (entities, steps, concepts) +3. **Relationships** (flow, connections, hierarchy) +4. **Complexity** (number of elements) + +### Step 2: Choose the Appropriate Diagram Type + +| User Intent | Diagram Type | Example Keywords | +|-------------|--------------|------------------| +| Process flow, steps, procedures | **Flowchart** | "workflow", "process", "steps", "procedure" | +| Connections, dependencies, associations | **Relationship Diagram** | "relationship", "connections", "dependencies", "structure" | +| Concept hierarchy, brainstorming | **Mind Map** | "mind map", "concepts", "ideas", "breakdown" | +| System design, components | **Architecture Diagram** | "architecture", "system", "components", "modules" | +| Data flow, transformation processes | **Data Flow Diagram (DFD)** | "data flow", "data processing", "data transformation" | +| Cross-functional processes, actor responsibilities | **Business Flow (Swimlane)** | "business process", "swimlane", "actors", "responsibilities" | +| Object-oriented design, class structures | **Class Diagram** | "class", "inheritance", "OOP", "object model" | +| Interaction sequences, message flows | **Sequence Diagram** | "sequence", "interaction", "messages", "timeline" | +| Database design, entity relationships | **ER Diagram** | "database", "entity", "relationship", "data model" | + +### Step 3: Extract Structured Information + +**For Flowcharts:** +- List of sequential steps +- Decision points (if any) +- Start and end points + +**For Relationship Diagrams:** +- Entities/nodes (name + optional description) +- Relationships between entities (from → to, with label) + +**For Mind Maps:** +- Central topic +- Main branches (3-6 recommended) +- Sub-topics for each branch (optional) + +**For Data Flow Diagrams (DFD):** +- Data sources and destinations (external entities) +- Processes (data transformations) +- Data stores (databases, files) +- Data flows (arrows showing data movement from left-to-right or from top-left to bottom-right) +- **Important**: Do not represent process order, only data flow + +**For Business Flow (Swimlane):** +- Actors/roles (departments, systems, people) - displayed as header columns +- Process lanes (vertical lanes under each actor) +- Process boxes (activities within each lane) +- Flow arrows (connecting process boxes, including cross-lane handoffs) + +**For Class Diagrams:** +- Classes with names +- Attributes with visibility (+, -, #) +- Methods with visibility and parameters +- Relationships: inheritance (solid line + white triangle), implementation (dashed line + white triangle), association (solid line), dependency (dashed line), aggregation (solid line + white diamond), composition (solid line + filled diamond) +- Multiplicity notations (1, 0..1, 1..*, *) + +**For Sequence Diagrams:** +- Objects/actors (arranged horizontally at top) +- Lifelines (vertical lines from each object) +- Messages (horizontal arrows between lifelines) +- Synchronous messages (solid arrow), asynchronous messages (dashed arrow) +- Return values (dashed arrows) +- Activation boxes (rectangles on lifelines during execution) +- Time flows from top to bottom + +**For ER Diagrams:** +- Entities (rectangles with entity names) +- Attributes (listed inside entities) +- Primary keys (underlined or marked with PK) +- Foreign keys (marked with FK) +- Relationships (lines connecting entities) +- Cardinality: 1:1 (one-to-one), 1:N (one-to-many), N:M (many-to-many) +- Junction/associative entities for many-to-many relationships (dashed rectangles) + +### Step 4: Generate the Excalidraw JSON + +Create the `.excalidraw` file with appropriate elements: + +**Available element types:** +- `rectangle`: Boxes for entities, steps, concepts +- `ellipse`: Alternative shapes for emphasis +- `diamond`: Decision points +- `arrow`: Directional connections +- `text`: Labels and annotations + +**Key properties to set:** +- **Position**: `x`, `y` coordinates +- **Size**: `width`, `height` +- **Style**: `strokeColor`, `backgroundColor`, `fillStyle` +- **Font**: `fontFamily: 5` (Excalifont - **required for all text elements**) +- **Text**: Embedded text for labels +- **Connections**: `points` array for arrows + +**Important**: All text elements must use `fontFamily: 5` (Excalifont) for consistent visual appearance. + +### Step 5: Format the Output + +Structure the complete Excalidraw file: + +```json +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + // Array of diagram elements + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} +``` + +### Step 6: Save and Provide Instructions + +1. Save as `.excalidraw` +2. Inform user how to open: + - Visit https://excalidraw.com + - Click "Open" or drag-and-drop the file + - Or use Excalidraw VS Code extension + +## Best Practices + +### Element Count Guidelines + +| Diagram Type | Recommended Count | Maximum | +|--------------|-------------------|---------| +| Flowchart steps | 3-10 | 15 | +| Relationship entities | 3-8 | 12 | +| Mind map branches | 4-6 | 8 | +| Mind map sub-topics per branch | 2-4 | 6 | + +### Layout Tips + +1. **Start positions**: Center important elements, use consistent spacing +2. **Spacing**: + - Horizontal gap: 200-300px between elements + - Vertical gap: 100-150px between rows +3. **Colors**: Use consistent color scheme + - Primary elements: Light blue (`#a5d8ff`) + - Secondary elements: Light green (`#b2f2bb`) + - Important/Central: Yellow (`#ffd43b`) + - Alerts/Warnings: Light red (`#ffc9c9`) +4. **Text sizing**: 16-24px for readability +5. **Font**: Always use `fontFamily: 5` (Excalifont) for all text elements +6. **Arrow style**: Use straight arrows for simple flows, curved for complex relationships + +### Complexity Management + +**If user request has too many elements:** +- Suggest breaking into multiple diagrams +- Focus on main elements first +- Offer to create detailed sub-diagrams + +**Example response:** +``` +"Your request includes 15 components. For clarity, I recommend: +1. High-level architecture diagram (6 main components) +2. Detailed diagram for each subsystem + +Would you like me to start with the high-level view?" +``` + +## Example Prompts and Responses + +### Example 1: Simple Flowchart + +**User:** "Create a flowchart for user registration" + +**Agent generates:** +1. Extract steps: "Enter email" → "Verify email" → "Set password" → "Complete" +2. Create flowchart with 4 rectangles + 3 arrows +3. Save as `user-registration-flow.excalidraw` + +### Example 2: Relationship Diagram + +**User:** "Diagram the relationship between User, Post, and Comment entities" + +**Agent generates:** +1. Entities: User, Post, Comment +2. Relationships: User → Post ("creates"), User → Comment ("writes"), Post → Comment ("contains") +3. Save as `user-content-relationships.excalidraw` + +### Example 3: Mind Map + +**User:** "Mind map about machine learning concepts" + +**Agent generates:** +1. Center: "Machine Learning" +2. Branches: Supervised Learning, Unsupervised Learning, Reinforcement Learning, Deep Learning +3. Sub-topics under each branch +4. Save as `machine-learning-mindmap.excalidraw` + +## Troubleshooting + +| Issue | Solution | +|-------|----------| +| Elements overlap | Increase spacing between coordinates | +| Text doesn't fit in boxes | Increase box width or reduce font size | +| Too many elements | Break into multiple diagrams | +| Unclear layout | Use grid layout (rows/columns) or radial layout (mind maps) | +| Colors inconsistent | Define color palette upfront based on element types | + +## Advanced Techniques + +### Grid Layout (for Relationship Diagrams) +```javascript +const columns = Math.ceil(Math.sqrt(entityCount)); +const x = startX + (index % columns) * horizontalGap; +const y = startY + Math.floor(index / columns) * verticalGap; +``` + +### Radial Layout (for Mind Maps) +```javascript +const angle = (2 * Math.PI * index) / branchCount; +const x = centerX + radius * Math.cos(angle); +const y = centerY + radius * Math.sin(angle); +``` + +### Auto-generated IDs +Use timestamp + random string for unique IDs: +```javascript +const id = Date.now().toString(36) + Math.random().toString(36).substr(2); +``` + +## Output Format + +Always provide: +1. ✅ Complete `.excalidraw` JSON file +2. 📊 Summary of what was created +3. 📝 Element count +4. 💡 Instructions for opening/editing + +**Example summary:** +``` +Created: user-workflow.excalidraw +Type: Flowchart +Elements: 7 rectangles, 6 arrows, 1 title text +Total: 14 elements + +To view: +1. Visit https://excalidraw.com +2. Drag and drop user-workflow.excalidraw +3. Or use File → Open in Excalidraw VS Code extension +``` + +## Validation Checklist + +Before delivering the diagram: +- [ ] All elements have unique IDs +- [ ] Coordinates prevent overlapping +- [ ] Text is readable (font size 16+) +- [ ] **All text elements use `fontFamily: 5` (Excalifont)** +- [ ] Arrows connect logically +- [ ] Colors follow consistent scheme +- [ ] File is valid JSON +- [ ] Element count is reasonable (<20 for clarity) + +## Icon Libraries (Optional Enhancement) + +For specialized diagrams (e.g., AWS/GCP/Azure architecture diagrams), you can use pre-made icon libraries from Excalidraw. This provides professional, standardized icons instead of basic shapes. + +### When User Requests Icons + +**If user asks for AWS/cloud architecture diagrams or mentions wanting to use specific icons:** + +1. **Check if library exists**: Look for `libraries//reference.md` +2. **If library exists**: Proceed to use icons (see AI Assistant Workflow below) +3. **If library does NOT exist**: Respond with setup instructions: + + ``` + To use [AWS/GCP/Azure/etc.] architecture icons, please follow these steps: + + 1. Visit https://libraries.excalidraw.com/ + 2. Search for "[AWS Architecture Icons/etc.]" and download the .excalidrawlib file + 3. Create directory: skills/excalidraw-diagram-generator/libraries/[icon-set-name]/ + 4. Place the downloaded file in that directory + 5. Run the splitter script: + python skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/[icon-set-name]/ + + This will split the library into individual icon files for efficient use. + After setup is complete, I can create your diagram using the actual AWS/cloud icons. + + Alternatively, I can create the diagram now using simple shapes (rectangles, ellipses) + which you can later replace with icons manually in Excalidraw. + ``` + +### User Setup Instructions (Detailed) + +**Step 1: Create Library Directory** +```bash +mkdir -p skills/excalidraw-diagram-generator/libraries/aws-architecture-icons +``` + +**Step 2: Download Library** +- Visit: https://libraries.excalidraw.com/ +- Search for your desired icon set (e.g., "AWS Architecture Icons") +- Click download to get the `.excalidrawlib` file +- Example categories (availability varies; confirm on the site): + - Cloud service icons + - UI/Material icons + - Flowchart symbols + +**Step 3: Place Library File** +- Rename the downloaded file to match the directory name (e.g., `aws-architecture-icons.excalidrawlib`) +- Move it to the directory created in Step 1 + +**Step 4: Run Splitter Script** +```bash +python skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/ +``` + +**Step 5: Verify Setup** +After running the script, verify the following structure exists: +``` +skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/ + aws-architecture-icons.excalidrawlib (original) + reference.md (generated - icon lookup table) + icons/ (generated - individual icon files) + API-Gateway.json + CloudFront.json + EC2.json + Lambda.json + RDS.json + S3.json + ... +``` + +### AI Assistant Workflow + +**When icon libraries are available in `libraries/`:** + +**RECOMMENDED APPROACH: Use Python Scripts (Efficient & Reliable)** + +The repository includes Python scripts that handle icon integration automatically: + +1. **Create base diagram structure**: + - Create `.excalidraw` file with basic layout (title, boxes, regions) + - This establishes the canvas and overall structure + +2. **Add icons using Python script**: + ```bash + python skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py \ + [--label "Text"] [--library-path PATH] + ``` + - Edit via `.excalidraw.edit` is enabled by default to avoid overwrite issues; pass `--no-use-edit-suffix` to disable. + + **Examples**: + ```bash + # Add EC2 icon at position (400, 300) with label + python scripts/add-icon-to-diagram.py diagram.excalidraw EC2 400 300 --label "Web Server" + + # Add VPC icon at position (200, 150) + python scripts/add-icon-to-diagram.py diagram.excalidraw VPC 200 150 + + # Add icon from different library + python scripts/add-icon-to-diagram.py diagram.excalidraw Compute-Engine 500 200 \ + --library-path libraries/gcp-icons --label "API Server" + ``` + +3. **Add connecting arrows**: + ```bash + python skills/excalidraw-diagram-generator/scripts/add-arrow.py \ + [--label "Text"] [--style solid|dashed|dotted] [--color HEX] + ``` + - Edit via `.excalidraw.edit` is enabled by default to avoid overwrite issues; pass `--no-use-edit-suffix` to disable. + + **Examples**: + ```bash + # Simple arrow from (300, 250) to (500, 300) + python scripts/add-arrow.py diagram.excalidraw 300 250 500 300 + + # Arrow with label + python scripts/add-arrow.py diagram.excalidraw 300 250 500 300 --label "HTTPS" + + # Dashed arrow with custom color + python scripts/add-arrow.py diagram.excalidraw 400 350 600 400 --style dashed --color "#7950f2" + ``` + +4. **Workflow summary**: + ```bash + # Step 1: Create base diagram with title and structure + # (Create .excalidraw file with initial elements) + + # Step 2: Add icons with labels + python scripts/add-icon-to-diagram.py my-diagram.excalidraw "Internet-gateway" 200 150 --label "Internet Gateway" + python scripts/add-icon-to-diagram.py my-diagram.excalidraw VPC 250 250 + python scripts/add-icon-to-diagram.py my-diagram.excalidraw ELB 350 300 --label "Load Balancer" + python scripts/add-icon-to-diagram.py my-diagram.excalidraw EC2 450 350 --label "EC2 Instance" + python scripts/add-icon-to-diagram.py my-diagram.excalidraw RDS 550 400 --label "Database" + + # Step 3: Add connecting arrows + python scripts/add-arrow.py my-diagram.excalidraw 250 200 300 250 # Internet → VPC + python scripts/add-arrow.py my-diagram.excalidraw 300 300 400 300 # VPC → ELB + python scripts/add-arrow.py my-diagram.excalidraw 400 330 500 350 # ELB → EC2 + python scripts/add-arrow.py my-diagram.excalidraw 500 380 600 400 # EC2 → RDS + ``` + +**Benefits of Python Script Approach**: +- ✅ **No token consumption**: Icon JSON data (200-1000 lines each) never enters AI context +- ✅ **Accurate transformations**: Coordinate calculations handled deterministically +- ✅ **ID management**: Automatic UUID generation prevents conflicts +- ✅ **Reliable**: No risk of coordinate miscalculation or ID collision +- ✅ **Fast**: Direct file manipulation, no parsing overhead +- ✅ **Reusable**: Works with any Excalidraw library you provide + +**ALTERNATIVE: Manual Icon Integration (Not Recommended)** + +Only use this if Python scripts are unavailable: + +1. **Check for libraries**: + ``` + List directory: skills/excalidraw-diagram-generator/libraries/ + Look for subdirectories containing reference.md files + ``` + +2. **Read reference.md**: + ``` + Open: libraries//reference.md + This is lightweight (typically <300 lines) and lists all available icons + ``` + +3. **Find relevant icons**: + ``` + Search the reference.md table for icon names matching diagram needs + Example: For AWS diagram with EC2, S3, Lambda → Find "EC2", "S3", "Lambda" in table + ``` + +4. **Load specific icon data** (WARNING: Large files): + ``` + Read ONLY the needed icon files: + - libraries/aws-architecture-icons/icons/EC2.json (200-300 lines) + - libraries/aws-architecture-icons/icons/S3.json (200-300 lines) + - libraries/aws-architecture-icons/icons/Lambda.json (200-300 lines) + Note: Each icon file is 200-1000 lines - this consumes significant tokens + ``` + +5. **Extract and transform elements**: + ``` + Each icon JSON contains an "elements" array + Calculate bounding box (min_x, min_y, max_x, max_y) + Apply offset to all x/y coordinates + Generate new unique IDs for all elements + Update groupIds references + Copy transformed elements into your diagram + ``` + +6. **Position icons and add connections**: + ``` + Adjust x/y coordinates to position icons correctly in the diagram + Update IDs to ensure uniqueness across diagram + Add connecting arrows and labels as needed + ``` + +**Manual Integration Challenges**: +- ⚠️ High token consumption (200-1000 lines per icon × number of icons) +- ⚠️ Complex coordinate transformation calculations +- ⚠️ Risk of ID collision if not handled carefully +- ⚠️ Time-consuming for diagrams with many icons + +### Example: Creating AWS Diagram with Icons + +**Request**: "Create an AWS architecture diagram with Internet Gateway, VPC, ELB, EC2, and RDS" + +**Recommended Workflow (using Python scripts)**: +**Request**: "Create an AWS architecture diagram with Internet Gateway, VPC, ELB, EC2, and RDS" + +**Recommended Workflow (using Python scripts)**: + +```bash +# Step 1: Create base diagram file with title +# Create my-aws-diagram.excalidraw with basic structure (title, etc.) + +# Step 2: Check icon availability +# Read: libraries/aws-architecture-icons/reference.md +# Confirm icons exist: Internet-gateway, VPC, ELB, EC2, RDS + +# Step 3: Add icons with Python script +python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw "Internet-gateway" 150 100 --label "Internet Gateway" +python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw VPC 200 200 +python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw ELB 350 250 --label "Load Balancer" +python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw EC2 500 300 --label "Web Server" +python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw RDS 650 350 --label "Database" + +# Step 4: Add connecting arrows +python scripts/add-arrow.py my-aws-diagram.excalidraw 200 150 250 200 # Internet → VPC +python scripts/add-arrow.py my-aws-diagram.excalidraw 265 230 350 250 # VPC → ELB +python scripts/add-arrow.py my-aws-diagram.excalidraw 415 280 500 300 # ELB → EC2 +python scripts/add-arrow.py my-aws-diagram.excalidraw 565 330 650 350 --label "SQL" --style dashed + +# Result: Complete diagram with professional AWS icons, labels, and connections +``` + +**Benefits**: +- No manual coordinate calculation +- No token consumption for icon data +- Deterministic, reliable results +- Easy to iterate and adjust positions + +**Alternative Workflow (manual, if scripts unavailable)**: +1. Check: `libraries/aws-architecture-icons/reference.md` exists → Yes +2. Read reference.md → Find entries for Internet-gateway, VPC, ELB, EC2, RDS +3. Load: + - `icons/Internet-gateway.json` (298 lines) + - `icons/VPC.json` (550 lines) + - `icons/ELB.json` (363 lines) + - `icons/EC2.json` (231 lines) + - `icons/RDS.json` (similar size) + **Total: ~2000+ lines of JSON to process** +4. Extract elements from each JSON +5. Calculate bounding boxes and offsets for each icon +6. Transform all coordinates (x, y) for positioning +7. Generate unique IDs for all elements +8. Add arrows showing data flow +9. Add text labels +10. Generate final `.excalidraw` file + +**Challenges with manual approach**: +- High token consumption (~2000-5000 lines) +- Complex coordinate math +- Risk of ID conflicts + +### Supported Icon Libraries (Examples — verify availability) + +- This workflow works with any valid `.excalidrawlib` file you provide. +- Examples of library categories you may find on https://libraries.excalidraw.com/: + - Cloud service icons + - Kubernetes / infrastructure icons + - UI / Material icons + - Flowchart / diagram symbols + - Network diagram icons +- Availability and naming can change; verify exact library names on the site before use. + +### Fallback: No Icons Available + +**If no icon libraries are set up:** +- Create diagrams using basic shapes (rectangles, ellipses, arrows) +- Use color coding and text labels to distinguish components +- Inform user they can add icons later or set up libraries for future diagrams +- The diagram will still be functional and clear, just less visually polished + +## References + +See bundled references for: +- `references/excalidraw-schema.md` - Complete Excalidraw JSON schema +- `references/element-types.md` - Detailed element type specifications +- `templates/flowchart-template.json` - Basic flowchart starter +- `templates/relationship-template.json` - Relationship diagram starter +- `templates/mindmap-template.json` - Mind map starter +- `scripts/split-excalidraw-library.py` - Tool to split `.excalidrawlib` files +- `scripts/README.md` - Documentation for library tools +- `scripts/.gitignore` - Prevents local Python artifacts from being committed + +## Limitations + +- Complex curves are simplified to straight/basic curved lines +- Hand-drawn roughness is set to default (1) +- No embedded images support in auto-generation +- Maximum recommended elements: 20 per diagram +- No automatic collision detection (use spacing guidelines) + +## Future Enhancements + +Potential improvements: +- Auto-layout optimization algorithms +- Import from Mermaid/PlantUML syntax +- Template library expansion +- Interactive editing after generation diff --git a/skills/excalidraw-diagram-generator/references/element-types.md b/skills/excalidraw-diagram-generator/references/element-types.md new file mode 100644 index 000000000..3d85f8b2f --- /dev/null +++ b/skills/excalidraw-diagram-generator/references/element-types.md @@ -0,0 +1,497 @@ +# Excalidraw Element Types Guide + +Detailed specifications for each Excalidraw element type with visual examples and use cases. + +## Element Type Overview + +| Type | Visual | Primary Use | Text Support | +|------|--------|-------------|--------------| +| `rectangle` | □ | Boxes, containers, process steps | ✅ Yes | +| `ellipse` | ○ | Emphasis, terminals, states | ✅ Yes | +| `diamond` | ◇ | Decision points, choices | ✅ Yes | +| `arrow` | → | Directional flow, relationships | ❌ No (use separate text) | +| `line` | — | Connections, dividers | ❌ No | +| `text` | A | Labels, annotations, titles | ✅ (Its purpose) | + +--- + +## Rectangle + +**Best for:** Process steps, entities, data stores, components + +### Properties + +```typescript +{ + type: "rectangle", + roundness: { type: 3 }, // Rounded corners + text: "Step Name", // Optional embedded text + fontSize: 20, + textAlign: "center", + verticalAlign: "middle" +} +``` + +### Use Cases + +| Scenario | Configuration | +|----------|---------------| +| **Process step** | Green background (`#b2f2bb`), centered text | +| **Entity/Object** | Blue background (`#a5d8ff`), medium size | +| **System component** | Light color, descriptive text | +| **Data store** | Gray/white, database-like label | + +### Size Guidelines + +| Content | Width | Height | +|---------|-------|--------| +| Single word | 120-150px | 60-80px | +| Short phrase (2-4 words) | 180-220px | 80-100px | +| Sentence | 250-300px | 100-120px | + +### Example + +```json +{ + "type": "rectangle", + "x": 100, + "y": 100, + "width": 200, + "height": 80, + "backgroundColor": "#b2f2bb", + "text": "Validate Input", + "fontSize": 20, + "textAlign": "center", + "verticalAlign": "middle", + "roundness": { "type": 3 } +} +``` + +--- + +## Ellipse + +**Best for:** Start/end points, states, emphasis circles + +### Properties + +```typescript +{ + type: "ellipse", + text: "Start", + fontSize: 18, + textAlign: "center", + verticalAlign: "middle" +} +``` + +### Use Cases + +| Scenario | Configuration | +|----------|---------------| +| **Flow start** | Light green, "Start" text | +| **Flow end** | Light red, "End" text | +| **State** | Soft color, state name | +| **Highlight** | Bright color, emphasis text | + +### Size Guidelines + +For circular shapes, use `width === height`: + +| Content | Diameter | +|---------|----------| +| Icon/Symbol | 60-80px | +| Short text | 100-120px | +| Longer text | 150-180px | + +### Example + +```json +{ + "type": "ellipse", + "x": 100, + "y": 100, + "width": 120, + "height": 120, + "backgroundColor": "#d0f0c0", + "text": "Start", + "fontSize": 18, + "textAlign": "center", + "verticalAlign": "middle" +} +``` + +--- + +## Diamond + +**Best for:** Decision points, conditional branches + +### Properties + +```typescript +{ + type: "diamond", + text: "Valid?", + fontSize: 18, + textAlign: "center", + verticalAlign": "middle" +} +``` + +### Use Cases + +| Scenario | Text Example | +|----------|--------------| +| **Yes/No decision** | "Is Valid?", "Exists?" | +| **Multiple choice** | "Type?", "Status?" | +| **Conditional** | "Score > 50?" | + +### Size Guidelines + +Diamonds need more space than rectangles for the same text: + +| Content | Width | Height | +|---------|-------|--------| +| Yes/No | 120-140px | 120-140px | +| Short question | 160-180px | 160-180px | +| Longer question | 200-220px | 200-220px | + +### Example + +```json +{ + "type": "diamond", + "x": 100, + "y": 100, + "width": 150, + "height": 150, + "backgroundColor": "#ffe4a3", + "text": "Valid?", + "fontSize": 18, + "textAlign": "center", + "verticalAlign": "middle" +} +``` + +--- + +## Arrow + +**Best for:** Flow direction, relationships, dependencies + +### Properties + +```typescript +{ + type: "arrow", + points: [[0, 0], [endX, endY]], // Relative coordinates + roundness: { type: 2 }, // Curved + startBinding: null, // Or { elementId, focus, gap } + endBinding: null +} +``` + +### Arrow Directions + +#### Horizontal (Left to Right) + +```json +{ + "x": 100, + "y": 150, + "width": 200, + "height": 0, + "points": [[0, 0], [200, 0]] +} +``` + +#### Vertical (Top to Bottom) + +```json +{ + "x": 200, + "y": 100, + "width": 0, + "height": 150, + "points": [[0, 0], [0, 150]] +} +``` + +#### Diagonal + +```json +{ + "x": 100, + "y": 100, + "width": 200, + "height": 150, + "points": [[0, 0], [200, 150]] +} +``` + +### Arrow Styles + +| Style | `strokeStyle` | `strokeWidth` | Use Case | +|-------|---------------|---------------|----------| +| **Normal flow** | `"solid"` | 2 | Standard connections | +| **Optional/Weak** | `"dashed"` | 2 | Optional paths | +| **Important** | `"solid"` | 3-4 | Emphasized flow | +| **Dotted** | `"dotted"` | 2 | Indirect relationships | + +### Adding Arrow Labels + +Use separate text elements positioned near arrow midpoint: + +```json +[ + { + "type": "arrow", + "id": "arrow1", + "x": 100, + "y": 150, + "points": [[0, 0], [200, 0]] + }, + { + "type": "text", + "x": 180, // Near midpoint + "y": 130, // Above arrow + "text": "sends", + "fontSize": 14 + } +] +``` + +--- + +## Line + +**Best for:** Non-directional connections, dividers, borders + +### Properties + +```typescript +{ + type: "line", + points: [[0, 0], [x2, y2], [x3, y3], ...], + roundness: null // Or { type: 2 } for curved +} +``` + +### Use Cases + +| Scenario | Configuration | +|----------|---------------| +| **Divider** | Horizontal, thin stroke | +| **Border** | Closed path (polygon) | +| **Connection** | Multi-point path | +| **Underline** | Short horizontal line | + +### Multi-Point Line Example + +```json +{ + "type": "line", + "x": 100, + "y": 100, + "points": [ + [0, 0], + [100, 50], + [200, 0] + ] +} +``` + +--- + +## Text + +**Best for:** Labels, titles, annotations, standalone text + +### Properties + +```typescript +{ + type: "text", + text: "Label text", + fontSize: 20, + fontFamily: 1, // 1=Virgil, 2=Helvetica, 3=Cascadia + textAlign: "left", + verticalAlign: "top" +} +``` + +### Font Sizes by Purpose + +| Purpose | Font Size | +|---------|-----------| +| **Main title** | 28-36 | +| **Section header** | 24-28 | +| **Element label** | 18-22 | +| **Annotation** | 14-16 | +| **Small note** | 12-14 | + +### Width/Height Calculation + +```javascript +// Approximate width +const width = text.length * fontSize * 0.6; + +// Approximate height (single line) +const height = fontSize * 1.2; + +// Multi-line +const lines = text.split('\n').length; +const height = fontSize * 1.2 * lines; +``` + +### Text Positioning + +| Position | textAlign | verticalAlign | Use Case | +|----------|-----------|---------------|----------| +| **Top-left** | `"left"` | `"top"` | Default labels | +| **Centered** | `"center"` | `"middle"` | Titles | +| **Bottom-right** | `"right"` | `"bottom"` | Footnotes | + +### Example: Title + +```json +{ + "type": "text", + "x": 100, + "y": 50, + "width": 400, + "height": 40, + "text": "System Architecture", + "fontSize": 32, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top" +} +``` + +### Example: Annotation + +```json +{ + "type": "text", + "x": 150, + "y": 200, + "width": 100, + "height": 20, + "text": "User input", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" +} +``` + +--- + +## Combining Elements + +### Pattern: Labeled Box + +```json +[ + { + "type": "rectangle", + "id": "box1", + "x": 100, + "y": 100, + "width": 200, + "height": 100, + "text": "Component", + "textAlign": "center", + "verticalAlign": "middle" + } +] +``` + +### Pattern: Connected Boxes + +```json +[ + { + "type": "rectangle", + "id": "box1", + "x": 100, + "y": 100, + "width": 150, + "height": 80, + "text": "Step 1" + }, + { + "type": "arrow", + "id": "arrow1", + "x": 250, + "y": 140, + "points": [[0, 0], [100, 0]] + }, + { + "type": "rectangle", + "id": "box2", + "x": 350, + "y": 100, + "width": 150, + "height": 80, + "text": "Step 2" + } +] +``` + +### Pattern: Decision Tree + +```json +[ + { + "type": "diamond", + "id": "decision", + "x": 100, + "y": 100, + "width": 140, + "height": 140, + "text": "Valid?" + }, + { + "type": "arrow", + "id": "yes-arrow", + "x": 240, + "y": 170, + "points": [[0, 0], [60, 0]] + }, + { + "type": "text", + "id": "yes-label", + "x": 250, + "y": 150, + "text": "Yes", + "fontSize": 14 + }, + { + "type": "rectangle", + "id": "yes-box", + "x": 300, + "y": 140, + "width": 120, + "height": 60, + "text": "Process" + } +] +``` + +--- + +## Summary + +| When you need... | Use this element | +|------------------|------------------| +| Process box | `rectangle` with text | +| Decision point | `diamond` with question | +| Flow direction | `arrow` | +| Start/End | `ellipse` | +| Title/Header | `text` (large font) | +| Annotation | `text` (small font) | +| Non-directional link | `line` | +| Divider | `line` (horizontal) | diff --git a/skills/excalidraw-diagram-generator/references/excalidraw-schema.md b/skills/excalidraw-diagram-generator/references/excalidraw-schema.md new file mode 100644 index 000000000..bfdac6cf8 --- /dev/null +++ b/skills/excalidraw-diagram-generator/references/excalidraw-schema.md @@ -0,0 +1,350 @@ +# Excalidraw JSON Schema Reference + +This document describes the structure of Excalidraw `.excalidraw` files for diagram generation. + +## Top-Level Structure + +```typescript +interface ExcalidrawFile { + type: "excalidraw"; + version: number; // Always 2 + source: string; // "https://excalidraw.com" + elements: ExcalidrawElement[]; + appState: AppState; + files: Record; // Usually empty {} +} +``` + +## AppState + +```typescript +interface AppState { + viewBackgroundColor: string; // Hex color, e.g., "#ffffff" + gridSize: number; // Typically 20 +} +``` + +## ExcalidrawElement Base Properties + +All elements share these common properties: + +```typescript +interface BaseElement { + id: string; // Unique identifier + type: ElementType; // See Element Types below + x: number; // X coordinate (pixels from top-left) + y: number; // Y coordinate (pixels from top-left) + width: number; // Width in pixels + height: number; // Height in pixels + angle: number; // Rotation angle in radians (usually 0) + strokeColor: string; // Hex color, e.g., "#1e1e1e" + backgroundColor: string; // Hex color or "transparent" + fillStyle: "solid" | "hachure" | "cross-hatch"; + strokeWidth: number; // 1-4 typically + strokeStyle: "solid" | "dashed" | "dotted"; + roughness: number; // 0-2, controls hand-drawn effect (1 = default) + opacity: number; // 0-100 + groupIds: string[]; // IDs of groups this element belongs to + frameId: null; // Usually null + index: string; // Stacking order identifier + roundness: Roundness | null; + seed: number; // Random seed for deterministic rendering + version: number; // Element version (increment on edit) + versionNonce: number; // Random number changed on edit + isDeleted: boolean; // Should be false + boundElements: any; // Usually null + updated: number; // Timestamp in milliseconds + link: null; // External link (usually null) + locked: boolean; // Whether element is locked +} +``` + +## Element Types + +### Rectangle + +```typescript +interface RectangleElement extends BaseElement { + type: "rectangle"; + roundness: { type: 3 }; // 3 = rounded corners + text?: string; // Optional text inside + fontSize?: number; // Font size (16-32 typical) + fontFamily?: number; // 1 = Virgil, 2 = Helvetica, 3 = Cascadia + textAlign?: "left" | "center" | "right"; + verticalAlign?: "top" | "middle" | "bottom"; +} +``` + +**Example:** +```json +{ + "id": "rect1", + "type": "rectangle", + "x": 100, + "y": 100, + "width": 200, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "text": "My Box", + "fontSize": 20, + "textAlign": "center", + "verticalAlign": "middle", + "roundness": { "type": 3 } +} +``` + +### Ellipse + +```typescript +interface EllipseElement extends BaseElement { + type: "ellipse"; + text?: string; + fontSize?: number; + fontFamily?: number; + textAlign?: "left" | "center" | "right"; + verticalAlign?: "top" | "middle" | "bottom"; +} +``` + +### Diamond + +```typescript +interface DiamondElement extends BaseElement { + type: "diamond"; + text?: string; + fontSize?: number; + fontFamily?: number; + textAlign?: "left" | "center" | "right"; + verticalAlign?: "top" | "middle" | "bottom"; +} +``` + +### Arrow + +```typescript +interface ArrowElement extends BaseElement { + type: "arrow"; + points: [number, number][]; // Array of [x, y] coordinates relative to element + startBinding: Binding | null; + endBinding: Binding | null; + roundness: { type: 2 }; // 2 = curved arrow +} +``` + +**Example:** +```json +{ + "id": "arrow1", + "type": "arrow", + "x": 100, + "y": 100, + "width": 200, + "height": 0, + "points": [ + [0, 0], + [200, 0] + ], + "roundness": { "type": 2 }, + "startBinding": null, + "endBinding": null +} +``` + +**Points explanation:** +- First point `[0, 0]` is relative to `(x, y)` +- Subsequent points are relative to the first point +- For straight horizontal arrow: `[[0, 0], [width, 0]]` +- For straight vertical arrow: `[[0, 0], [0, height]]` + +### Line + +```typescript +interface LineElement extends BaseElement { + type: "line"; + points: [number, number][]; + startBinding: Binding | null; + endBinding: Binding | null; + roundness: { type: 2 } | null; +} +``` + +### Text + +```typescript +interface TextElement extends BaseElement { + type: "text"; + text: string; + fontSize: number; + fontFamily: number; // 1-3 + textAlign: "left" | "center" | "right"; + verticalAlign: "top" | "middle" | "bottom"; + roundness: null; // Text has no roundness +} +``` + +**Example:** +```json +{ + "id": "text1", + "type": "text", + "x": 100, + "y": 100, + "width": 150, + "height": 25, + "text": "Hello World", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "roundness": null +} +``` + +**Width/Height calculation:** +- Width ≈ `text.length * fontSize * 0.6` +- Height ≈ `fontSize * 1.2 * numberOfLines` + +## Bindings + +Bindings connect arrows to shapes: + +```typescript +interface Binding { + elementId: string; // ID of bound element + focus: number; // -1 to 1, position along edge + gap: number; // Distance from element edge +} +``` + +## Common Colors + +| Color Name | Hex Code | Use Case | +|------------|----------|----------| +| Black | `#1e1e1e` | Default stroke | +| Light Blue | `#a5d8ff` | Primary entities | +| Light Green | `#b2f2bb` | Process steps | +| Yellow | `#ffd43b` | Important/Central | +| Light Red | `#ffc9c9` | Warnings/Errors | +| Cyan | `#96f2d7` | Secondary items | +| Transparent | `transparent` | No fill | +| White | `#ffffff` | Background | + +## ID Generation + +IDs should be unique strings. Common patterns: + +```javascript +// Timestamp-based +const id = Date.now().toString(36) + Math.random().toString(36).substr(2); + +// Sequential +const id = "element-" + counter++; + +// Descriptive +const id = "step-1", "entity-user", "arrow-1-to-2"; +``` + +## Seed Generation + +Seeds are used for deterministic randomness in hand-drawn effect: + +```javascript +const seed = Math.floor(Math.random() * 2147483647); +``` + +## Version and VersionNonce + +```javascript +const version = 1; // Increment when element is edited +const versionNonce = Math.floor(Math.random() * 2147483647); +``` + +## Coordinate System + +- Origin `(0, 0)` is top-left corner +- X increases to the right +- Y increases downward +- All units are in pixels + +## Recommended Spacing + +| Context | Spacing | +|---------|---------| +| Horizontal gap between elements | 200-300px | +| Vertical gap between rows | 100-150px | +| Minimum margin from edge | 50px | +| Arrow-to-box clearance | 20-30px | + +## Font Families + +| ID | Name | Description | +|----|------|-------------| +| 1 | Virgil | Hand-drawn style (default) | +| 2 | Helvetica | Clean sans-serif | +| 3 | Cascadia | Monospace | + +## Validation Rules + +✅ **Required:** +- All IDs must be unique +- `type` must match actual element type +- `version` must be an integer ≥ 1 +- `opacity` must be 0-100 + +⚠️ **Recommended:** +- Keep `roughness` at 1 for consistency +- Use `strokeWidth` of 2 for clarity +- Set `isDeleted` to `false` +- Set `locked` to `false` +- Keep `frameId`, `boundElements`, `link` as `null` + +## Complete Minimal Example + +```json +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "box1", + "type": "rectangle", + "x": 100, + "y": 100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { "type": 3 }, + "seed": 1234567890, + "version": 1, + "versionNonce": 987654321, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Hello", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} +``` diff --git a/skills/excalidraw-diagram-generator/scripts/.gitignore b/skills/excalidraw-diagram-generator/scripts/.gitignore new file mode 100644 index 000000000..bcc50b67f --- /dev/null +++ b/skills/excalidraw-diagram-generator/scripts/.gitignore @@ -0,0 +1,34 @@ +# Avoid accidentally committing local Python artifacts produced when running these scripts. + +# Byte-compiled / cache files +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments (people often create these next to scripts) +.venv/ +venv/ +env/ +ENV/ + +# Tool caches +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.tox/ +.nox/ + +# Coverage outputs +.coverage +.coverage.* +htmlcov/ + +# Packaging/build outputs (rare here, but harmless) +build/ +dist/ +*.egg-info/ +.eggs/ + +# OS cruft +.DS_Store +Thumbs.db diff --git a/skills/excalidraw-diagram-generator/scripts/README.md b/skills/excalidraw-diagram-generator/scripts/README.md new file mode 100644 index 000000000..df810f1ab --- /dev/null +++ b/skills/excalidraw-diagram-generator/scripts/README.md @@ -0,0 +1,193 @@ +# Excalidraw Library Tools + +This directory contains scripts for working with Excalidraw libraries. + +## split-excalidraw-library.py + +Splits an Excalidraw library file (`*.excalidrawlib`) into individual icon JSON files for efficient token usage by AI assistants. + +### Prerequisites + +- Python 3.6 or higher +- No additional dependencies required (uses only standard library) + +### Usage + +```bash +python split-excalidraw-library.py +``` + +### Step-by-Step Workflow + +1. **Create library directory**: + ```bash + mkdir -p skills/excalidraw-diagram-generator/libraries/aws-architecture-icons + ``` + +2. **Download and place library file**: + - Visit: https://libraries.excalidraw.com/ + - Search for "AWS Architecture Icons" and download the `.excalidrawlib` file + - Rename it to match the directory name: `aws-architecture-icons.excalidrawlib` + - Place it in the directory created in step 1 + +3. **Run the script**: + ```bash + python skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/ + ``` + +### Output Structure + +The script creates the following structure in the library directory: + +``` +skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/ + aws-architecture-icons.excalidrawlib # Original file (kept) + reference.md # Generated: Quick reference table + icons/ # Generated: Individual icon files + API-Gateway.json + CloudFront.json + EC2.json + S3.json + ... +``` + +### What the Script Does + +1. **Reads** the `.excalidrawlib` file +2. **Extracts** each icon from the `libraryItems` array +3. **Sanitizes** icon names to create valid filenames (spaces → hyphens, removes special characters) +4. **Saves** each icon as a separate JSON file in the `icons/` directory +5. **Generates** a `reference.md` file with a table mapping icon names to filenames + +### Benefits + +- **Token Efficiency**: AI can first read the lightweight `reference.md` to find relevant icons, then load only the specific icon files needed +- **Organization**: Icons are organized in a clear directory structure +- **Extensibility**: Users can add multiple library sets side-by-side + +### Recommended Workflow + +1. Download desired Excalidraw libraries from https://libraries.excalidraw.com/ +2. Run this script on each library file +3. Move the generated folders to `../libraries/` +4. The AI assistant will use `reference.md` files to locate and use icons efficiently + +### Library Sources (Examples — verify availability) + +- Examples found on https://libraries.excalidraw.com/ may include cloud/service icon sets. +- Availability changes over time; verify the exact library names on the site before use. +- This script works with any valid `.excalidrawlib` file you provide. + +### Troubleshooting + +**Error: File not found** +- Check that the file path is correct +- Make sure the file has a `.excalidrawlib` extension + +**Error: Invalid library file format** +- Ensure the file is a valid Excalidraw library file +- Check that it contains a `libraryItems` array + +### License Considerations + +When using third-party icon libraries: +- **AWS Architecture Icons**: Subject to AWS Content License +- **GCP Icons**: Subject to Google's terms +- **Other libraries**: Check each library's license + +This script is for personal/organizational use. Redistribution of split icon files should comply with the original library's license terms. + +## add-icon-to-diagram.py + +Adds a specific icon from a split Excalidraw library into an existing `.excalidraw` diagram. The script handles coordinate translation and ID collision avoidance, and can optionally add a label under the icon. + +### Prerequisites + +- Python 3.6 or higher +- A diagram file (`.excalidraw`) +- A split icon library directory (created by `split-excalidraw-library.py`) + +### Usage + +```bash +python add-icon-to-diagram.py [OPTIONS] +``` + +**Options** +- `--library-path PATH` : Path to the icon library directory (default: `aws-architecture-icons`) +- `--label TEXT` : Add a text label below the icon +-- `--use-edit-suffix` : Edit via `.excalidraw.edit` to avoid editor overwrite issues (enabled by default; pass `--no-use-edit-suffix` to disable) + +### Examples + +```bash +# Add EC2 icon at position (400, 300) +python add-icon-to-diagram.py diagram.excalidraw EC2 400 300 + +# Add VPC icon with label +python add-icon-to-diagram.py diagram.excalidraw VPC 200 150 --label "VPC" + +# Safe edit mode is enabled by default (avoids editor overwrite issues) +# Use `--no-use-edit-suffix` to disable +python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 + +# Add icon from another library +python add-icon-to-diagram.py diagram.excalidraw Compute-Engine 500 200 \ + --library-path libraries/gcp-icons --label "API Server" +``` + +### What the Script Does + +1. **Loads** the icon JSON from the library’s `icons/` directory +2. **Calculates** the icon’s bounding box +3. **Offsets** all coordinates to the target position +4. **Generates** unique IDs for all elements and groups +5. **Appends** the transformed elements to the diagram +6. **(Optional)** Adds a label beneath the icon + +--- + +## add-arrow.py + +Adds a straight arrow between two points in an existing `.excalidraw` diagram. Supports optional labels and line styles. + +### Prerequisites + +- Python 3.6 or higher +- A diagram file (`.excalidraw`) + +### Usage + +```bash +python add-arrow.py [OPTIONS] +``` + +**Options** +- `--style {solid|dashed|dotted}` : Line style (default: `solid`) +- `--color HEX` : Arrow color (default: `#1e1e1e`) +- `--label TEXT` : Add a text label on the arrow +-- `--use-edit-suffix` : Edit via `.excalidraw.edit` to avoid editor overwrite issues (enabled by default; pass `--no-use-edit-suffix` to disable) + +### Examples + +```bash +# Simple arrow +python add-arrow.py diagram.excalidraw 300 200 500 300 + +# Arrow with label +python add-arrow.py diagram.excalidraw 300 200 500 300 --label "HTTPS" + +# Dashed arrow with custom color +python add-arrow.py diagram.excalidraw 400 350 600 400 --style dashed --color "#7950f2" + +# Safe edit mode is enabled by default (avoids editor overwrite issues) +# Use `--no-use-edit-suffix` to disable +python add-arrow.py diagram.excalidraw 300 200 500 300 +``` + +### What the Script Does + +1. **Creates** an arrow element from the given coordinates +2. **(Optional)** Adds a label near the arrow midpoint +3. **Appends** elements to the diagram +4. **Saves** the updated file diff --git a/skills/excalidraw-diagram-generator/scripts/add-arrow.py b/skills/excalidraw-diagram-generator/scripts/add-arrow.py new file mode 100644 index 000000000..169f09ff1 --- /dev/null +++ b/skills/excalidraw-diagram-generator/scripts/add-arrow.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python3 +""" +Add arrows (connections) between elements in Excalidraw diagrams. + +Usage: + python add-arrow.py [OPTIONS] + +Options: + --style {solid|dashed|dotted} Arrow line style (default: solid) + --color HEX Arrow color (default: #1e1e1e) + --label TEXT Add text label on the arrow + --use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable) + +Examples: + python add-arrow.py diagram.excalidraw 300 200 500 300 + python add-arrow.py diagram.excalidraw 300 200 500 300 --label "HTTP" + python add-arrow.py diagram.excalidraw 300 200 500 300 --style dashed --color "#7950f2" + python add-arrow.py diagram.excalidraw 300 200 500 300 --use-edit-suffix +""" + +import json +import sys +import uuid +from pathlib import Path +from typing import Dict, Any + + +def generate_unique_id() -> str: + """Generate a unique ID for Excalidraw elements.""" + return str(uuid.uuid4()).replace('-', '')[:16] + + +def prepare_edit_path(diagram_path: Path, use_edit_suffix: bool) -> tuple[Path, Path | None]: + """ + Prepare a safe edit path to avoid editor overwrite issues. + + Returns: + (work_path, final_path) + - work_path: file path to read/write during edit + - final_path: file path to rename back to (or None if not used) + """ + if not use_edit_suffix: + return diagram_path, None + + if diagram_path.suffix != ".excalidraw": + return diagram_path, None + + edit_path = diagram_path.with_suffix(diagram_path.suffix + ".edit") + + if diagram_path.exists(): + if edit_path.exists(): + raise FileExistsError(f"Edit file already exists: {edit_path}") + diagram_path.rename(edit_path) + + return edit_path, diagram_path + + +def finalize_edit_path(work_path: Path, final_path: Path | None) -> None: + """Finalize edit by renaming .edit back to .excalidraw if needed.""" + if final_path is None: + return + + if final_path.exists(): + final_path.unlink() + + work_path.rename(final_path) + + +def create_arrow( + from_x: float, + from_y: float, + to_x: float, + to_y: float, + style: str = "solid", + color: str = "#1e1e1e", + label: str = None +) -> list: + """ + Create an arrow element. + + Args: + from_x: Starting X coordinate + from_y: Starting Y coordinate + to_x: Ending X coordinate + to_y: Ending Y coordinate + style: Line style (solid, dashed, dotted) + color: Arrow color + label: Optional text label on the arrow + + Returns: + List of elements (arrow and optional label) + """ + elements = [] + + # Arrow element + arrow = { + "id": generate_unique_id(), + "type": "arrow", + "x": from_x, + "y": from_y, + "width": to_x - from_x, + "height": to_y - from_y, + "angle": 0, + "strokeColor": color, + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": style, + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": None, + "index": "a0", + "roundness": { + "type": 2 + }, + "seed": 1000000000 + hash(f"{from_x}{from_y}{to_x}{to_y}") % 1000000000, + "version": 1, + "versionNonce": 2000000000 + hash(f"{from_x}{from_y}{to_x}{to_y}") % 1000000000, + "isDeleted": False, + "boundElements": [], + "updated": 1738195200000, + "link": None, + "locked": False, + "points": [ + [0, 0], + [to_x - from_x, to_y - from_y] + ], + "startBinding": None, + "endBinding": None, + "startArrowhead": None, + "endArrowhead": "arrow", + "lastCommittedPoint": None + } + elements.append(arrow) + + # Optional label + if label: + mid_x = (from_x + to_x) / 2 - (len(label) * 5) + mid_y = (from_y + to_y) / 2 - 10 + + label_element = { + "id": generate_unique_id(), + "type": "text", + "x": mid_x, + "y": mid_y, + "width": len(label) * 10, + "height": 20, + "angle": 0, + "strokeColor": color, + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": None, + "index": "a0", + "roundness": None, + "seed": 1000000000 + hash(label) % 1000000000, + "version": 1, + "versionNonce": 2000000000 + hash(label) % 1000000000, + "isDeleted": False, + "boundElements": [], + "updated": 1738195200000, + "link": None, + "locked": False, + "text": label, + "fontSize": 14, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": None, + "originalText": label, + "autoResize": True, + "lineHeight": 1.25 + } + elements.append(label_element) + + return elements + + +def add_arrow_to_diagram( + diagram_path: Path, + from_x: float, + from_y: float, + to_x: float, + to_y: float, + style: str = "solid", + color: str = "#1e1e1e", + label: str = None +) -> None: + """ + Add an arrow to an Excalidraw diagram. + + Args: + diagram_path: Path to the Excalidraw diagram file + from_x: Starting X coordinate + from_y: Starting Y coordinate + to_x: Ending X coordinate + to_y: Ending Y coordinate + style: Line style (solid, dashed, dotted) + color: Arrow color + label: Optional text label + """ + print(f"Creating arrow from ({from_x}, {from_y}) to ({to_x}, {to_y})") + arrow_elements = create_arrow(from_x, from_y, to_x, to_y, style, color, label) + + if label: + print(f" With label: '{label}'") + + # Load diagram + print(f"Loading diagram: {diagram_path}") + with open(diagram_path, 'r', encoding='utf-8') as f: + diagram = json.load(f) + + # Add arrow elements + if 'elements' not in diagram: + diagram['elements'] = [] + + original_count = len(diagram['elements']) + diagram['elements'].extend(arrow_elements) + print(f" Added {len(arrow_elements)} elements (total: {original_count} -> {len(diagram['elements'])})") + + # Save diagram + print(f"Saving diagram") + with open(diagram_path, 'w', encoding='utf-8') as f: + json.dump(diagram, f, indent=2, ensure_ascii=False) + + print(f"✓ Successfully added arrow to diagram") + + +def main(): + """Main entry point.""" + if len(sys.argv) < 6: + print("Usage: python add-arrow.py [OPTIONS]") + print("\nOptions:") + print(" --style {solid|dashed|dotted} Line style (default: solid)") + print(" --color HEX Color (default: #1e1e1e)") + print(" --label TEXT Text label on arrow") + print(" --use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable)") + print("\nExamples:") + print(" python add-arrow.py diagram.excalidraw 300 200 500 300") + print(" python add-arrow.py diagram.excalidraw 300 200 500 300 --label 'HTTP'") + sys.exit(1) + + diagram_path = Path(sys.argv[1]) + from_x = float(sys.argv[2]) + from_y = float(sys.argv[3]) + to_x = float(sys.argv[4]) + to_y = float(sys.argv[5]) + + # Parse optional arguments + style = "solid" + color = "#1e1e1e" + label = None + # Default: use edit suffix to avoid editor overwrite issues + use_edit_suffix = True + + i = 6 + while i < len(sys.argv): + if sys.argv[i] == '--style': + if i + 1 < len(sys.argv): + style = sys.argv[i + 1] + if style not in ['solid', 'dashed', 'dotted']: + print(f"Error: Invalid style '{style}'. Must be: solid, dashed, or dotted") + sys.exit(1) + i += 2 + else: + print("Error: --style requires an argument") + sys.exit(1) + elif sys.argv[i] == '--color': + if i + 1 < len(sys.argv): + color = sys.argv[i + 1] + i += 2 + else: + print("Error: --color requires an argument") + sys.exit(1) + elif sys.argv[i] == '--label': + if i + 1 < len(sys.argv): + label = sys.argv[i + 1] + i += 2 + else: + print("Error: --label requires a text argument") + sys.exit(1) + elif sys.argv[i] == '--use-edit-suffix': + use_edit_suffix = True + i += 1 + elif sys.argv[i] == '--no-use-edit-suffix': + use_edit_suffix = False + i += 1 + else: + print(f"Error: Unknown option: {sys.argv[i]}") + sys.exit(1) + + # Validate inputs + if not diagram_path.exists(): + print(f"Error: Diagram file not found: {diagram_path}") + sys.exit(1) + + try: + work_path, final_path = prepare_edit_path(diagram_path, use_edit_suffix) + add_arrow_to_diagram(work_path, from_x, from_y, to_x, to_y, style, color, label) + finalize_edit_path(work_path, final_path) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py b/skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py new file mode 100644 index 000000000..f10352545 --- /dev/null +++ b/skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +""" +Add icons from Excalidraw libraries to diagrams. + +This script reads an icon JSON file from an Excalidraw library, transforms its coordinates +to a target position, generates unique IDs, and adds it to an existing Excalidraw diagram. +Works with any Excalidraw library (AWS, GCP, Azure, Kubernetes, etc.). + +Usage: + python add-icon-to-diagram.py [OPTIONS] + +Options: + --library-path PATH Path to the icon library directory (default: aws-architecture-icons) + --label TEXT Add a text label below the icon + --use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable) + +Examples: + python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 + python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 --label "Web Server" + python add-icon-to-diagram.py diagram.excalidraw VPC 200 150 --library-path libraries/gcp-icons + python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 --use-edit-suffix +""" + +import json +import sys +import uuid +from pathlib import Path +from typing import Dict, List, Any, Tuple + + +def generate_unique_id() -> str: + """Generate a unique ID for Excalidraw elements.""" + return str(uuid.uuid4()).replace('-', '')[:16] + + +def calculate_bounding_box(elements: List[Dict[str, Any]]) -> Tuple[float, float, float, float]: + """Calculate the bounding box (min_x, min_y, max_x, max_y) of icon elements.""" + if not elements: + return (0, 0, 0, 0) + + min_x = float('inf') + min_y = float('inf') + max_x = float('-inf') + max_y = float('-inf') + + for element in elements: + if 'x' in element and 'y' in element: + x = element['x'] + y = element['y'] + width = element.get('width', 0) + height = element.get('height', 0) + + min_x = min(min_x, x) + min_y = min(min_y, y) + max_x = max(max_x, x + width) + max_y = max(max_y, y + height) + + return (min_x, min_y, max_x, max_y) + + +def transform_icon_elements( + elements: List[Dict[str, Any]], + target_x: float, + target_y: float +) -> List[Dict[str, Any]]: + """ + Transform icon elements to target coordinates with unique IDs. + + Args: + elements: Icon elements from JSON file + target_x: Target X coordinate (top-left position) + target_y: Target Y coordinate (top-left position) + + Returns: + Transformed elements with new coordinates and IDs + """ + if not elements: + return [] + + # Calculate bounding box + min_x, min_y, max_x, max_y = calculate_bounding_box(elements) + + # Calculate offset + offset_x = target_x - min_x + offset_y = target_y - min_y + + # Create ID mapping: old_id -> new_id + id_mapping = {} + for element in elements: + if 'id' in element: + old_id = element['id'] + id_mapping[old_id] = generate_unique_id() + + # Create group ID mapping + group_id_mapping = {} + for element in elements: + if 'groupIds' in element: + for old_group_id in element['groupIds']: + if old_group_id not in group_id_mapping: + group_id_mapping[old_group_id] = generate_unique_id() + + # Transform elements + transformed = [] + for element in elements: + new_element = element.copy() + + # Update coordinates + if 'x' in new_element: + new_element['x'] = new_element['x'] + offset_x + if 'y' in new_element: + new_element['y'] = new_element['y'] + offset_y + + # Update ID + if 'id' in new_element: + new_element['id'] = id_mapping[new_element['id']] + + # Update group IDs + if 'groupIds' in new_element: + new_element['groupIds'] = [ + group_id_mapping[gid] for gid in new_element['groupIds'] + ] + + # Update binding references if they exist + if 'startBinding' in new_element and new_element['startBinding']: + if 'elementId' in new_element['startBinding']: + old_id = new_element['startBinding']['elementId'] + if old_id in id_mapping: + new_element['startBinding']['elementId'] = id_mapping[old_id] + + if 'endBinding' in new_element and new_element['endBinding']: + if 'elementId' in new_element['endBinding']: + old_id = new_element['endBinding']['elementId'] + if old_id in id_mapping: + new_element['endBinding']['elementId'] = id_mapping[old_id] + + # Update containerId if it exists + if 'containerId' in new_element and new_element['containerId']: + old_id = new_element['containerId'] + if old_id in id_mapping: + new_element['containerId'] = id_mapping[old_id] + + # Update boundElements if they exist + if 'boundElements' in new_element and new_element['boundElements']: + new_bound_elements = [] + for bound_elem in new_element['boundElements']: + if isinstance(bound_elem, dict) and 'id' in bound_elem: + old_id = bound_elem['id'] + if old_id in id_mapping: + bound_elem['id'] = id_mapping[old_id] + new_bound_elements.append(bound_elem) + new_element['boundElements'] = new_bound_elements + + transformed.append(new_element) + + return transformed + + +def load_icon(icon_name: str, library_path: Path) -> List[Dict[str, Any]]: + """ + Load icon elements from library. + + Args: + icon_name: Name of the icon (e.g., "EC2", "VPC") + library_path: Path to the icon library directory + + Returns: + List of icon elements + """ + icon_file = library_path / "icons" / f"{icon_name}.json" + + if not icon_file.exists(): + raise FileNotFoundError(f"Icon file not found: {icon_file}") + + with open(icon_file, 'r', encoding='utf-8') as f: + icon_data = json.load(f) + + return icon_data.get('elements', []) + + +def prepare_edit_path(diagram_path: Path, use_edit_suffix: bool) -> tuple[Path, Path | None]: + """ + Prepare a safe edit path to avoid editor overwrite issues. + + Returns: + (work_path, final_path) + - work_path: file path to read/write during edit + - final_path: file path to rename back to (or None if not used) + """ + if not use_edit_suffix: + return diagram_path, None + + if diagram_path.suffix != ".excalidraw": + return diagram_path, None + + edit_path = diagram_path.with_suffix(diagram_path.suffix + ".edit") + + if diagram_path.exists(): + if edit_path.exists(): + raise FileExistsError(f"Edit file already exists: {edit_path}") + diagram_path.rename(edit_path) + + return edit_path, diagram_path + + +def finalize_edit_path(work_path: Path, final_path: Path | None) -> None: + """Finalize edit by renaming .edit back to .excalidraw if needed.""" + if final_path is None: + return + + if final_path.exists(): + final_path.unlink() + + work_path.rename(final_path) + + +def create_text_label(text: str, x: float, y: float) -> Dict[str, Any]: + """ + Create a text label element. + + Args: + text: Label text + x: X coordinate + y: Y coordinate + + Returns: + Text element dictionary + """ + return { + "id": generate_unique_id(), + "type": "text", + "x": x, + "y": y, + "width": len(text) * 10, # Approximate width + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": None, + "index": "a0", + "roundness": None, + "seed": 1000000000 + hash(text) % 1000000000, + "version": 1, + "versionNonce": 2000000000 + hash(text) % 1000000000, + "isDeleted": False, + "boundElements": [], + "updated": 1738195200000, + "link": None, + "locked": False, + "text": text, + "fontSize": 16, + "fontFamily": 5, # Excalifont + "textAlign": "center", + "verticalAlign": "top", + "containerId": None, + "originalText": text, + "autoResize": True, + "lineHeight": 1.25 + } + + +def add_icon_to_diagram( + diagram_path: Path, + icon_name: str, + x: float, + y: float, + library_path: Path, + label: str = None +) -> None: + """ + Add an icon to an Excalidraw diagram. + + Args: + diagram_path: Path to the Excalidraw diagram file + icon_name: Name of the icon to add + x: Target X coordinate + y: Target Y coordinate + library_path: Path to the icon library directory + label: Optional text label to add below the icon + """ + # Load icon elements + print(f"Loading icon: {icon_name}") + icon_elements = load_icon(icon_name, library_path) + print(f" Loaded {len(icon_elements)} elements") + + # Transform icon elements + print(f"Transforming to position ({x}, {y})") + transformed_elements = transform_icon_elements(icon_elements, x, y) + + # Calculate icon bounding box for label positioning + if label and transformed_elements: + min_x, min_y, max_x, max_y = calculate_bounding_box(transformed_elements) + icon_width = max_x - min_x + icon_height = max_y - min_y + + # Position label below icon, centered + label_x = min_x + (icon_width / 2) - (len(label) * 5) + label_y = max_y + 10 + + label_element = create_text_label(label, label_x, label_y) + transformed_elements.append(label_element) + print(f" Added label: '{label}'") + + # Load diagram + print(f"Loading diagram: {diagram_path}") + with open(diagram_path, 'r', encoding='utf-8') as f: + diagram = json.load(f) + + # Add transformed elements + if 'elements' not in diagram: + diagram['elements'] = [] + + original_count = len(diagram['elements']) + diagram['elements'].extend(transformed_elements) + print(f" Added {len(transformed_elements)} elements (total: {original_count} -> {len(diagram['elements'])})") + + # Save diagram + print(f"Saving diagram") + with open(diagram_path, 'w', encoding='utf-8') as f: + json.dump(diagram, f, indent=2, ensure_ascii=False) + + print(f"✓ Successfully added '{icon_name}' icon to diagram") + + +def main(): + """Main entry point.""" + if len(sys.argv) < 5: + print("Usage: python add-icon-to-diagram.py [OPTIONS]") + print("\nOptions:") + print(" --library-path PATH Path to icon library directory") + print(" --label TEXT Add text label below icon") + print(" --use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable)") + print("\nExamples:") + print(" python add-icon-to-diagram.py diagram.excalidraw EC2 500 300") + print(" python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 --label 'Web Server'") + sys.exit(1) + + diagram_path = Path(sys.argv[1]) + icon_name = sys.argv[2] + x = float(sys.argv[3]) + y = float(sys.argv[4]) + + # Default library path + script_dir = Path(__file__).parent + default_library_path = script_dir.parent / "libraries" / "aws-architecture-icons" + + # Parse optional arguments + library_path = default_library_path + label = None + # Default: use edit suffix to avoid editor overwrite issues + use_edit_suffix = True + + i = 5 + while i < len(sys.argv): + if sys.argv[i] == '--library-path': + if i + 1 < len(sys.argv): + library_path = Path(sys.argv[i + 1]) + i += 2 + else: + print("Error: --library-path requires a path argument") + sys.exit(1) + elif sys.argv[i] == '--label': + if i + 1 < len(sys.argv): + label = sys.argv[i + 1] + i += 2 + else: + print("Error: --label requires a text argument") + sys.exit(1) + elif sys.argv[i] == '--use-edit-suffix': + use_edit_suffix = True + i += 1 + elif sys.argv[i] == '--no-use-edit-suffix': + use_edit_suffix = False + i += 1 + else: + print(f"Error: Unknown option: {sys.argv[i]}") + sys.exit(1) + + # Validate inputs + if not diagram_path.exists(): + print(f"Error: Diagram file not found: {diagram_path}") + sys.exit(1) + + if not library_path.exists(): + print(f"Error: Library path not found: {library_path}") + sys.exit(1) + + try: + work_path, final_path = prepare_edit_path(diagram_path, use_edit_suffix) + add_icon_to_diagram(work_path, icon_name, x, y, library_path, label) + finalize_edit_path(work_path, final_path) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + +if __name__ == '__main__': + main() + diff --git a/skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py b/skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py new file mode 100644 index 000000000..ec903dd25 --- /dev/null +++ b/skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +""" +Excalidraw Library Splitter + +This script splits an Excalidraw library file (*.excalidrawlib) into individual +icon JSON files and generates a reference.md file for easy lookup. + +The script expects the following structure: + skills/excalidraw-diagram-generator/libraries/{icon-set-name}/ + {icon-set-name}.excalidrawlib (place this file first) + +Usage: + python split-excalidraw-library.py + +Example: + python split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/ +""" + +import json +import os +import re +import sys +from pathlib import Path + + +def sanitize_filename(name: str) -> str: + """ + Sanitize icon name to create a valid filename. + + Args: + name: Original icon name + + Returns: + Sanitized filename safe for all platforms + """ + # Replace spaces with hyphens + filename = name.replace(' ', '-') + + # Remove or replace special characters + filename = re.sub(r'[^\w\-.]', '', filename) + + # Remove multiple consecutive hyphens + filename = re.sub(r'-+', '-', filename) + + # Remove leading/trailing hyphens + filename = filename.strip('-') + + return filename + + +def find_library_file(directory: Path) -> Path: + """ + Find the .excalidrawlib file in the given directory. + + Args: + directory: Directory to search + + Returns: + Path to the library file + + Raises: + SystemExit: If no library file or multiple library files found + """ + library_files = list(directory.glob('*.excalidrawlib')) + + if len(library_files) == 0: + print(f"Error: No .excalidrawlib file found in {directory}") + print(f"Please place a .excalidrawlib file in {directory} first.") + sys.exit(1) + + if len(library_files) > 1: + print(f"Error: Multiple .excalidrawlib files found in {directory}") + print(f"Please keep only one library file in {directory}.") + sys.exit(1) + + return library_files[0] + + +def split_library(library_dir: str) -> None: + """ + Split an Excalidraw library file into individual icon files. + + Args: + library_dir: Path to the directory containing the .excalidrawlib file + """ + library_dir = Path(library_dir) + + if not library_dir.exists(): + print(f"Error: Directory not found: {library_dir}") + sys.exit(1) + + if not library_dir.is_dir(): + print(f"Error: Path is not a directory: {library_dir}") + sys.exit(1) + + # Find the library file + library_path = find_library_file(library_dir) + print(f"Found library: {library_path.name}") + + # Load library file + print(f"Loading library data...") + with open(library_path, 'r', encoding='utf-8') as f: + library_data = json.load(f) + + # Validate library structure + if 'libraryItems' not in library_data: + print("Error: Invalid library file format (missing 'libraryItems')") + sys.exit(1) + + # Create icons directory + icons_dir = library_dir / 'icons' + icons_dir.mkdir(exist_ok=True) + print(f"Output directory: {library_dir}") + + # Process each library item (icon) + library_items = library_data['libraryItems'] + icon_list = [] + + print(f"Processing {len(library_items)} icons...") + + for item in library_items: + # Get icon name + icon_name = item.get('name', 'Unnamed') + + # Create sanitized filename + filename = sanitize_filename(icon_name) + '.json' + + # Save icon data + icon_path = icons_dir / filename + with open(icon_path, 'w', encoding='utf-8') as f: + json.dump(item, f, ensure_ascii=False, indent=2) + + # Add to reference list + icon_list.append({ + 'name': icon_name, + 'filename': filename + }) + + print(f" ✓ {icon_name} → {filename}") + + # Sort icon list by name + icon_list.sort(key=lambda x: x['name']) + + # Generate reference.md + library_name = library_path.stem + reference_path = library_dir / 'reference.md' + with open(reference_path, 'w', encoding='utf-8') as f: + f.write(f"# {library_name} Reference\n\n") + f.write(f"This directory contains {len(icon_list)} icons extracted from `{library_path.name}`.\n\n") + f.write("## Available Icons\n\n") + f.write("| Icon Name | Filename |\n") + f.write("|-----------|----------|\n") + + for icon in icon_list: + f.write(f"| {icon['name']} | `icons/{icon['filename']}` |\n") + + f.write("\n## Usage\n\n") + f.write("Each icon JSON file contains the complete `elements` array needed to render that icon in Excalidraw.\n") + f.write("You can copy the elements from these files into your Excalidraw diagrams.\n") + + print(f"\n✅ Successfully split library into {len(icon_list)} icons") + print(f"📄 Reference file created: {reference_path}") + print(f"📁 Icons directory: {icons_dir}") + + +def main(): + """Main entry point.""" + if hasattr(sys.stdout, "reconfigure"): + # Ensure consistent UTF-8 output on Windows consoles. + sys.stdout.reconfigure(encoding="utf-8") + if len(sys.argv) != 2: + print("Usage: python split-excalidraw-library.py ") + print("\nExample:") + print(" python split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/") + print("\nNote: The directory should contain a .excalidrawlib file.") + sys.exit(1) + + library_dir = sys.argv[1] + split_library(library_dir) + + +if __name__ == '__main__': + main() diff --git a/skills/excalidraw-diagram-generator/templates/business-flow-swimlane-template.excalidraw b/skills/excalidraw-diagram-generator/templates/business-flow-swimlane-template.excalidraw new file mode 100644 index 000000000..0d0c26b81 --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/business-flow-swimlane-template.excalidraw @@ -0,0 +1,334 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "title", + "type": "text", + "x": 200, + "y": 50, + "width": 300, + "height": 30, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": null, + "seed": 2001001001, + "version": 1, + "versionNonce": 3002002001, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Business Process Flow", + "fontSize": 24, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top" + }, + { + "id": "lane-header-1", + "type": "rectangle", + "x": 100, + "y": 120, + "width": 200, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 2001001002, + "version": 1, + "versionNonce": 3002002002, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Customer", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "lane-1", + "type": "rectangle", + "x": 100, + "y": 170, + "width": 200, + "height": 250, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": null, + "seed": 2001001003, + "version": 1, + "versionNonce": 3002002003, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "process-1", + "type": "rectangle", + "x": 130, + "y": 200, + "width": 140, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": { "type": 3 }, + "seed": 2001001004, + "version": 1, + "versionNonce": 3002002004, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Submit\nRequest", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "lane-header-2", + "type": "rectangle", + "x": 300, + "y": 120, + "width": 200, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": null, + "seed": 2001001005, + "version": 1, + "versionNonce": 3002002005, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Sales Team", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "lane-2", + "type": "rectangle", + "x": 300, + "y": 170, + "width": 200, + "height": 250, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 2001001006, + "version": 1, + "versionNonce": 3002002006, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "process-2", + "type": "rectangle", + "x": 330, + "y": 200, + "width": 140, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd43b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": { "type": 3 }, + "seed": 2001001007, + "version": 1, + "versionNonce": 3002002007, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Review\nRequest", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "cross-lane-arrow", + "type": "arrow", + "x": 270, + "y": 235, + "width": 60, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": { "type": 2 }, + "seed": 2001001008, + "version": 1, + "versionNonce": 3002002008, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [60, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "process-3", + "type": "rectangle", + "x": 330, + "y": 310, + "width": 140, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd43b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": { "type": 3 }, + "seed": 2001001009, + "version": 1, + "versionNonce": 3002002009, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Approve", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "within-lane-arrow", + "type": "arrow", + "x": 400, + "y": 270, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": { "type": 2 }, + "seed": 2001001010, + "version": 1, + "versionNonce": 3002002010, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 40] + ], + "startBinding": null, + "endBinding": null + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/skills/excalidraw-diagram-generator/templates/class-diagram-template.excalidraw b/skills/excalidraw-diagram-generator/templates/class-diagram-template.excalidraw new file mode 100644 index 000000000..aae28dfbe --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/class-diagram-template.excalidraw @@ -0,0 +1,558 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "class-1", + "type": "rectangle", + "x": 100, + "y": 100, + "width": 200, + "height": 180, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": null, + "seed": 3001001001, + "version": 1, + "versionNonce": 4002002001, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "class-name-1", + "type": "text", + "x": 150, + "y": 110, + "width": 100, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 3001001002, + "version": 1, + "versionNonce": 4002002002, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "User", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "User", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "separator-1", + "type": "line", + "x": 100, + "y": 145, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": null, + "seed": 3001001003, + "version": 1, + "versionNonce": 4002002003, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ] + ], + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "attributes-1", + "type": "text", + "x": 110, + "y": 155, + "width": 180, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 3001001004, + "version": 1, + "versionNonce": 4002002004, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "- id: number\n- name: string\n- email: string", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- id: number\n- name: string\n- email: string", + "autoResize": true, + "lineHeight": 1.1904761904761905 + }, + { + "id": "separator-2", + "type": "line", + "x": 100, + "y": 215, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": null, + "seed": 3001001005, + "version": 1, + "versionNonce": 4002002005, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ] + ], + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "methods-1", + "type": "text", + "x": 110, + "y": 225, + "width": 180, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 3001001006, + "version": 3, + "versionNonce": 1660402375, + "isDeleted": false, + "boundElements": [], + "updated": 1769755991910, + "link": null, + "locked": false, + "text": "+ login(): void\n+ logout(): void\n+ updateProfile(): void", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "+ login(): void\n+ logout(): void\n+ updateProfile(): void", + "autoResize": true, + "lineHeight": 1.0714285714285714 + }, + { + "id": "class-2", + "type": "rectangle", + "x": 400, + "y": 100, + "width": 200, + "height": 180, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": null, + "seed": 3001001007, + "version": 1, + "versionNonce": 4002002007, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "class-name-2", + "type": "text", + "x": 430, + "y": 110, + "width": 140, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 3001001008, + "version": 1, + "versionNonce": 4002002008, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "AdminUser", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "AdminUser", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "separator-3", + "type": "line", + "x": 400, + "y": 145, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": null, + "seed": 3001001009, + "version": 1, + "versionNonce": 4002002009, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ] + ], + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "attributes-2", + "type": "text", + "x": 410, + "y": 155, + "width": 180, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": null, + "seed": 3001001010, + "version": 1, + "versionNonce": 4002002010, + "isDeleted": false, + "boundElements": [], + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "- role: string\n- permissions: string[]", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- role: string\n- permissions: string[]", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "separator-4", + "type": "line", + "x": 400, + "y": 200, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aA", + "roundness": null, + "seed": 3001001011, + "version": 2, + "versionNonce": 873024679, + "isDeleted": false, + "boundElements": [], + "updated": 1769755880046, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ] + ], + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "methods-2", + "type": "text", + "x": 410, + "y": 210, + "width": 180, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 3001001012, + "version": 2, + "versionNonce": 1702655305, + "isDeleted": false, + "boundElements": [], + "updated": 1769755880046, + "link": null, + "locked": false, + "text": "+ manageUsers(): void\n+ assignRole(): void\n+ revokePermission(): void", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "+ manageUsers(): void\n+ assignRole(): void\n+ revokePermission(): void", + "autoResize": true, + "lineHeight": 1.4285714285714286 + }, + { + "id": "inheritance-line", + "type": "line", + "x": 400, + "y": 190, + "width": 100, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aC", + "roundness": null, + "seed": 3001001013, + "version": 18, + "versionNonce": 1139021225, + "isDeleted": false, + "boundElements": [], + "updated": 1769755989350, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -100, + 0 + ] + ], + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "inheritance-triangle", + "type": "line", + "x": 314.1999816894531, + "y": 181.5, + "width": 15, + "height": 15, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aD", + "roundness": null, + "seed": 3001001014, + "version": 21, + "versionNonce": 1468657767, + "isDeleted": false, + "boundElements": [], + "updated": 1769756005117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -15, + 15 + ], + [ + 0, + 15 + ], + [ + 0, + 0 + ] + ], + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/skills/excalidraw-diagram-generator/templates/data-flow-diagram-template.excalidraw b/skills/excalidraw-diagram-generator/templates/data-flow-diagram-template.excalidraw new file mode 100644 index 000000000..baea839e8 --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/data-flow-diagram-template.excalidraw @@ -0,0 +1,279 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "external-entity-1", + "type": "rectangle", + "x": 100, + "y": 200, + "width": 120, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { "type": 3 }, + "seed": 1001001001, + "version": 1, + "versionNonce": 2002002002, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "User", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "data-flow-1", + "type": "arrow", + "x": 220, + "y": 240, + "width": 80, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": { "type": 2 }, + "seed": 1001001002, + "version": 1, + "versionNonce": 2002002003, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [80, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "flow-label-1", + "type": "text", + "x": 230, + "y": 220, + "width": 80, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": null, + "seed": 1001001003, + "version": 1, + "versionNonce": 2002002004, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "input data", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "process-1", + "type": "ellipse", + "x": 300, + "y": 200, + "width": 120, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 1001001004, + "version": 1, + "versionNonce": 2002002005, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Process\nData", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "data-flow-2", + "type": "arrow", + "x": 420, + "y": 240, + "width": 80, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": { "type": 2 }, + "seed": 1001001005, + "version": 1, + "versionNonce": 2002002006, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [80, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "flow-label-2", + "type": "text", + "x": 425, + "y": 220, + "width": 100, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 1001001006, + "version": 1, + "versionNonce": 2002002007, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "processed data", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "data-store-1", + "type": "rectangle", + "x": 500, + "y": 200, + "width": 150, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#96f2d7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": null, + "seed": 1001001007, + "version": 1, + "versionNonce": 2002002008, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Data Store\n(Database)", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "data-store-line", + "type": "line", + "x": 500, + "y": 225, + "width": 150, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 1001001008, + "version": 1, + "versionNonce": 2002002009, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [150, 0] + ], + "startBinding": null, + "endBinding": null + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/skills/excalidraw-diagram-generator/templates/er-diagram-template.excalidraw b/skills/excalidraw-diagram-generator/templates/er-diagram-template.excalidraw new file mode 100644 index 000000000..a023522ca --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/er-diagram-template.excalidraw @@ -0,0 +1,662 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "entity-1", + "type": "rectangle", + "x": 100, + "y": 150, + "width": 180, + "height": 150, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": null, + "seed": 5001001001, + "version": 1, + "versionNonce": 6002002001, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "entity-name-1", + "type": "text", + "x": 150, + "y": 160, + "width": 80, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 5001001002, + "version": 1, + "versionNonce": 6002002002, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "User", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top" + }, + { + "id": "entity-separator-1", + "type": "line", + "x": 100, + "y": 195, + "width": 180, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": null, + "seed": 5001001003, + "version": 1, + "versionNonce": 6002002003, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [180, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "attributes-1", + "type": "text", + "x": 110, + "y": 205, + "width": 160, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 5001001004, + "version": 1, + "versionNonce": 6002002004, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "PK: user_id\nname\nemail\ncreated_at", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "entity-2", + "type": "rectangle", + "x": 450, + "y": 150, + "width": 180, + "height": 150, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": null, + "seed": 5001001005, + "version": 1, + "versionNonce": 6002002005, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "entity-name-2", + "type": "text", + "x": 500, + "y": 160, + "width": 80, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 5001001006, + "version": 1, + "versionNonce": 6002002006, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Order", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top" + }, + { + "id": "entity-separator-2", + "type": "line", + "x": 450, + "y": 195, + "width": 180, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": null, + "seed": 5001001007, + "version": 1, + "versionNonce": 6002002007, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [180, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "attributes-2", + "type": "text", + "x": 460, + "y": 205, + "width": 160, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 5001001008, + "version": 1, + "versionNonce": 6002002008, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "PK: order_id\nFK: user_id\ntotal_amount\norder_date", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "relationship-line", + "type": "line", + "x": 280, + "y": 225, + "width": 170, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": null, + "seed": 5001001009, + "version": 1, + "versionNonce": 6002002009, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [170, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "cardinality-1", + "type": "text", + "x": 290, + "y": 205, + "width": 20, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": null, + "seed": 5001001010, + "version": 1, + "versionNonce": 6002002010, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "1", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "cardinality-2", + "type": "text", + "x": 420, + "y": 205, + "width": 20, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a10", + "roundness": null, + "seed": 5001001011, + "version": 1, + "versionNonce": 6002002011, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "N", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "relationship-label", + "type": "text", + "x": 330, + "y": 200, + "width": 80, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a11", + "roundness": null, + "seed": 5001001012, + "version": 1, + "versionNonce": 6002002012, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "places", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top" + }, + { + "id": "entity-3", + "type": "rectangle", + "x": 450, + "y": 380, + "width": 180, + "height": 120, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0f0c0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a12", + "roundness": null, + "seed": 5001001013, + "version": 1, + "versionNonce": 6002002013, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "entity-name-3", + "type": "text", + "x": 480, + "y": 390, + "width": 120, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a13", + "roundness": null, + "seed": 5001001014, + "version": 1, + "versionNonce": 6002002014, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Product", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top" + }, + { + "id": "entity-separator-3", + "type": "line", + "x": 450, + "y": 425, + "width": 180, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a14", + "roundness": null, + "seed": 5001001015, + "version": 1, + "versionNonce": 6002002015, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [180, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "attributes-3", + "type": "text", + "x": 460, + "y": 435, + "width": 160, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a15", + "roundness": null, + "seed": 5001001016, + "version": 1, + "versionNonce": 6002002016, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "PK: product_id\nname\nprice", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "relationship-line-2", + "type": "line", + "x": 540, + "y": 300, + "width": 0, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a16", + "roundness": null, + "seed": 5001001017, + "version": 1, + "versionNonce": 6002002017, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 80] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "cardinality-3", + "type": "text", + "x": 550, + "y": 310, + "width": 20, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a17", + "roundness": null, + "seed": 5001001018, + "version": 1, + "versionNonce": 6002002018, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "N", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "cardinality-4", + "type": "text", + "x": 550, + "y": 350, + "width": 20, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a18", + "roundness": null, + "seed": 5001001019, + "version": 1, + "versionNonce": 6002002019, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "M", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "relationship-label-2", + "type": "text", + "x": 490, + "y": 330, + "width": 80, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a19", + "roundness": null, + "seed": 5001001020, + "version": 1, + "versionNonce": 6002002020, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "contains", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/skills/excalidraw-diagram-generator/templates/flowchart-template.excalidraw b/skills/excalidraw-diagram-generator/templates/flowchart-template.excalidraw new file mode 100644 index 000000000..965a3f9c0 --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/flowchart-template.excalidraw @@ -0,0 +1,179 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "step1", + "type": "rectangle", + "x": 400, + "y": 200, + "width": 200, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { "type": 3 }, + "seed": 1234567890, + "version": 1, + "versionNonce": 987654321, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Step 1", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "arrow1", + "type": "arrow", + "x": 500, + "y": 280, + "width": 0, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": { "type": 2 }, + "seed": 1234567891, + "version": 1, + "versionNonce": 987654322, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 100] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "step2", + "type": "rectangle", + "x": 400, + "y": 380, + "width": 200, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { "type": 3 }, + "seed": 1234567892, + "version": 1, + "versionNonce": 987654323, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Step 2", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "arrow2", + "type": "arrow", + "x": 500, + "y": 460, + "width": 0, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": { "type": 2 }, + "seed": 1234567893, + "version": 1, + "versionNonce": 987654324, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 100] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "step3", + "type": "rectangle", + "x": 400, + "y": 560, + "width": 200, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": { "type": 3 }, + "seed": 1234567894, + "version": 1, + "versionNonce": 987654325, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Step 3", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/skills/excalidraw-diagram-generator/templates/mindmap-template.excalidraw b/skills/excalidraw-diagram-generator/templates/mindmap-template.excalidraw new file mode 100644 index 000000000..53e382c4e --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/mindmap-template.excalidraw @@ -0,0 +1,244 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "center", + "type": "rectangle", + "x": 500, + "y": 350, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd43b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { + "type": 3 + }, + "seed": 3333333333, + "version": 3, + "versionNonce": 641024845, + "isDeleted": false, + "boundElements": [ + { + "id": "arrow1", + "type": "arrow" + }, + { + "id": "arrow2", + "type": "arrow" + } + ], + "updated": 1769755916717, + "link": null, + "locked": false, + "text": "Central Topic", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "branch1", + "type": "rectangle", + "x": 250, + "y": 150, + "width": 150, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#96f2d7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": { + "type": 3 + }, + "seed": 3333333334, + "version": 2, + "versionNonce": 2040232045, + "isDeleted": false, + "boundElements": [ + { + "id": "arrow1", + "type": "arrow" + } + ], + "updated": 1769755912840, + "link": null, + "locked": false, + "text": "Branch 1", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "arrow1", + "type": "arrow", + "x": 600, + "y": 350, + "width": 246.39999389648438, + "height": 111.20001220703125, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { + "type": 2 + }, + "seed": 3333333335, + "version": 23, + "versionNonce": 308894189, + "isDeleted": false, + "boundElements": [], + "updated": 1769755914127, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -246.39999389648438, + -111.20001220703125 + ] + ], + "startBinding": { + "elementId": "center", + "focus": 0.5255972360761778, + "gap": 1 + }, + "endBinding": { + "elementId": "branch1", + "focus": 0.48604063201707415, + "gap": 8.79998779296875 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "branch2", + "type": "rectangle", + "x": 750, + "y": 150, + "width": 150, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#96f2d7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": { + "type": 3 + }, + "seed": 3333333336, + "version": 2, + "versionNonce": 1459929741, + "isDeleted": false, + "boundElements": [ + { + "id": "arrow2", + "type": "arrow" + } + ], + "updated": 1769755916716, + "link": null, + "locked": false, + "text": "Branch 2", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "arrow2", + "type": "arrow", + "x": 600, + "y": 350, + "width": 216, + "height": 112.80001831054688, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": { + "type": 2 + }, + "seed": 3333333337, + "version": 41, + "versionNonce": 1447859213, + "isDeleted": false, + "boundElements": [], + "updated": 1769756030188, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 216, + -112.80001831054688 + ] + ], + "startBinding": { + "elementId": "center", + "focus": -0.48913039421990545, + "gap": 1 + }, + "endBinding": { + "elementId": "branch2", + "focus": -0.5368418212214556, + "gap": 7.199981689453125 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow" + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/skills/excalidraw-diagram-generator/templates/relationship-template.excalidraw b/skills/excalidraw-diagram-generator/templates/relationship-template.excalidraw new file mode 100644 index 000000000..b2ea0b6a0 --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/relationship-template.excalidraw @@ -0,0 +1,145 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "entity1", + "type": "rectangle", + "x": 300, + "y": 300, + "width": 180, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { "type": 3 }, + "seed": 1111111111, + "version": 1, + "versionNonce": 2222222222, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Entity A", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "entity2", + "type": "rectangle", + "x": 600, + "y": 300, + "width": 180, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": { "type": 3 }, + "seed": 1111111112, + "version": 1, + "versionNonce": 2222222223, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Entity B", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "relationship", + "type": "arrow", + "x": 480, + "y": 350, + "width": 120, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { "type": 2 }, + "seed": 1111111113, + "version": 1, + "versionNonce": 2222222224, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [120, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "label", + "type": "text", + "x": 510, + "y": 325, + "width": 60, + "height": 24, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 1111111114, + "version": 1, + "versionNonce": 2222222225, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "relates to", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/skills/excalidraw-diagram-generator/templates/sequence-diagram-template.excalidraw b/skills/excalidraw-diagram-generator/templates/sequence-diagram-template.excalidraw new file mode 100644 index 000000000..6602ae264 --- /dev/null +++ b/skills/excalidraw-diagram-generator/templates/sequence-diagram-template.excalidraw @@ -0,0 +1,509 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "object-1", + "type": "rectangle", + "x": 150, + "y": 100, + "width": 120, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": null, + "seed": 4001001001, + "version": 1, + "versionNonce": 5002002001, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Client", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "lifeline-1", + "type": "line", + "x": 210, + "y": 150, + "width": 0, + "height": 300, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 4001001002, + "version": 1, + "versionNonce": 5002002002, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 300] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "object-2", + "type": "rectangle", + "x": 350, + "y": 100, + "width": 120, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": null, + "seed": 4001001003, + "version": 1, + "versionNonce": 5002002003, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Server", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "lifeline-2", + "type": "line", + "x": 410, + "y": 150, + "width": 0, + "height": 300, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 4001001004, + "version": 1, + "versionNonce": 5002002004, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 300] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "object-3", + "type": "rectangle", + "x": 550, + "y": 100, + "width": 120, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0f0c0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": null, + "seed": 4001001005, + "version": 1, + "versionNonce": 5002002005, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "Database", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "id": "lifeline-3", + "type": "line", + "x": 610, + "y": 150, + "width": 0, + "height": 300, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 4001001006, + "version": 1, + "versionNonce": 5002002006, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [0, 300] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "message-1", + "type": "arrow", + "x": 210, + "y": 200, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": { "type": 2 }, + "seed": 4001001007, + "version": 1, + "versionNonce": 5002002007, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [200, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "message-label-1", + "type": "text", + "x": 250, + "y": 180, + "width": 120, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 4001001008, + "version": 1, + "versionNonce": 5002002008, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "1: request()", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "activation-1", + "type": "rectangle", + "x": 405, + "y": 200, + "width": 10, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd43b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": null, + "seed": 4001001009, + "version": 1, + "versionNonce": 5002002009, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false + }, + { + "id": "message-2", + "type": "arrow", + "x": 415, + "y": 230, + "width": 195, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": { "type": 2 }, + "seed": 4001001010, + "version": 1, + "versionNonce": 5002002010, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [195, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "message-label-2", + "type": "text", + "x": 450, + "y": 210, + "width": 120, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a10", + "roundness": null, + "seed": 4001001011, + "version": 1, + "versionNonce": 5002002011, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "2: query()", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "return-message-1", + "type": "arrow", + "x": 610, + "y": 250, + "width": 195, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a11", + "roundness": { "type": 2 }, + "seed": 4001001012, + "version": 1, + "versionNonce": 5002002012, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [-195, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "return-label-1", + "type": "text", + "x": 450, + "y": 255, + "width": 120, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a12", + "roundness": null, + "seed": 4001001013, + "version": 1, + "versionNonce": 5002002013, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "3: result", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "id": "return-message-2", + "type": "arrow", + "x": 410, + "y": 280, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a13", + "roundness": { "type": 2 }, + "seed": 4001001014, + "version": 1, + "versionNonce": 5002002014, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "points": [ + [0, 0], + [-200, 0] + ], + "startBinding": null, + "endBinding": null + }, + { + "id": "return-label-2", + "type": "text", + "x": 250, + "y": 285, + "width": 120, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a14", + "roundness": null, + "seed": 4001001015, + "version": 1, + "versionNonce": 5002002015, + "isDeleted": false, + "boundElements": null, + "updated": 1706659200000, + "link": null, + "locked": false, + "text": "4: response", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/skills/monorepo-management/SKILL.md b/skills/monorepo-management/SKILL.md new file mode 100644 index 000000000..4c549eb63 --- /dev/null +++ b/skills/monorepo-management/SKILL.md @@ -0,0 +1,623 @@ +--- +name: monorepo-management +description: Master monorepo management with Turborepo, Nx, and pnpm workspaces to build efficient, scalable multi-package repositories with optimized builds and dependency management. Use when setting up monorepos, optimizing builds, or managing shared dependencies. +--- + +# Monorepo Management + +Build efficient, scalable monorepos that enable code sharing, consistent tooling, and atomic changes across multiple packages and applications. + +## When to Use This Skill + +- Setting up new monorepo projects +- Migrating from multi-repo to monorepo +- Optimizing build and test performance +- Managing shared dependencies +- Implementing code sharing strategies +- Setting up CI/CD for monorepos +- Versioning and publishing packages +- Debugging monorepo-specific issues + +## Core Concepts + +### 1. Why Monorepos? + +**Advantages:** + +- Shared code and dependencies +- Atomic commits across projects +- Consistent tooling and standards +- Easier refactoring +- Simplified dependency management +- Better code visibility + +**Challenges:** + +- Build performance at scale +- CI/CD complexity +- Access control +- Large Git repository + +### 2. Monorepo Tools + +**Package Managers:** + +- pnpm workspaces (recommended) +- npm workspaces +- Yarn workspaces + +**Build Systems:** + +- Turborepo (recommended for most) +- Nx (feature-rich, complex) +- Lerna (older, maintenance mode) + +## Turborepo Setup + +### Initial Setup + +```bash +# Create new monorepo +npx create-turbo@latest my-monorepo +cd my-monorepo + +# Structure: +# apps/ +# web/ - Next.js app +# docs/ - Documentation site +# packages/ +# ui/ - Shared UI components +# config/ - Shared configurations +# tsconfig/ - Shared TypeScript configs +# turbo.json - Turborepo configuration +# package.json - Root package.json +``` + +### Configuration + +```json +// turbo.json +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["**/.env.*local"], + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", ".next/**", "!.next/cache/**"] + }, + "test": { + "dependsOn": ["build"], + "outputs": ["coverage/**"] + }, + "lint": { + "outputs": [] + }, + "dev": { + "cache": false, + "persistent": true + }, + "type-check": { + "dependsOn": ["^build"], + "outputs": [] + } + } +} +``` + +```json +// package.json (root) +{ + "name": "my-monorepo", + "private": true, + "workspaces": ["apps/*", "packages/*"], + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev", + "test": "turbo run test", + "lint": "turbo run lint", + "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "clean": "turbo run clean && rm -rf node_modules" + }, + "devDependencies": { + "turbo": "^1.10.0", + "prettier": "^3.0.0", + "typescript": "^5.0.0" + }, + "packageManager": "pnpm@8.0.0" +} +``` + +### Package Structure + +```json +// packages/ui/package.json +{ + "name": "@repo/ui", + "version": "0.0.0", + "private": true, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./button": { + "import": "./dist/button.js", + "types": "./dist/button.d.ts" + } + }, + "scripts": { + "build": "tsup src/index.ts --format esm,cjs --dts", + "dev": "tsup src/index.ts --format esm,cjs --dts --watch", + "lint": "eslint src/", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@repo/tsconfig": "workspace:*", + "tsup": "^7.0.0", + "typescript": "^5.0.0" + }, + "dependencies": { + "react": "^18.2.0" + } +} +``` + +## pnpm Workspaces + +### Setup + +```yaml +# pnpm-workspace.yaml +packages: + - "apps/*" + - "packages/*" + - "tools/*" +``` + +```json +// .npmrc +# Hoist shared dependencies +shamefully-hoist=true + +# Strict peer dependencies +auto-install-peers=true +strict-peer-dependencies=true + +# Performance +store-dir=~/.pnpm-store +``` + +### Dependency Management + +```bash +# Install dependency in specific package +pnpm add react --filter @repo/ui +pnpm add -D typescript --filter @repo/ui + +# Install workspace dependency +pnpm add @repo/ui --filter web + +# Install in all packages +pnpm add -D eslint -w + +# Update all dependencies +pnpm update -r + +# Remove dependency +pnpm remove react --filter @repo/ui +``` + +### Scripts + +```bash +# Run script in specific package +pnpm --filter web dev +pnpm --filter @repo/ui build + +# Run in all packages +pnpm -r build +pnpm -r test + +# Run in parallel +pnpm -r --parallel dev + +# Filter by pattern +pnpm --filter "@repo/*" build +pnpm --filter "...web" build # Build web and dependencies +``` + +## Nx Monorepo + +### Setup + +```bash +# Create Nx monorepo +npx create-nx-workspace@latest my-org + +# Generate applications +nx generate @nx/react:app my-app +nx generate @nx/next:app my-next-app + +# Generate libraries +nx generate @nx/react:lib ui-components +nx generate @nx/js:lib utils +``` + +### Configuration + +```json +// nx.json +{ + "extends": "nx/presets/npm.json", + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "targetDefaults": { + "build": { + "dependsOn": ["^build"], + "inputs": ["production", "^production"], + "cache": true + }, + "test": { + "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], + "cache": true + }, + "lint": { + "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], + "cache": true + } + }, + "namedInputs": { + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "production": [ + "default", + "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", + "!{projectRoot}/tsconfig.spec.json" + ], + "sharedGlobals": [] + } +} +``` + +### Running Tasks + +```bash +# Run task for specific project +nx build my-app +nx test ui-components +nx lint utils + +# Run for affected projects +nx affected:build +nx affected:test --base=main + +# Visualize dependencies +nx graph + +# Run in parallel +nx run-many --target=build --all --parallel=3 +``` + +## Shared Configurations + +### TypeScript Configuration + +```json +// packages/tsconfig/base.json +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "incremental": true, + "declaration": true + }, + "exclude": ["node_modules"] +} + +// packages/tsconfig/react.json +{ + "extends": "./base.json", + "compilerOptions": { + "jsx": "react-jsx", + "lib": ["ES2022", "DOM", "DOM.Iterable"] + } +} + +// apps/web/tsconfig.json +{ + "extends": "@repo/tsconfig/react.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} +``` + +### ESLint Configuration + +```javascript +// packages/config/eslint-preset.js +module.exports = { + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", + "prettier", + ], + plugins: ["@typescript-eslint", "react", "react-hooks"], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 2022, + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + settings: { + react: { + version: "detect", + }, + }, + rules: { + "@typescript-eslint/no-unused-vars": "error", + "react/react-in-jsx-scope": "off", + }, +}; + +// apps/web/.eslintrc.js +module.exports = { + extends: ["@repo/config/eslint-preset"], + rules: { + // App-specific rules + }, +}; +``` + +## Code Sharing Patterns + +### Pattern 1: Shared UI Components + +```typescript +// packages/ui/src/button.tsx +import * as React from 'react'; + +export interface ButtonProps { + variant?: 'primary' | 'secondary'; + children: React.ReactNode; + onClick?: () => void; +} + +export function Button({ variant = 'primary', children, onClick }: ButtonProps) { + return ( + + ); +} + +// packages/ui/src/index.ts +export { Button, type ButtonProps } from './button'; +export { Input, type InputProps } from './input'; + +// apps/web/src/app.tsx +import { Button } from '@repo/ui'; + +export function App() { + return ; +} +``` + +### Pattern 2: Shared Utilities + +```typescript +// packages/utils/src/string.ts +export function capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +export function truncate(str: string, length: number): string { + return str.length > length ? str.slice(0, length) + "..." : str; +} + +// packages/utils/src/index.ts +export * from "./string"; +export * from "./array"; +export * from "./date"; + +// Usage in apps +import { capitalize, truncate } from "@repo/utils"; +``` + +### Pattern 3: Shared Types + +```typescript +// packages/types/src/user.ts +export interface User { + id: string; + email: string; + name: string; + role: "admin" | "user"; +} + +export interface CreateUserInput { + email: string; + name: string; + password: string; +} + +// Used in both frontend and backend +import type { User, CreateUserInput } from "@repo/types"; +``` + +## Build Optimization + +### Turborepo Caching + +```json +// turbo.json +{ + "pipeline": { + "build": { + // Build depends on dependencies being built first + "dependsOn": ["^build"], + + // Cache these outputs + "outputs": ["dist/**", ".next/**"], + + // Cache based on these inputs (default: all files) + "inputs": ["src/**/*.tsx", "src/**/*.ts", "package.json"] + }, + "test": { + // Run tests in parallel, don't depend on build + "cache": true, + "outputs": ["coverage/**"] + } + } +} +``` + +### Remote Caching + +```bash +# Turborepo Remote Cache (Vercel) +npx turbo login +npx turbo link + +# Custom remote cache +# turbo.json +{ + "remoteCache": { + "signature": true, + "enabled": true + } +} +``` + +## CI/CD for Monorepos + +### GitHub Actions + +```yaml +# .github/workflows/ci.yml +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # For Nx affected commands + + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm turbo run build + + - name: Test + run: pnpm turbo run test + + - name: Lint + run: pnpm turbo run lint + + - name: Type check + run: pnpm turbo run type-check +``` + +### Deploy Affected Only + +```yaml +# Deploy only changed apps +- name: Deploy affected apps + run: | + if pnpm nx affected:apps --base=origin/main --head=HEAD | grep -q "web"; then + echo "Deploying web app" + pnpm --filter web deploy + fi +``` + +## Best Practices + +1. **Consistent Versioning**: Lock dependency versions across workspace +2. **Shared Configs**: Centralize ESLint, TypeScript, Prettier configs +3. **Dependency Graph**: Keep it acyclic, avoid circular dependencies +4. **Cache Effectively**: Configure inputs/outputs correctly +5. **Type Safety**: Share types between frontend/backend +6. **Testing Strategy**: Unit tests in packages, E2E in apps +7. **Documentation**: README in each package +8. **Release Strategy**: Use changesets for versioning + +## Common Pitfalls + +- **Circular Dependencies**: A depends on B, B depends on A +- **Phantom Dependencies**: Using deps not in package.json +- **Incorrect Cache Inputs**: Missing files in Turborepo inputs +- **Over-Sharing**: Sharing code that should be separate +- **Under-Sharing**: Duplicating code across packages +- **Large Monorepos**: Without proper tooling, builds slow down + +## Publishing Packages + +```bash +# Using Changesets +pnpm add -Dw @changesets/cli +pnpm changeset init + +# Create changeset +pnpm changeset + +# Version packages +pnpm changeset version + +# Publish +pnpm changeset publish +``` + +```yaml +# .github/workflows/release.yml +- name: Create Release Pull Request or Publish + uses: changesets/action@v1 + with: + publish: pnpm release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} +``` + +## Resources + +- **references/turborepo-guide.md**: Comprehensive Turborepo documentation +- **references/nx-guide.md**: Nx monorepo patterns +- **references/pnpm-workspaces.md**: pnpm workspace features +- **assets/monorepo-checklist.md**: Setup checklist +- **assets/migration-guide.md**: Multi-repo to monorepo migration +- **scripts/dependency-graph.ts**: Visualize package dependencies