{"owner":"Jeffallan","repo":"claude-skills","hasSkills":true,"totalSkillsCount":441,"totalTokensCount":945261,"categories":["marketplace","plugin-manifest","claude-rule","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":[".claude-plugin/marketplace.json",".claude-plugin/plugin.json","CLAUDE.md","skills/angular-architect/SKILL.md","skills/angular-architect/references/components.md","skills/angular-architect/references/ngrx.md","skills/angular-architect/references/routing.md","skills/angular-architect/references/rxjs.md","skills/angular-architect/references/testing.md","skills/api-designer/SKILL.md","skills/api-designer/references/error-handling.md","skills/api-designer/references/openapi.md","skills/api-designer/references/pagination.md","skills/api-designer/references/rest-patterns.md","skills/api-designer/references/versioning.md","skills/architecture-designer/SKILL.md","skills/architecture-designer/references/adr-template.md","skills/architecture-designer/references/architecture-patterns.md","skills/architecture-designer/references/database-selection.md","skills/architecture-designer/references/nfr-checklist.md","skills/architecture-designer/references/system-design.md","skills/atlassian-mcp/SKILL.md","skills/atlassian-mcp/references/authentication-patterns.md","skills/atlassian-mcp/references/common-workflows.md","skills/atlassian-mcp/references/confluence-operations.md","skills/atlassian-mcp/references/jira-queries.md","skills/atlassian-mcp/references/mcp-server-setup.md","skills/chaos-engineer/SKILL.md","skills/chaos-engineer/references/chaos-tools.md","skills/chaos-engineer/references/experiment-design.md","skills/chaos-engineer/references/game-days.md","skills/chaos-engineer/references/infrastructure-chaos.md","skills/chaos-engineer/references/kubernetes-chaos.md","skills/cli-developer/SKILL.md","skills/cli-developer/references/design-patterns.md","skills/cli-developer/references/go-cli.md","skills/cli-developer/references/node-cli.md","skills/cli-developer/references/python-cli.md","skills/cli-developer/references/ux-patterns.md","skills/cloud-architect/SKILL.md","skills/cloud-architect/references/aws.md","skills/cloud-architect/references/azure.md","skills/cloud-architect/references/cost.md","skills/cloud-architect/references/gcp.md","skills/cloud-architect/references/multi-cloud.md","skills/code-documenter/SKILL.md","skills/code-documenter/references/api-docs-fastapi-django.md","skills/code-documenter/references/api-docs-nestjs-express.md","skills/code-documenter/references/coverage-reports.md","skills/code-documenter/references/documentation-systems.md","skills/code-documenter/references/interactive-api-docs.md","skills/code-documenter/references/python-docstrings.md","skills/code-documenter/references/typescript-jsdoc.md","skills/code-documenter/references/user-guides-tutorials.md","skills/code-reviewer/SKILL.md","skills/code-reviewer/references/common-issues.md","skills/code-reviewer/references/feedback-examples.md","skills/code-reviewer/references/receiving-feedback.md","skills/code-reviewer/references/report-template.md","skills/code-reviewer/references/review-checklist.md","skills/code-reviewer/references/spec-compliance-review.md","skills/cpp-pro/SKILL.md","skills/cpp-pro/references/build-tooling.md","skills/cpp-pro/references/concurrency.md","skills/cpp-pro/references/memory-performance.md","skills/cpp-pro/references/modern-cpp.md","skills/cpp-pro/references/templates.md","skills/csharp-developer/SKILL.md","skills/csharp-developer/references/aspnet-core.md","skills/csharp-developer/references/blazor.md","skills/csharp-developer/references/entity-framework.md","skills/csharp-developer/references/modern-csharp.md","skills/csharp-developer/references/performance.md","skills/database-optimizer/SKILL.md","skills/database-optimizer/references/index-strategies.md","skills/database-optimizer/references/monitoring-analysis.md","skills/database-optimizer/references/mysql-tuning.md","skills/database-optimizer/references/postgresql-tuning.md","skills/database-optimizer/references/query-optimization.md","skills/debugging-wizard/SKILL.md","skills/debugging-wizard/references/common-patterns.md","skills/debugging-wizard/references/debugging-tools.md","skills/debugging-wizard/references/quick-fixes.md","skills/debugging-wizard/references/strategies.md","skills/debugging-wizard/references/systematic-debugging.md","skills/devops-engineer/SKILL.md","skills/devops-engineer/references/deployment-strategies.md","skills/devops-engineer/references/docker-patterns.md","skills/devops-engineer/references/github-actions.md","skills/devops-engineer/references/gitlab-ci.md","skills/devops-engineer/references/incident-response.md","skills/devops-engineer/references/kubernetes.md","skills/devops-engineer/references/platform-engineering.md","skills/devops-engineer/references/release-automation.md","skills/devops-engineer/references/terraform-iac.md","skills/django-expert/SKILL.md","skills/django-expert/references/authentication.md","skills/django-expert/references/drf-serializers.md","skills/django-expert/references/models-orm.md","skills/django-expert/references/testing-django.md","skills/django-expert/references/viewsets-views.md","skills/django-storages-s3/SKILL.md","skills/django-storages-s3/references/configuration.md","skills/django-storages-s3/references/custom-backends.md","skills/django-storages-s3/references/presigned-urls.md","skills/django-storages-s3/references/testing-storages.md","skills/dotnet-core-expert/SKILL.md","skills/dotnet-core-expert/references/authentication.md","skills/dotnet-core-expert/references/clean-architecture.md","skills/dotnet-core-expert/references/cloud-native.md","skills/dotnet-core-expert/references/entity-framework.md","skills/dotnet-core-expert/references/minimal-apis.md","skills/embedded-systems/SKILL.md","skills/embedded-systems/references/communication-protocols.md","skills/embedded-systems/references/memory-optimization.md","skills/embedded-systems/references/microcontroller-programming.md","skills/embedded-systems/references/power-optimization.md","skills/embedded-systems/references/rtos-patterns.md","skills/fastapi-expert/SKILL.md","skills/fastapi-expert/references/async-sqlalchemy.md","skills/fastapi-expert/references/authentication.md","skills/fastapi-expert/references/endpoints-routing.md","skills/fastapi-expert/references/migration-from-django.md","skills/fastapi-expert/references/pydantic-v2.md","skills/fastapi-expert/references/testing-async.md","skills/feature-forge/SKILL.md","skills/feature-forge/references/acceptance-criteria.md","skills/feature-forge/references/ears-syntax.md","skills/feature-forge/references/interview-questions.md","skills/feature-forge/references/pre-discovery-subagents.md","skills/feature-forge/references/specification-template.md","skills/fine-tuning-expert/SKILL.md","skills/fine-tuning-expert/references/dataset-preparation.md","skills/fine-tuning-expert/references/deployment-optimization.md","skills/fine-tuning-expert/references/evaluation-metrics.md","skills/fine-tuning-expert/references/hyperparameter-tuning.md","skills/fine-tuning-expert/references/lora-peft.md","skills/flutter-expert/SKILL.md","skills/flutter-expert/references/bloc-state.md","skills/flutter-expert/references/gorouter-navigation.md","skills/flutter-expert/references/performance.md","skills/flutter-expert/references/project-structure.md","skills/flutter-expert/references/riverpod-state.md","skills/flutter-expert/references/widget-patterns.md","skills/fullstack-guardian/SKILL.md","skills/fullstack-guardian/references/api-design-standards.md","skills/fullstack-guardian/references/architecture-decisions.md","skills/fullstack-guardian/references/backend-patterns.md","skills/fullstack-guardian/references/common-patterns.md","skills/fullstack-guardian/references/deliverables-checklist.md","skills/fullstack-guardian/references/design-template.md","skills/fullstack-guardian/references/error-handling.md","skills/fullstack-guardian/references/frontend-patterns.md","skills/fullstack-guardian/references/integration-patterns.md","skills/fullstack-guardian/references/security-checklist.md","skills/game-developer/SKILL.md","skills/game-developer/references/ecs-patterns.md","skills/game-developer/references/multiplayer-networking.md","skills/game-developer/references/performance-optimization.md","skills/game-developer/references/unity-patterns.md","skills/game-developer/references/unreal-cpp.md","skills/golang-pro/SKILL.md","skills/golang-pro/references/concurrency.md","skills/golang-pro/references/generics.md","skills/golang-pro/references/interfaces.md","skills/golang-pro/references/project-structure.md","skills/golang-pro/references/testing.md","skills/graphql-architect/SKILL.md","skills/graphql-architect/references/federation.md","skills/graphql-architect/references/migration-from-rest.md","skills/graphql-architect/references/resolvers.md","skills/graphql-architect/references/schema-design.md","skills/graphql-architect/references/security.md","skills/graphql-architect/references/subscriptions.md","skills/java-architect/SKILL.md","skills/java-architect/references/jpa-optimization.md","skills/java-architect/references/reactive-webflux.md","skills/java-architect/references/spring-boot-setup.md","skills/java-architect/references/spring-security.md","skills/java-architect/references/testing-patterns.md","skills/javascript-pro/SKILL.md","skills/javascript-pro/references/async-patterns.md","skills/javascript-pro/references/browser-apis.md","skills/javascript-pro/references/modern-syntax.md","skills/javascript-pro/references/modules.md","skills/javascript-pro/references/node-essentials.md","skills/kotlin-specialist/SKILL.md","skills/kotlin-specialist/references/android-compose.md","skills/kotlin-specialist/references/coroutines-flow.md","skills/kotlin-specialist/references/dsl-idioms.md","skills/kotlin-specialist/references/ktor-server.md","skills/kotlin-specialist/references/multiplatform-kmp.md","skills/kubernetes-specialist/SKILL.md","skills/kubernetes-specialist/references/configuration.md","skills/kubernetes-specialist/references/cost-optimization.md","skills/kubernetes-specialist/references/custom-operators.md","skills/kubernetes-specialist/references/gitops.md","skills/kubernetes-specialist/references/helm-charts.md","skills/kubernetes-specialist/references/multi-cluster.md","skills/kubernetes-specialist/references/networking.md","skills/kubernetes-specialist/references/service-mesh.md","skills/kubernetes-specialist/references/storage.md","skills/kubernetes-specialist/references/troubleshooting.md","skills/kubernetes-specialist/references/workloads.md","skills/laravel-specialist/SKILL.md","skills/laravel-specialist/references/eloquent.md","skills/laravel-specialist/references/livewire.md","skills/laravel-specialist/references/queues.md","skills/laravel-specialist/references/routing.md","skills/laravel-specialist/references/testing.md","skills/legacy-modernizer/SKILL.md","skills/legacy-modernizer/references/legacy-testing.md","skills/legacy-modernizer/references/migration-strategies.md","skills/legacy-modernizer/references/refactoring-patterns.md","skills/legacy-modernizer/references/strangler-fig-pattern.md","skills/legacy-modernizer/references/system-assessment.md","skills/mcp-developer/SKILL.md","skills/mcp-developer/references/protocol.md","skills/mcp-developer/references/python-sdk.md","skills/mcp-developer/references/resources.md","skills/mcp-developer/references/tools.md","skills/mcp-developer/references/typescript-sdk.md","skills/microservices-architect/SKILL.md","skills/microservices-architect/references/communication.md","skills/microservices-architect/references/data.md","skills/microservices-architect/references/decomposition.md","skills/microservices-architect/references/observability.md","skills/microservices-architect/references/patterns.md","skills/ml-pipeline/SKILL.md","skills/ml-pipeline/references/experiment-tracking.md","skills/ml-pipeline/references/feature-engineering.md","skills/ml-pipeline/references/model-validation.md","skills/ml-pipeline/references/pipeline-orchestration.md","skills/ml-pipeline/references/training-pipelines.md","skills/monitoring-expert/SKILL.md","skills/monitoring-expert/references/alerting-rules.md","skills/monitoring-expert/references/application-profiling.md","skills/monitoring-expert/references/capacity-planning.md","skills/monitoring-expert/references/dashboards.md","skills/monitoring-expert/references/opentelemetry.md","skills/monitoring-expert/references/performance-testing.md","skills/monitoring-expert/references/prometheus-metrics.md","skills/monitoring-expert/references/structured-logging.md","skills/nestjs-expert/SKILL.md","skills/nestjs-expert/references/authentication.md","skills/nestjs-expert/references/controllers-routing.md","skills/nestjs-expert/references/dtos-validation.md","skills/nestjs-expert/references/migration-from-express.md","skills/nestjs-expert/references/services-di.md","skills/nestjs-expert/references/testing-patterns.md","skills/nextjs-developer/SKILL.md","skills/nextjs-developer/references/app-router.md","skills/nextjs-developer/references/data-fetching.md","skills/nextjs-developer/references/deployment.md","skills/nextjs-developer/references/server-actions.md","skills/nextjs-developer/references/server-components.md","skills/pandas-pro/SKILL.md","skills/pandas-pro/references/aggregation-groupby.md","skills/pandas-pro/references/data-cleaning.md","skills/pandas-pro/references/dataframe-operations.md","skills/pandas-pro/references/merging-joining.md","skills/pandas-pro/references/performance-optimization.md","skills/php-pro/SKILL.md","skills/php-pro/references/async-patterns.md","skills/php-pro/references/laravel-patterns.md","skills/php-pro/references/modern-php-features.md","skills/php-pro/references/symfony-patterns.md","skills/php-pro/references/testing-quality.md","skills/playwright-expert/SKILL.md","skills/playwright-expert/references/api-mocking.md","skills/playwright-expert/references/configuration.md","skills/playwright-expert/references/debugging-flaky.md","skills/playwright-expert/references/page-object-model.md","skills/playwright-expert/references/selectors-locators.md","skills/postgres-pro/SKILL.md","skills/postgres-pro/references/extensions.md","skills/postgres-pro/references/jsonb.md","skills/postgres-pro/references/maintenance.md","skills/postgres-pro/references/performance.md","skills/postgres-pro/references/replication.md","skills/prompt-engineer/SKILL.md","skills/prompt-engineer/references/context-management.md","skills/prompt-engineer/references/evaluation-frameworks.md","skills/prompt-engineer/references/prompt-optimization.md","skills/prompt-engineer/references/prompt-patterns.md","skills/prompt-engineer/references/structured-outputs.md","skills/prompt-engineer/references/system-prompts.md","skills/python-pro/SKILL.md","skills/python-pro/references/async-patterns.md","skills/python-pro/references/packaging.md","skills/python-pro/references/standard-library.md","skills/python-pro/references/testing.md","skills/python-pro/references/type-system.md","skills/rag-architect/SKILL.md","skills/rag-architect/references/chunking-strategies.md","skills/rag-architect/references/embedding-models.md","skills/rag-architect/references/rag-evaluation.md","skills/rag-architect/references/retrieval-optimization.md","skills/rag-architect/references/vector-databases.md","skills/rails-expert/SKILL.md","skills/rails-expert/references/active-record.md","skills/rails-expert/references/api-development.md","skills/rails-expert/references/background-jobs.md","skills/rails-expert/references/hotwire-turbo.md","skills/rails-expert/references/rspec-testing.md","skills/react-expert/SKILL.md","skills/react-expert/references/hooks-patterns.md","skills/react-expert/references/migration-class-to-modern.md","skills/react-expert/references/performance.md","skills/react-expert/references/react-19-features.md","skills/react-expert/references/server-components.md","skills/react-expert/references/state-management.md","skills/react-expert/references/testing-react.md","skills/react-native-expert/SKILL.md","skills/react-native-expert/references/expo-router.md","skills/react-native-expert/references/list-optimization.md","skills/react-native-expert/references/platform-handling.md","skills/react-native-expert/references/project-structure.md","skills/react-native-expert/references/storage-hooks.md","skills/rust-engineer/SKILL.md","skills/rust-engineer/references/async.md","skills/rust-engineer/references/error-handling.md","skills/rust-engineer/references/ownership.md","skills/rust-engineer/references/testing.md","skills/rust-engineer/references/traits.md","skills/salesforce-developer/SKILL.md","skills/salesforce-developer/references/apex-development.md","skills/salesforce-developer/references/deployment-devops.md","skills/salesforce-developer/references/integration-patterns.md","skills/salesforce-developer/references/lightning-web-components.md","skills/salesforce-developer/references/soql-sosl.md","skills/secure-code-guardian/SKILL.md","skills/secure-code-guardian/references/authentication.md","skills/secure-code-guardian/references/input-validation.md","skills/secure-code-guardian/references/owasp-prevention.md","skills/secure-code-guardian/references/security-headers.md","skills/secure-code-guardian/references/xss-csrf.md","skills/security-reviewer/SKILL.md","skills/security-reviewer/references/infrastructure-security.md","skills/security-reviewer/references/penetration-testing.md","skills/security-reviewer/references/report-template.md","skills/security-reviewer/references/sast-tools.md","skills/security-reviewer/references/secret-scanning.md","skills/security-reviewer/references/vulnerability-patterns.md","skills/shopify-expert/SKILL.md","skills/shopify-expert/references/app-development.md","skills/shopify-expert/references/checkout-customization.md","skills/shopify-expert/references/liquid-templating.md","skills/shopify-expert/references/performance-optimization.md","skills/shopify-expert/references/storefront-api.md","skills/spark-engineer/SKILL.md","skills/spark-engineer/references/partitioning-caching.md","skills/spark-engineer/references/performance-tuning.md","skills/spark-engineer/references/rdd-operations.md","skills/spark-engineer/references/spark-sql-dataframes.md","skills/spark-engineer/references/streaming-patterns.md","skills/spec-miner/SKILL.md","skills/spec-miner/references/analysis-checklist.md","skills/spec-miner/references/analysis-process.md","skills/spec-miner/references/ears-format.md","skills/spec-miner/references/specification-template.md","skills/spring-boot-engineer/SKILL.md","skills/spring-boot-engineer/references/cloud.md","skills/spring-boot-engineer/references/data.md","skills/spring-boot-engineer/references/security.md","skills/spring-boot-engineer/references/testing.md","skills/spring-boot-engineer/references/web.md","skills/sql-pro/SKILL.md","skills/sql-pro/references/database-design.md","skills/sql-pro/references/dialect-differences.md","skills/sql-pro/references/optimization.md","skills/sql-pro/references/query-patterns.md","skills/sql-pro/references/window-functions.md","skills/sre-engineer/SKILL.md","skills/sre-engineer/references/automation-toil.md","skills/sre-engineer/references/error-budget-policy.md","skills/sre-engineer/references/incident-chaos.md","skills/sre-engineer/references/monitoring-alerting.md","skills/sre-engineer/references/slo-sli-management.md","skills/swift-expert/SKILL.md","skills/swift-expert/references/async-concurrency.md","skills/swift-expert/references/memory-performance.md","skills/swift-expert/references/protocol-oriented.md","skills/swift-expert/references/swiftui-patterns.md","skills/swift-expert/references/testing-patterns.md","skills/terraform-engineer/SKILL.md","skills/terraform-engineer/references/best-practices.md","skills/terraform-engineer/references/module-patterns.md","skills/terraform-engineer/references/providers.md","skills/terraform-engineer/references/state-management.md","skills/terraform-engineer/references/testing.md","skills/test-master/SKILL.md","skills/test-master/references/automation-frameworks.md","skills/test-master/references/e2e-testing.md","skills/test-master/references/integration-testing.md","skills/test-master/references/performance-testing.md","skills/test-master/references/qa-methodology.md","skills/test-master/references/security-testing.md","skills/test-master/references/tdd-iron-laws.md","skills/test-master/references/test-reports.md","skills/test-master/references/testing-anti-patterns.md","skills/test-master/references/unit-testing.md","skills/the-fool/SKILL.md","skills/the-fool/references/dialectic-synthesis.md","skills/the-fool/references/evidence-audit.md","skills/the-fool/references/mode-selection-guide.md","skills/the-fool/references/pre-mortem-analysis.md","skills/the-fool/references/red-team-adversarial.md","skills/the-fool/references/socratic-questioning.md","skills/typescript-pro/SKILL.md","skills/typescript-pro/references/advanced-types.md","skills/typescript-pro/references/configuration.md","skills/typescript-pro/references/patterns.md","skills/typescript-pro/references/type-guards.md","skills/typescript-pro/references/utility-types.md","skills/vue-expert-js/SKILL.md","skills/vue-expert-js/references/component-architecture.md","skills/vue-expert-js/references/composables-patterns.md","skills/vue-expert-js/references/jsdoc-typing.md","skills/vue-expert-js/references/state-management.md","skills/vue-expert-js/references/testing-patterns.md","skills/vue-expert/SKILL.md","skills/vue-expert/references/build-tooling.md","skills/vue-expert/references/components.md","skills/vue-expert/references/composition-api.md","skills/vue-expert/references/mobile-hybrid.md","skills/vue-expert/references/nuxt.md","skills/vue-expert/references/state-management.md","skills/vue-expert/references/typescript.md","skills/websocket-engineer/SKILL.md","skills/websocket-engineer/references/alternatives.md","skills/websocket-engineer/references/patterns.md","skills/websocket-engineer/references/protocol.md","skills/websocket-engineer/references/scaling.md","skills/websocket-engineer/references/security.md","skills/wordpress-pro/SKILL.md","skills/wordpress-pro/references/gutenberg-blocks.md","skills/wordpress-pro/references/hooks-filters.md","skills/wordpress-pro/references/performance-security.md","skills/wordpress-pro/references/plugin-architecture.md","skills/wordpress-pro/references/theme-development.md"],"skills":{".claude-plugin/marketplace.json":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=.claude-plugin%2Fmarketplace.json -->",".claude-plugin/plugin.json":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=.claude-plugin%2Fplugin.json -->","CLAUDE.md":"# Claude Skills Project Configuration\n\n> This file governs Claude's behavior when working on the claude-skills repository.\n\n---\n\n## Skill Authorship Standards\n\nSkills follow the [Agent Skills specification](https://agentskills.io/specification). This section covers project-specific conventions that go beyond the base spec.\n\n### The Description Trap\n\n**Critical:** Never put process steps or workflow sequences in descriptions. When descriptions contain step-by-step instructions, agents follow the brief description instead of reading the full skill content. This defeats the purpose of detailed skills.\n\nBrief capability statements (what it does) and trigger conditions (when to use it) are both appropriate. Process steps (how it works) are not.\n\n**BAD - Process steps in description:**\n```yaml\ndescription: Use for debugging. First investigate root cause, then analyze\npatterns, test hypotheses, and implement fixes with tests.\n```\n\n**GOOD - Capability + trigger:**\n```yaml\ndescription: Diagnoses bugs through root cause analysis and pattern matching.\nUse when encountering errors or unexpected behavior requiring investigation.\n```\n\n**Format:** `[Brief capability statement]. Use when [triggering conditions].`\n\nDescriptions tell WHAT the skill does and WHEN to use it. The SKILL.md body tells HOW.\n\n---\n\n### Frontmatter Requirements\n\nPer the [Agent Skills specification](https://agentskills.io/specification), only `name` and `description` are top-level required fields. Custom fields go under `metadata`.\n\n```yaml\n---\nname: skill-name-with-hyphens\ndescription: [Brief capability statement]. Use when [triggering conditions] - max 1024 chars\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.0.0\"\n  domain: frontend\n  triggers: keyword1, keyword2, keyword3\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: fullstack-guardian, test-master, devops-engineer\n---\n```\n\n**Top-level fields (spec-defined):**\n- `name`: Letters, numbers, and hyphens only (no parentheses or special characters)\n- `description`: Maximum 1024 characters. Capability statement + trigger conditions. No process steps.\n- `license`: Always `MIT` for this project\n- `allowed-tools`: Space-delimited tool list (only on skills that restrict tools)\n\n**Metadata fields (project-specific):**\n- `author`: GitHub profile URL of the skill author\n- `company`: Optional. URL of the author's company (`https://synergetic.solutions` on skills authored by @jeffallan); omit for contributed skills unless the contributor supplies one\n- `version`: Semantic version string (quoted, e.g., `\"1.0.0\"`)\n- `domain`: Category from the domain list below\n- `triggers`: Comma-separated searchable keywords\n- `role`: `specialist` | `expert` | `architect` | `engineer`\n- `scope`: `implementation` | `review` | `design` | `system-design` | `testing` | `analysis` | `infrastructure` | `optimization` | `architecture`\n- `output-format`: `code` | `document` | `report` | `architecture` | `specification` | `schema` | `manifests` | `analysis` | `analysis-and-code`\n- `related-skills`: Comma-separated skill directory names (e.g., `fullstack-guardian, test-master`). Must resolve to existing skill directories.\n\n**Domain values:**\n`language` · `backend` · `frontend` · `infrastructure` · `api-architecture` · `quality` · `devops` · `security` · `data-ml` · `platform` · `specialized` · `workflow`\n\n---\n\n### Reference File Standards\n\nReference files follow the [Agent Skills specification](https://agentskills.io/specification). No specific headers are required.\n\n**Guidelines:**\n- 100-600 lines per reference file\n- Keep files focused on a single topic\n- Complete, working code examples with TypeScript types\n- Cross-reference related skills where relevant\n- Include \"when to use\" and \"when not to use\" guidance\n- Practical patterns over theoretical explanations\n\n### Framework Idiom Principle\n\nReference files for framework-specific skills must reflect the idiomatic best practices of that framework, not generic patterns applied uniformly across all skills. If a framework provides a built-in mechanism (e.g., global error handling, middleware, dependency injection), reference examples should use it rather than duplicating that behavior manually. Each framework's conventions for error handling, architecture, and code organization take precedence over cross-project consistency.\n\n---\n\n### Documentation Backlink\n\nEvery `SKILL.md` MUST end with a single canonical Documentation link pointing back to the docs site:\n\n```\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- `{domain}` is `metadata.domain` from the frontmatter and must be one of the `domains` in `pyproject.toml` (`[tool.skill-linter]`); the docs site build fails on any other value.\n- `{skill-name}` is the skill's directory name.\n- Format is exactly markdown link syntax (`[text](url)`), not a bare URL. Strict CommonMark renderers do not auto-link bare URLs, and the line's purpose is to render as a real `<a href>` on aggregators (skills.sh, etc.) for SEO backlinks.\n\n**Why two surfaces, two behaviors:**\n\n- Aggregators consume raw `SKILL.md` from GitHub and render the line as a hyperlink. This is the SEO point.\n- The docs site itself would render this line as a self-link, which is redundant. The site's content sync (`site/src/components/site-sync/`, configured by `site/scripts/sync-content.mjs`) strips the line, and the credit line above it, at build time so the docs site, public markdown mirrors, `llms.txt`, and `llms-full.txt` never show it.\n\n**When adding or renaming a skill:** update both the directory name (which becomes `{skill-name}`) and the `metadata.domain` consistently with this URL formula, otherwise the backlink will 404.\n\n### Maintainer Credit\n\nSkills authored by @jeffallan (those with `metadata.company: https://synergetic.solutions`) carry one more line, directly above the Documentation backlink, separated by a blank line. It matches the docs site footer credit:\n\n```\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- The Documentation backlink stays the last line\n- Contributed skills do not get this line unless their `metadata.company` is set to the same URL\n- `syncSkillPages` strips it at build time alongside the Documentation backlink, so the docs site, markdown mirrors, and `llms` files never show it (the site footer shows the same credit once per page)\n\n---\n\n### Progressive Disclosure Architecture\n\n**Tier 1 - SKILL.md (~80-100 lines)**\n- Role definition and expertise level\n- When-to-use guidance (triggers)\n- Core workflow (5 steps)\n- Constraints (MUST DO / MUST NOT DO)\n- Routing table to references\n\n**Tier 2 - Reference Files (100-600 lines each)**\n- Deep technical content\n- Complete code examples\n- Edge cases and anti-patterns\n- Loaded only when context requires\n\n**Goal:** 50% token reduction through selective loading.\n\n---\n\n## Project Workflow\n\n### When Creating New Skills\n\n1. Check existing skills for overlap\n2. Write SKILL.md with capability + trigger description (no process steps)\n3. Create reference files for deep content (100+ lines)\n4. Add routing table linking topics to references\n5. Append the canonical Documentation backlink as the last line (see Documentation Backlink above)\n6. Test skill triggers with realistic prompts\n7. Add the skill to SKILLS_GUIDE.md in its category\n\n### When Modifying Skills\n\n1. Read the full current skill before editing\n2. Maintain capability + trigger description format (no process steps)\n3. Preserve progressive disclosure structure\n4. Update related cross-references\n5. Verify routing table accuracy\n\n### When Accepting Contributions\n\nEvery external contribution (issue or PR) gets the `gratitude` label, whatever its outcome.\n\nFor each PR:\n\n1. Discuss fit with project goals before any merge decision (scope, overlap with existing skills, promotional content), then verify the content\n2. Squash-merge with subject `type(scope): title (#N)`\n   - Conflicted fork PRs: squash locally (`git merge --squash`), add `Co-authored-by:` with the contributor's commit email, then close the PR with a comment pointing at the commit\n3. Record it in a separate commit, `docs(changelog): record #N <summary> under Unreleased`, adding an entry under `[Unreleased]` → Added/Changed/Fixed that ends with `(#N)` (append `closing #ISSUE` when it resolves one)\n4. If skill or reference counts changed, run `uvx skill-linter@0.2.0 stamp` in the same push, otherwise `stamp --check` fails CI on main\n5. Post-merge corrections go in their own commit with their own `### Changed` entry\n\nAt release time, credit contributors (including issue reporters) in a `### Contributors` section of the version entry:\n\n```markdown\n### Contributors\n- @handle: What they contributed (#N)\n```\n\n---\n\n## Shared Code from the Handbook\n\nThe site code under `site/src/components/site-*/`, the shared CI workflows and `.github/workflows/handbook-update.yml` come from the private handbook through Copier (`.copier-answers.yml`). Change them in the handbook, not here.\n\nThe versions of the shared site packages come from the handbook too (`personal-sites/package.json`): `astro`, `@astrojs/starlight`, `@jdevalk/astro-seo-graph`, `astro-og-canvas`, `starlight-links-validator`, the `@fontsource` fonts, `js-yaml`, `smol-toml`, `sharp`, `prettier` and `prettier-plugin-astro`. Don't bump them in `site/package.json`. Bump them in the handbook. **They reach this repo only with a handbook release**, as the update PR that `handbook-update.yml` opens, carrying the new `site/package.json` and lockfile.\n\nThe same goes for the actions in the workflows the handbook delivers (`handbook-update.yml`, `docs-site.yml`, `docs-deploy.yml`). Dependabot here skips those files (`exclude-paths`), and the handbook bumps them.\n\n## Release Checklist\n\nWhen releasing a new version, follow these steps.\n\n### 1. Update Version and Counts\n\nVersion and counts are managed through `version.json`:\n\n```json\n{\n  \"version\": \"0.4.2\",\n  \"skillCount\": 65,\n  \"workflowCount\": 9,\n  \"referenceFileCount\": 355\n}\n```\n\n**To release a new version:**\n\n1. Update the `version` field in `version.json`\n2. Stamp the counts and version:\n\n```bash\nuvx skill-linter@0.2.0 stamp\n```\n\nThis computes the counts from the filesystem (skills, per-domain skills, references, workflows), writes them to `version.json`, and stamps the version and counts into the files listed under `[[tool.skill-linter.stamp.files]]` in `pyproject.toml` (README.md, QUICKSTART.md, plugin.json, marketplace.json, the docs site and others).\n\n`uvx skill-linter@0.2.0 stamp --check` reports stale files without writing (CI runs it).\n\n### 2. Update CHANGELOG.md\n\nAdd new version entry at the top following Keep a Changelog format:\n\n```markdown\n## [X.Y.Z] - YYYY-MM-DD\n\n### Added\n- New features, skills, commands\n\n### Changed\n- Modified functionality, updated skills\n\n### Fixed\n- Bug fixes\n```\n\nAdd version comparison link at bottom:\n```markdown\n[X.Y.Z]: https://github.com/jeffallan/claude-skills/compare/vPREVIOUS...vX.Y.Z\n```\n\n### 3. Update Documentation for New/Modified Content\n\n**For new skills:**\n- Add to `SKILLS_GUIDE.md` in appropriate category\n- Add to decision trees if applicable\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For new commands:**\n- Add to `docs/WORKFLOW_COMMANDS.md`\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For modified skills/commands:**\n- Update any cross-references\n- Update SKILLS_GUIDE.md if triggers changed\n\n### 4. Generate Social Preview (conditional)\n\nThe social preview embeds skill, workflow, and reference counts but **not** the version. Only regenerate when at least one of `skillCount`, `workflowCount`, or `referenceFileCount` in `version.json` changed in this release — patch releases that only fix docs or a single skill normally do not need it.\n\nWhen regeneration is needed:\n\n```bash\nnpx -y -p puppeteer node ./assets/capture-screenshot.js\n```\n\nThis creates `assets/social-preview.png` from `assets/social-preview.html`.\n\n### 5. Validate\n\n**Critical:** Run validation before release to prevent broken skills from being published.\n\n```bash\nmake validate\n```\n\nThis runs [skill-linter](https://pypi.org/project/skill-linter/), configured in `pyproject.toml` (`[tool.skill-linter]`), and is the same check CI runs:\n\n- `skill-linter release` checks the shipped plugin tree (manifest, commands map, marketplace entry), then runs every other rule: frontmatter, names and descriptions, sections and references, Markdown syntax (unclosed fences, table separators, comments and column counts), commands, the changelog and stamped counts\n- `skill-linter stamp --check` fails when a stamped count or version is stale\n\n**Options:**\n```bash\nuvx skill-linter@0.2.0 check --skill react-expert   # One skill's rules only\nuvx skill-linter@0.2.0 check --only markdown        # One rule group\nuvx skill-linter@0.2.0 check --format json          # JSON output\nuvx skill-linter@0.2.0 rules                        # Every rule, its group and severity\n```\n\n**Exit codes:** 0 = no errors (warnings OK), 1 = errors found\n\n### 6. Final Verification\n\nAfter running validation, manually verify:\n\n```bash\n# Check no old version references remain (except historical changelog)\ngrep -r \"OLD_VERSION\" --include=\"*.md\" --include=\"*.json\" --include=\"*.html\"\n```\n\n### 7. Plugin Branch\n\nThe plugin ships from the `plugin` branch, not from `main`. Pushing the `vX.Y.Z` tag runs `.github/workflows/publish-plugin.yml`, which rebuilds that branch from `scripts/build-plugin-dist.sh` (only `.claude-plugin/plugin.json`, `skills/`, `commands/`, `references/`, `README.md`, `LICENSE`). Both the marketplace entry and the Claude plugin directory read from it.\n\n- Never edit the `plugin` branch by hand; the next publish overwrites it\n- After the tag push, confirm the workflow succeeded and the branch's latest commit names the new tag\n- A file the plugin needs at runtime must live under one of the shipped paths, or it will be missing from installs\n\n---\n\n## Attribution\n\nBehavioral patterns and process discipline adapted from:\n- **[obra/superpowers](https://github.com/obra/superpowers)** by Jesse Vincent (@obra)\n- License: MIT\n\nResearch documented in: `research/superpowers.md`\n","skills/angular-architect/SKILL.md":"---\nname: angular-architect\ndescription: Generates Angular 17+ standalone components, configures advanced routing with lazy loading and guards, implements NgRx state management, applies RxJS patterns, and optimizes bundle performance. Use when building Angular 17+ applications with standalone components or signals, setting up NgRx stores, establishing RxJS reactive patterns, performance tuning, or writing Angular tests for enterprise apps.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: frontend\n  triggers: Angular, Angular 17, standalone components, signals, RxJS, NgRx, Angular performance, Angular routing, Angular testing\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: typescript-pro, test-master\n---\n\n# Angular Architect\n\nSenior Angular architect specializing in Angular 17+ with standalone components, signals, and enterprise-grade application development.\n\n## Core Workflow\n\n1. **Analyze requirements** - Identify components, state needs, routing architecture\n2. **Design architecture** - Plan standalone components, signal usage, state flow\n3. **Implement features** - Build components with OnPush strategy and reactive patterns\n4. **Manage state** - Setup NgRx store, effects, selectors as needed; verify store hydration and action flow with Redux DevTools before proceeding\n5. **Optimize** - Apply performance best practices and bundle optimization; run `ng build --configuration production` to verify bundle size and flag regressions\n6. **Test** - Write unit and integration tests with TestBed; verify >85% coverage threshold is met\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Components | `references/components.md` | Standalone components, signals, input/output |\n| RxJS | `references/rxjs.md` | Observables, operators, subjects, error handling |\n| NgRx | `references/ngrx.md` | Store, effects, selectors, entity adapter |\n| Routing | `references/routing.md` | Router config, guards, lazy loading, resolvers |\n| Testing | `references/testing.md` | TestBed, component tests, service tests |\n\n## Key Patterns\n\n### Standalone Component with OnPush and Signals\n\n```typescript\nimport { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n  selector: 'app-user-card',\n  standalone: true,\n  imports: [CommonModule],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <div class=\"user-card\">\n      <h2>{{ fullName() }}</h2>\n      <button (click)=\"onSelect()\">Select</button>\n    </div>\n  `,\n})\nexport class UserCardComponent {\n  firstName = input.required<string>();\n  lastName = input.required<string>();\n  selected = output<string>();\n\n  fullName = computed(() => `${this.firstName()} ${this.lastName()}`);\n\n  onSelect(): void {\n    this.selected.emit(this.fullName());\n  }\n}\n```\n\n### RxJS Subscription Management with `takeUntilDestroyed`\n\n```typescript\nimport { Component, OnInit, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { UserService } from './user.service';\n\n@Component({ selector: 'app-users', standalone: true, template: `...` })\nexport class UsersComponent implements OnInit {\n  private userService = inject(UserService);\n  // DestroyRef is captured at construction time for use in ngOnInit\n  private destroyRef = inject(DestroyRef);\n\n  ngOnInit(): void {\n    this.userService.getUsers()\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe({\n        next: (users) => { /* handle */ },\n        error: (err) => console.error('Failed to load users', err),\n      });\n  }\n}\n```\n\n### NgRx Action / Reducer / Selector\n\n```typescript\n// actions\nexport const loadUsers = createAction('[Users] Load Users');\nexport const loadUsersSuccess = createAction('[Users] Load Users Success', props<{ users: User[] }>());\nexport const loadUsersFailure = createAction('[Users] Load Users Failure', props<{ error: string }>());\n\n// reducer\nexport interface UsersState { users: User[]; loading: boolean; error: string | null; }\nconst initialState: UsersState = { users: [], loading: false, error: null };\n\nexport const usersReducer = createReducer(\n  initialState,\n  on(loadUsers, (state) => ({ ...state, loading: true, error: null })),\n  on(loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),\n  on(loadUsersFailure, (state, { error }) => ({ ...state, error, loading: false })),\n);\n\n// selectors\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\nexport const selectAllUsers = createSelector(selectUsersState, (s) => s.users);\nexport const selectUsersLoading = createSelector(selectUsersState, (s) => s.loading);\n```\n\n## Constraints\n\n### MUST DO\n- Use standalone components (Angular 17+ default)\n- Use signals for reactive state where appropriate\n- Use OnPush change detection strategy\n- Use strict TypeScript configuration\n- Implement proper error handling in RxJS streams\n- Use `trackBy` functions in `*ngFor` loops\n- Write tests with >85% coverage\n- Follow Angular style guide\n\n### MUST NOT DO\n- Use NgModule-based components (except when required for compatibility)\n- Forget to unsubscribe from observables (use `takeUntilDestroyed` or `async` pipe)\n- Use async operations without proper error handling\n- Skip accessibility attributes\n- Expose sensitive data in client-side code\n- Use `any` type without justification\n- Mutate state directly in NgRx\n- Skip unit tests for critical logic\n\n## Output Templates\n\nWhen implementing Angular features, provide:\n1. Component file with standalone configuration\n2. Service file if business logic is involved\n3. State management files if using NgRx\n4. Test file with comprehensive test cases\n5. Brief explanation of architectural decisions\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/frontend/angular-architect/)\n","skills/angular-architect/references/components.md":"# Standalone Components & Signals\n\n## Standalone Component Pattern\n\n```typescript\nimport { Component, signal, computed, effect } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n@Component({\n  selector: 'app-user-profile',\n  standalone: true,\n  imports: [CommonModule, FormsModule],\n  templateUrl: './user-profile.component.html',\n  styleUrl: './user-profile.component.scss',\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserProfileComponent {\n  // Signal-based state\n  count = signal(0);\n  doubleCount = computed(() => this.count() * 2);\n\n  constructor() {\n    // Side effects\n    effect(() => {\n      console.log(`Count is: ${this.count()}`);\n    });\n  }\n\n  increment() {\n    this.count.update(value => value + 1);\n  }\n}\n```\n\n## Input/Output with Signals\n\n```typescript\nimport { Component, input, output, model } from '@angular/core';\n\n@Component({\n  selector: 'app-search-box',\n  standalone: true,\n  template: `\n    <input\n      [value]=\"query()\"\n      (input)=\"onQueryChange($event)\"\n      [placeholder]=\"placeholder()\" />\n  `\n})\nexport class SearchBoxComponent {\n  // Signal inputs (Angular 17.1+)\n  placeholder = input<string>('Search...');\n  initialQuery = input<string>('');\n\n  // Signal outputs\n  queryChange = output<string>();\n\n  // Two-way binding with model signal\n  query = model<string>('');\n\n  onQueryChange(event: Event) {\n    const value = (event.target as HTMLInputElement).value;\n    this.query.set(value);\n    this.queryChange.emit(value);\n  }\n}\n\n// Parent usage\n@Component({\n  template: `\n    <app-search-box\n      [(query)]=\"searchQuery\"\n      [placeholder]=\"'Find users...'\"\n      (queryChange)=\"onSearch($event)\" />\n  `\n})\nexport class ParentComponent {\n  searchQuery = signal('');\n\n  onSearch(query: string) {\n    console.log('Searching:', query);\n  }\n}\n```\n\n## Smart vs Dumb Components\n\n```typescript\n// Smart Component (Container)\n@Component({\n  selector: 'app-users-container',\n  standalone: true,\n  imports: [UserListComponent],\n  template: `\n    <app-user-list\n      [users]=\"users()\"\n      [loading]=\"loading()\"\n      (userSelected)=\"onUserSelected($event)\" />\n  `\n})\nexport class UsersContainerComponent {\n  private usersService = inject(UsersService);\n\n  users = signal<User[]>([]);\n  loading = signal(true);\n\n  constructor() {\n    effect(() => {\n      this.usersService.getUsers().subscribe({\n        next: users => {\n          this.users.set(users);\n          this.loading.set(false);\n        },\n        error: err => console.error(err)\n      });\n    });\n  }\n\n  onUserSelected(user: User) {\n    // Handle business logic\n  }\n}\n\n// Dumb Component (Presentational)\n@Component({\n  selector: 'app-user-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div (click)=\"userSelected.emit(user)\">\n          {{ user.name }}\n        </div>\n      }\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserListComponent {\n  users = input.required<User[]>();\n  loading = input<boolean>(false);\n  userSelected = output<User>();\n}\n```\n\n## Content Projection\n\n```typescript\n// Card component with multiple slots\n@Component({\n  selector: 'app-card',\n  standalone: true,\n  template: `\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <ng-content select=\"[header]\"></ng-content>\n      </div>\n      <div class=\"card-body\">\n        <ng-content></ng-content>\n      </div>\n      <div class=\"card-footer\">\n        <ng-content select=\"[footer]\"></ng-content>\n      </div>\n    </div>\n  `\n})\nexport class CardComponent {}\n\n// Usage\n@Component({\n  template: `\n    <app-card>\n      <h2 header>Card Title</h2>\n      <p>Card content goes here</p>\n      <button footer>Action</button>\n    </app-card>\n  `\n})\nexport class ParentComponent {}\n```\n\n## Dependency Injection\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { UserService } from './user.service';\n\n@Component({\n  selector: 'app-user-dashboard',\n  standalone: true\n})\nexport class UserDashboardComponent {\n  // Modern inject() API\n  private userService = inject(UserService);\n  private router = inject(Router);\n\n  // Optional dependency\n  private logger = inject(LoggerService, { optional: true });\n\n  users = signal<User[]>([]);\n\n  ngOnInit() {\n    this.loadUsers();\n  }\n\n  loadUsers() {\n    this.userService.getUsers().subscribe({\n      next: users => this.users.set(users),\n      error: err => this.logger?.error('Failed to load users', err)\n    });\n  }\n}\n```\n\n## New Control Flow (@if, @for)\n\n```typescript\n@Component({\n  template: `\n    <!-- @if instead of *ngIf -->\n    @if (user(); as currentUser) {\n      <div>Hello, {{ currentUser.name }}</div>\n    } @else if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      <div>Please log in</div>\n    }\n\n    <!-- @for instead of *ngFor -->\n    @for (item of items(); track item.id) {\n      <div>{{ item.name }}</div>\n    } @empty {\n      <div>No items found</div>\n    }\n\n    <!-- @switch instead of *ngSwitch -->\n    @switch (status()) {\n      @case ('pending') {\n        <span>Pending...</span>\n      }\n      @case ('success') {\n        <span>Success!</span>\n      }\n      @default {\n        <span>Unknown</span>\n      }\n    }\n  `\n})\nexport class ModernControlFlowComponent {\n  user = signal<User | null>(null);\n  loading = signal(false);\n  items = signal<Item[]>([]);\n  status = signal<'pending' | 'success' | 'error'>('pending');\n}\n```\n\n## Performance: OnPush & TrackBy\n\n```typescript\n@Component({\n  selector: 'app-product-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @for (product of products(); track trackByProductId($index, product)) {\n      <app-product-card [product]=\"product\" />\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ProductListComponent {\n  products = input.required<Product[]>();\n\n  // TrackBy for optimal rendering\n  trackByProductId(index: number, product: Product): number {\n    return product.id;\n  }\n}\n```\n\n## Quick Reference\n\n| Pattern | Angular 17+ Approach |\n|---------|---------------------|\n| Component | Standalone by default |\n| State | Signals (`signal()`, `computed()`) |\n| Input | `input()`, `input.required()` |\n| Output | `output<T>()` |\n| Two-way | `model<T>()` |\n| DI | `inject()` function |\n| Control Flow | `@if`, `@for`, `@switch` |\n| Change Detection | `ChangeDetectionStrategy.OnPush` |\n","skills/angular-architect/references/ngrx.md":"# NgRx State Management\n\n## Store Setup\n\n```typescript\n// app.config.ts\nimport { provideStore } from '@ngrx/store';\nimport { provideEffects } from '@ngrx/effects';\nimport { provideStoreDevtools } from '@ngrx/store-devtools';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideStore({\n      users: usersReducer,\n      products: productsReducer\n    }),\n    provideEffects([UsersEffects, ProductsEffects]),\n    provideStoreDevtools({\n      maxAge: 25,\n      logOnly: !isDevMode()\n    })\n  ]\n};\n```\n\n## Actions (Modern)\n\n```typescript\n// users.actions.ts\nimport { createActionGroup, emptyProps, props } from '@ngrx/store';\nimport { User } from './user.model';\n\nexport const UsersActions = createActionGroup({\n  source: 'Users',\n  events: {\n    'Load Users': emptyProps(),\n    'Load Users Success': props<{ users: User[] }>(),\n    'Load Users Failure': props<{ error: string }>(),\n\n    'Add User': props<{ user: User }>(),\n    'Add User Success': props<{ user: User }>(),\n    'Add User Failure': props<{ error: string }>(),\n\n    'Update User': props<{ id: string; changes: Partial<User> }>(),\n    'Update User Success': props<{ user: User }>(),\n\n    'Delete User': props<{ id: string }>(),\n    'Delete User Success': props<{ id: string }>()\n  }\n});\n```\n\n## Reducer with Entity Adapter\n\n```typescript\n// users.reducer.ts\nimport { createReducer, on } from '@ngrx/store';\nimport { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';\nimport { UsersActions } from './users.actions';\nimport { User } from './user.model';\n\nexport interface UsersState extends EntityState<User> {\n  loading: boolean;\n  error: string | null;\n  selectedUserId: string | null;\n}\n\nexport const usersAdapter: EntityAdapter<User> = createEntityAdapter<User>({\n  selectId: (user: User) => user.id,\n  sortComparer: (a, b) => a.name.localeCompare(b.name)\n});\n\nconst initialState: UsersState = usersAdapter.getInitialState({\n  loading: false,\n  error: null,\n  selectedUserId: null\n});\n\nexport const usersReducer = createReducer(\n  initialState,\n\n  // Load users\n  on(UsersActions.loadUsers, (state) => ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n\n  on(UsersActions.loadUsersSuccess, (state, { users }) =>\n    usersAdapter.setAll(users, {\n      ...state,\n      loading: false\n    })\n  ),\n\n  on(UsersActions.loadUsersFailure, (state, { error }) => ({\n    ...state,\n    loading: false,\n    error\n  })),\n\n  // Add user\n  on(UsersActions.addUserSuccess, (state, { user }) =>\n    usersAdapter.addOne(user, state)\n  ),\n\n  // Update user\n  on(UsersActions.updateUserSuccess, (state, { user }) =>\n    usersAdapter.updateOne(\n      { id: user.id, changes: user },\n      state\n    )\n  ),\n\n  // Delete user\n  on(UsersActions.deleteUserSuccess, (state, { id }) =>\n    usersAdapter.removeOne(id, state)\n  )\n);\n```\n\n## Selectors\n\n```typescript\n// users.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport { usersAdapter, UsersState } from './users.reducer';\n\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\n\n// Entity adapter selectors\nconst {\n  selectIds,\n  selectEntities,\n  selectAll,\n  selectTotal\n} = usersAdapter.getSelectors();\n\nexport const selectUserIds = createSelector(\n  selectUsersState,\n  selectIds\n);\n\nexport const selectUserEntities = createSelector(\n  selectUsersState,\n  selectEntities\n);\n\nexport const selectAllUsers = createSelector(\n  selectUsersState,\n  selectAll\n);\n\nexport const selectUsersTotal = createSelector(\n  selectUsersState,\n  selectTotal\n);\n\nexport const selectUsersLoading = createSelector(\n  selectUsersState,\n  (state) => state.loading\n);\n\nexport const selectUsersError = createSelector(\n  selectUsersState,\n  (state) => state.error\n);\n\n// Parameterized selector\nexport const selectUserById = (id: string) =>\n  createSelector(\n    selectUserEntities,\n    (entities) => entities[id]\n  );\n\n// Composed selector\nexport const selectActiveUsers = createSelector(\n  selectAllUsers,\n  (users) => users.filter(user => user.isActive)\n);\n\n// Selector with multiple inputs\nexport const selectUserWithPosts = createSelector(\n  selectUserById,\n  selectAllPosts,\n  (user, posts) => ({\n    user,\n    posts: posts.filter(post => post.userId === user?.id)\n  })\n);\n```\n\n## Effects\n\n```typescript\n// users.effects.ts\nimport { Injectable, inject } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { catchError, map, mergeMap, exhaustMap } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\n\n@Injectable()\nexport class UsersEffects {\n  private actions$ = inject(Actions);\n  private usersService = inject(UsersService);\n\n  // Load users effect\n  loadUsers$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.loadUsers),\n      mergeMap(() =>\n        this.usersService.getAll().pipe(\n          map(users => UsersActions.loadUsersSuccess({ users })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Add user effect (exhaustMap prevents duplicate submits)\n  addUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.addUser),\n      exhaustMap(({ user }) =>\n        this.usersService.create(user).pipe(\n          map(createdUser => UsersActions.addUserSuccess({ user: createdUser })),\n          catchError(error =>\n            of(UsersActions.addUserFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Update user effect\n  updateUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.updateUser),\n      mergeMap(({ id, changes }) =>\n        this.usersService.update(id, changes).pipe(\n          map(user => UsersActions.updateUserSuccess({ user })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Delete user effect\n  deleteUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.deleteUser),\n      mergeMap(({ id }) =>\n        this.usersService.delete(id).pipe(\n          map(() => UsersActions.deleteUserSuccess({ id })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Non-dispatching effect (side effect only)\n  logUserActions$ = createEffect(\n    () =>\n      this.actions$.pipe(\n        ofType(\n          UsersActions.addUserSuccess,\n          UsersActions.updateUserSuccess,\n          UsersActions.deleteUserSuccess\n        ),\n        tap(action => console.log('User action:', action))\n      ),\n    { dispatch: false }\n  );\n}\n```\n\n## Component Integration\n\n```typescript\n// users-list.component.ts\nimport { Component, inject } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { UsersActions } from './store/users.actions';\nimport {\n  selectAllUsers,\n  selectUsersLoading,\n  selectUsersError\n} from './store/users.selectors';\n\n@Component({\n  selector: 'app-users-list',\n  standalone: true,\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else if (error(); as err) {\n      <div>Error: {{ err }}</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div>\n          {{ user.name }}\n          <button (click)=\"onDelete(user.id)\">Delete</button>\n        </div>\n      }\n    }\n  `\n})\nexport class UsersListComponent {\n  private store = inject(Store);\n\n  // Select data as signals\n  users = toSignal(this.store.select(selectAllUsers), { initialValue: [] });\n  loading = toSignal(this.store.select(selectUsersLoading), { initialValue: false });\n  error = toSignal(this.store.select(selectUsersError), { initialValue: null });\n\n  ngOnInit() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  onDelete(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n}\n```\n\n## Facade Pattern\n\n```typescript\n// users.facade.ts\n@Injectable({ providedIn: 'root' })\nexport class UsersFacade {\n  private store = inject(Store);\n\n  // Selectors\n  users$ = this.store.select(selectAllUsers);\n  loading$ = this.store.select(selectUsersLoading);\n  error$ = this.store.select(selectUsersError);\n\n  // Actions\n  loadUsers() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  addUser(user: User) {\n    this.store.dispatch(UsersActions.addUser({ user }));\n  }\n\n  updateUser(id: string, changes: Partial<User>) {\n    this.store.dispatch(UsersActions.updateUser({ id, changes }));\n  }\n\n  deleteUser(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n\n  getUserById(id: string) {\n    return this.store.select(selectUserById(id));\n  }\n}\n\n// Usage in component\n@Component({\n  selector: 'app-users',\n  standalone: true\n})\nexport class UsersComponent {\n  private facade = inject(UsersFacade);\n\n  users = toSignal(this.facade.users$, { initialValue: [] });\n  loading = toSignal(this.facade.loading$, { initialValue: false });\n\n  ngOnInit() {\n    this.facade.loadUsers();\n  }\n\n  onAdd(user: User) {\n    this.facade.addUser(user);\n  }\n}\n```\n\n## Quick Reference\n\n| Concept | Usage |\n|---------|-------|\n| Actions | `createActionGroup()` |\n| Reducer | `createReducer()`, `on()` |\n| Entity | `createEntityAdapter()` |\n| Selectors | `createSelector()`, `createFeatureSelector()` |\n| Effects | `createEffect()`, `ofType()` |\n| Store | `inject(Store)`, `store.select()`, `store.dispatch()` |\n| DevTools | `provideStoreDevtools()` |\n| Testing | Mock store, marble testing |\n","skills/angular-architect/references/routing.md":"# Angular Routing\n\n## Routes Configuration\n\n```typescript\n// app.routes.ts\nimport { Routes } from '@angular/router';\nimport { HomeComponent } from './home/home.component';\n\nexport const routes: Routes = [\n  {\n    path: '',\n    redirectTo: '/home',\n    pathMatch: 'full'\n  },\n  {\n    path: 'home',\n    component: HomeComponent,\n    title: 'Home'\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./users/users.component').then(m => m.UsersComponent),\n    title: 'Users'\n  },\n  {\n    path: 'users/:id',\n    loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent),\n    canActivate: [authGuard],\n    resolve: { user: userResolver }\n  },\n  {\n    path: 'admin',\n    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),\n    canActivate: [authGuard, adminGuard]\n  },\n  {\n    path: '**',\n    loadComponent: () => import('./not-found/not-found.component').then(m => m.NotFoundComponent),\n    title: '404 Not Found'\n  }\n];\n\n// app.config.ts\nimport { provideRouter, withComponentInputBinding } from '@angular/router';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideRouter(\n      routes,\n      withComponentInputBinding(),  // Bind route params to @Input()\n      withViewTransitions(),        // Enable view transitions\n      withPreloading(PreloadAllModules)\n    )\n  ]\n};\n```\n\n## Lazy Loading\n\n```typescript\n// Feature routes\n// admin/admin.routes.ts\nimport { Routes } from '@angular/router';\n\nexport const ADMIN_ROUTES: Routes = [\n  {\n    path: '',\n    loadComponent: () => import('./admin-dashboard.component').then(m => m.AdminDashboardComponent)\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./admin-users.component').then(m => m.AdminUsersComponent)\n  },\n  {\n    path: 'settings',\n    loadComponent: () => import('./admin-settings.component').then(m => m.AdminSettingsComponent)\n  }\n];\n```\n\n## Functional Guards\n\n```typescript\n// guards/auth.guard.ts\nimport { inject } from '@angular/core';\nimport { Router, CanActivateFn } from '@angular/router';\nimport { AuthService } from '../services/auth.service';\n\nexport const authGuard: CanActivateFn = (route, state) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.isAuthenticated()) {\n    return true;\n  }\n\n  // Redirect to login with return URL\n  return router.createUrlTree(['/login'], {\n    queryParams: { returnUrl: state.url }\n  });\n};\n\n// Admin guard\nexport const adminGuard: CanActivateFn = () => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.hasRole('admin')) {\n    return true;\n  }\n\n  return router.createUrlTree(['/unauthorized']);\n};\n\n// Can deactivate (unsaved changes)\nexport const canDeactivateGuard: CanDeactivateFn<FormComponent> = (component) => {\n  if (component.hasUnsavedChanges()) {\n    return confirm('You have unsaved changes. Are you sure you want to leave?');\n  }\n  return true;\n};\n```\n\n## Resolvers\n\n```typescript\n// resolvers/user.resolver.ts\nimport { inject } from '@angular/core';\nimport { ResolveFn } from '@angular/router';\nimport { catchError, of } from 'rxjs';\nimport { User } from '../models/user.model';\nimport { UsersService } from '../services/users.service';\n\nexport const userResolver: ResolveFn<User | null> = (route, state) => {\n  const usersService = inject(UsersService);\n  const id = route.paramMap.get('id')!;\n\n  return usersService.getById(id).pipe(\n    catchError(() => of(null))\n  );\n};\n\n// Component receives resolved data\n@Component({\n  selector: 'app-user-detail',\n  standalone: true,\n  template: `\n    @if (user) {\n      <h1>{{ user.name }}</h1>\n    } @else {\n      <p>User not found</p>\n    }\n  `\n})\nexport class UserDetailComponent {\n  user = input<User | null>(null);  // Resolved data bound as input\n}\n```\n\n## Route Parameters\n\n```typescript\nimport { Component, inject, input } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n  selector: 'app-product-detail',\n  standalone: true\n})\nexport class ProductDetailComponent {\n  private route = inject(ActivatedRoute);\n  private router = inject(Router);\n\n  // Modern approach: route params as inputs\n  id = input.required<string>();\n\n  // Legacy approach: subscribe to params\n  ngOnInit() {\n    this.route.paramMap.subscribe(params => {\n      const id = params.get('id');\n      this.loadProduct(id);\n    });\n\n    // Query params\n    this.route.queryParamMap.subscribe(params => {\n      const filter = params.get('filter');\n      const sort = params.get('sort');\n    });\n  }\n\n  // Navigate programmatically\n  goToEdit() {\n    this.router.navigate(['/products', this.id(), 'edit']);\n  }\n\n  // Navigate with query params\n  applyFilter(filter: string) {\n    this.router.navigate([], {\n      relativeTo: this.route,\n      queryParams: { filter },\n      queryParamsHandling: 'merge'  // Preserve other params\n    });\n  }\n}\n```\n\n## Router Events\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { Router, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n  selector: 'app-root',\n  standalone: true\n})\nexport class AppComponent {\n  private router = inject(Router);\n  loading = signal(false);\n\n  constructor() {\n    // Show loading on navigation start\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationStart)\n    ).subscribe(() => {\n      this.loading.set(true);\n    });\n\n    // Hide loading on navigation end\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationEnd)\n    ).subscribe(() => {\n      this.loading.set(false);\n    });\n\n    // Handle navigation errors\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationError)\n    ).subscribe((event: NavigationError) => {\n      console.error('Navigation error:', event.error);\n      this.loading.set(false);\n    });\n  }\n}\n```\n\n## Child Routes & Outlets\n\n```typescript\n// Parent route with child routes\nconst routes: Routes = [\n  {\n    path: 'dashboard',\n    component: DashboardComponent,\n    children: [\n      {\n        path: 'stats',\n        component: StatsComponent,\n        outlet: 'panel'  // Named outlet\n      },\n      {\n        path: 'charts',\n        component: ChartsComponent,\n        outlet: 'panel'\n      }\n    ]\n  }\n];\n\n// Dashboard component template\n@Component({\n  template: `\n    <div class=\"dashboard\">\n      <div class=\"main\">\n        <router-outlet></router-outlet>  <!-- Primary outlet -->\n      </div>\n      <div class=\"panel\">\n        <router-outlet name=\"panel\"></router-outlet>  <!-- Named outlet -->\n      </div>\n    </div>\n  `\n})\nexport class DashboardComponent {}\n\n// Navigate to named outlet\nthis.router.navigate(['/dashboard', { outlets: { panel: ['stats'] } }]);\n```\n\n## Preloading Strategies\n\n```typescript\n// Custom preloading strategy\nimport { Injectable } from '@angular/core';\nimport { PreloadingStrategy, Route } from '@angular/router';\nimport { Observable, of, timer } from 'rxjs';\nimport { mergeMap } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class CustomPreloadingStrategy implements PreloadingStrategy {\n  preload(route: Route, load: () => Observable<any>): Observable<any> {\n    // Only preload routes with data.preload = true\n    if (route.data?.['preload']) {\n      const delay = route.data?.['preloadDelay'] || 0;\n      return timer(delay).pipe(\n        mergeMap(() => load())\n      );\n    }\n    return of(null);\n  }\n}\n\n// Route config with preload data\nconst routes: Routes = [\n  {\n    path: 'important',\n    loadChildren: () => import('./important/important.routes'),\n    data: { preload: true, preloadDelay: 2000 }\n  }\n];\n\n// Register in app config\nprovideRouter(routes, withPreloading(CustomPreloadingStrategy))\n```\n\n## Route Guards with Observables\n\n```typescript\nexport const dataGuard: CanActivateFn = (route, state) => {\n  const dataService = inject(DataService);\n  const router = inject(Router);\n\n  return dataService.checkAccess(route.params['id']).pipe(\n    map(hasAccess => {\n      if (hasAccess) {\n        return true;\n      }\n      return router.createUrlTree(['/no-access']);\n    }),\n    catchError(() => {\n      return of(router.createUrlTree(['/error']));\n    })\n  );\n};\n```\n\n## Quick Reference\n\n| Feature | Usage |\n|---------|-------|\n| Routes | `Routes` array in app.routes.ts |\n| Lazy load | `loadComponent()`, `loadChildren()` |\n| Guards | `CanActivateFn`, `CanDeactivateFn` |\n| Resolvers | `ResolveFn<T>` |\n| Params | `route.paramMap`, `input<T>()` |\n| Query | `route.queryParamMap` |\n| Navigate | `router.navigate()`, `routerLink` |\n| Events | `router.events` |\n| Outlets | `<router-outlet name=\"...\">` |\n| Preload | `withPreloading()` |\n","skills/angular-architect/references/rxjs.md":"# RxJS Patterns\n\n## Essential Operators\n\n```typescript\nimport { Component, inject, signal } from '@angular/core';\nimport {\n  map, filter, switchMap, catchError,\n  debounceTime, distinctUntilChanged,\n  tap, shareReplay, takeUntil\n} from 'rxjs/operators';\nimport { Subject, of, EMPTY } from 'rxjs';\n\n@Component({\n  selector: 'app-search',\n  standalone: true\n})\nexport class SearchComponent {\n  private searchService = inject(SearchService);\n  private destroy$ = new Subject<void>();\n\n  searchTerm$ = new Subject<string>();\n  results = signal<SearchResult[]>([]);\n\n  ngOnInit() {\n    this.searchTerm$.pipe(\n      debounceTime(300),              // Wait 300ms after typing\n      distinctUntilChanged(),         // Only if value changed\n      filter(term => term.length > 2), // Minimum 3 characters\n      tap(() => this.loading.set(true)),\n      switchMap(term =>               // Cancel previous requests\n        this.searchService.search(term).pipe(\n          catchError(err => {\n            console.error(err);\n            return of([]);            // Return empty on error\n          })\n        )\n      ),\n      tap(() => this.loading.set(false)),\n      takeUntil(this.destroy$)        // Auto-unsubscribe\n    ).subscribe(results => this.results.set(results));\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Subject Types\n\n```typescript\nimport { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';\n\nexport class SubjectExamples {\n  // Subject: No initial value, only emits to future subscribers\n  private clickSubject = new Subject<MouseEvent>();\n  click$ = this.clickSubject.asObservable();\n\n  onClick(event: MouseEvent) {\n    this.clickSubject.next(event);\n  }\n\n  // BehaviorSubject: Has initial value, emits latest value to new subscribers\n  private loadingSubject = new BehaviorSubject<boolean>(false);\n  loading$ = this.loadingSubject.asObservable();\n\n  setLoading(loading: boolean) {\n    this.loadingSubject.next(loading);\n  }\n\n  // ReplaySubject: Replays N previous values to new subscribers\n  private activitySubject = new ReplaySubject<Activity>(3); // Last 3 activities\n  activity$ = this.activitySubject.asObservable();\n\n  // AsyncSubject: Only emits last value when completed\n  private finalResultSubject = new AsyncSubject<Result>();\n  finalResult$ = this.finalResultSubject.asObservable();\n}\n```\n\n## Higher-Order Operators\n\n```typescript\nimport { switchMap, mergeMap, concatMap, exhaustMap } from 'rxjs/operators';\n\nexport class HigherOrderExamples {\n  private http = inject(HttpClient);\n\n  // switchMap: Cancel previous, use latest (search, typeahead)\n  searchUsers(term$: Observable<string>) {\n    return term$.pipe(\n      switchMap(term => this.http.get<User[]>(`/api/users?q=${term}`))\n    );\n  }\n\n  // mergeMap: Process all concurrently (independent requests)\n  uploadFiles(files: File[]) {\n    return from(files).pipe(\n      mergeMap(file => this.http.post('/api/upload', file))\n    );\n  }\n\n  // concatMap: Process sequentially (order matters)\n  processQueue(tasks: Task[]) {\n    return from(tasks).pipe(\n      concatMap(task => this.http.post('/api/process', task))\n    );\n  }\n\n  // exhaustMap: Ignore new until current completes (prevent double-click)\n  saveForm(clicks$: Observable<void>, formData: any) {\n    return clicks$.pipe(\n      exhaustMap(() => this.http.post('/api/save', formData))\n    );\n  }\n}\n```\n\n## Error Handling\n\n```typescript\nimport { catchError, retry, retryWhen, delay, tap } from 'rxjs/operators';\nimport { throwError, of, timer } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class DataService {\n  private http = inject(HttpClient);\n\n  // Retry with exponential backoff\n  getData() {\n    return this.http.get<Data>('/api/data').pipe(\n      retryWhen(errors =>\n        errors.pipe(\n          mergeMap((error, index) => {\n            if (index >= 3) {\n              return throwError(() => error);\n            }\n            const delayMs = Math.pow(2, index) * 1000;\n            return timer(delayMs);\n          })\n        )\n      ),\n      catchError(err => {\n        console.error('Failed after retries:', err);\n        return of(null); // Fallback value\n      })\n    );\n  }\n\n  // Catch and rethrow with context\n  saveData(data: Data) {\n    return this.http.post('/api/data', data).pipe(\n      catchError(err => {\n        if (err.status === 401) {\n          // Handle auth error\n          return throwError(() => new Error('Unauthorized'));\n        }\n        return throwError(() => err);\n      })\n    );\n  }\n}\n```\n\n## Memory Management\n\n```typescript\nimport { Component, DestroyRef, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n@Component({\n  selector: 'app-auto-cleanup',\n  standalone: true\n})\nexport class AutoCleanupComponent {\n  private dataService = inject(DataService);\n  private destroyRef = inject(DestroyRef);\n\n  data = signal<Data[]>([]);\n\n  constructor() {\n    // Modern approach: takeUntilDestroyed\n    this.dataService.getData().pipe(\n      takeUntilDestroyed()  // Auto-cleanup on destroy\n    ).subscribe(data => this.data.set(data));\n\n    // Manual cleanup with DestroyRef\n    const subscription = this.dataService.getUpdates().subscribe();\n    this.destroyRef.onDestroy(() => subscription.unsubscribe());\n  }\n}\n\n// Legacy approach (still valid)\n@Component({\n  selector: 'app-manual-cleanup',\n  standalone: true\n})\nexport class ManualCleanupComponent implements OnDestroy {\n  private destroy$ = new Subject<void>();\n\n  ngOnInit() {\n    this.dataService.getData().pipe(\n      takeUntil(this.destroy$)\n    ).subscribe();\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Combining Observables\n\n```typescript\nimport { combineLatest, forkJoin, merge, zip } from 'rxjs';\n\nexport class CombiningExamples {\n  private http = inject(HttpClient);\n\n  // combineLatest: Emit when any source emits (latest values)\n  getDashboard() {\n    return combineLatest({\n      user: this.http.get<User>('/api/user'),\n      stats: this.http.get<Stats>('/api/stats'),\n      notifications: this.http.get<Notification[]>('/api/notifications')\n    }).pipe(\n      map(({ user, stats, notifications }) => ({\n        user,\n        stats,\n        notifications\n      }))\n    );\n  }\n\n  // forkJoin: Emit when all sources complete (like Promise.all)\n  loadAllData() {\n    return forkJoin({\n      users: this.http.get<User[]>('/api/users'),\n      products: this.http.get<Product[]>('/api/products'),\n      orders: this.http.get<Order[]>('/api/orders')\n    });\n  }\n\n  // merge: Emit when any source emits (flattens all)\n  getActivityFeed() {\n    return merge(\n      this.http.get<Activity[]>('/api/recent'),\n      this.http.get<Activity[]>('/api/trending')\n    );\n  }\n}\n```\n\n## Custom Operators\n\n```typescript\nimport { Observable, OperatorFunction } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\n// Custom operator for logging\nexport function debug<T>(tag: string): OperatorFunction<T, T> {\n  return (source: Observable<T>) =>\n    source.pipe(\n      tap({\n        next: value => console.log(`[${tag}] Next:`, value),\n        error: err => console.error(`[${tag}] Error:`, err),\n        complete: () => console.log(`[${tag}] Complete`)\n      })\n    );\n}\n\n// Usage\nthis.http.get('/api/data').pipe(\n  debug('API Call'),\n  map(data => transform(data))\n).subscribe();\n```\n\n## ShareReplay for Caching\n\n```typescript\nimport { shareReplay } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class ConfigService {\n  private http = inject(HttpClient);\n\n  // Cache config, share with all subscribers\n  config$ = this.http.get<Config>('/api/config').pipe(\n    shareReplay({ bufferSize: 1, refCount: true })\n  );\n\n  // All components get same config without extra HTTP calls\n  getConfig() {\n    return this.config$;\n  }\n}\n```\n\n## Quick Reference\n\n| Use Case | Operator |\n|----------|----------|\n| Transform values | `map`, `pluck` |\n| Filter values | `filter`, `distinctUntilChanged` |\n| Time-based | `debounceTime`, `throttleTime`, `delay` |\n| Cancel previous | `switchMap` |\n| Process all | `mergeMap` |\n| Sequential | `concatMap` |\n| Ignore new | `exhaustMap` |\n| Combine latest | `combineLatest` |\n| Wait for all | `forkJoin` |\n| Error handling | `catchError`, `retry` |\n| Cleanup | `takeUntilDestroyed`, `takeUntil` |\n| Share result | `shareReplay` |\n","skills/angular-architect/references/testing.md":"# Angular Testing\n\n## Component Testing\n\n```typescript\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { signal } from '@angular/core';\nimport { UserListComponent } from './user-list.component';\nimport { UsersService } from './users.service';\nimport { of } from 'rxjs';\n\ndescribe('UserListComponent', () => {\n  let component: UserListComponent;\n  let fixture: ComponentFixture<UserListComponent>;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  const mockUsers = [\n    { id: '1', name: 'John Doe', email: 'john@example.com' },\n    { id: '2', name: 'Jane Smith', email: 'jane@example.com' }\n  ];\n\n  beforeEach(async () => {\n    // Create spy object\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll', 'delete']);\n\n    await TestBed.configureTestingModule({\n      imports: [UserListComponent],  // Standalone component\n      providers: [\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    }).compileComponents();\n\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n    fixture = TestBed.createComponent(UserListComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n\n  it('should load users on init', () => {\n    usersService.getAll.and.returnValue(of(mockUsers));\n\n    fixture.detectChanges();  // Trigger ngOnInit\n\n    expect(usersService.getAll).toHaveBeenCalled();\n    expect(component.users()).toEqual(mockUsers);\n  });\n\n  it('should display users in template', () => {\n    component.users.set(mockUsers);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    const userElements = compiled.querySelectorAll('.user-item');\n\n    expect(userElements.length).toBe(2);\n    expect(userElements[0].textContent).toContain('John Doe');\n  });\n\n  it('should emit userSelected when user clicked', () => {\n    const emitSpy = spyOn(component.userSelected, 'emit');\n\n    component.onUserClick(mockUsers[0]);\n\n    expect(emitSpy).toHaveBeenCalledWith(mockUsers[0]);\n  });\n\n  it('should show loading state', () => {\n    component.loading.set(true);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    expect(compiled.querySelector('.loading')).toBeTruthy();\n  });\n});\n```\n\n## Service Testing\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';\nimport { UsersService } from './users.service';\nimport { User } from './user.model';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let httpMock: HttpTestingController;\n\n  const mockUsers: User[] = [\n    { id: '1', name: 'John', email: 'john@example.com' },\n    { id: '2', name: 'Jane', email: 'jane@example.com' }\n  ];\n\n  beforeEach(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule],\n      providers: [UsersService]\n    });\n\n    service = TestBed.inject(UsersService);\n    httpMock = TestBed.inject(HttpTestingController);\n  });\n\n  afterEach(() => {\n    httpMock.verify();  // Verify no outstanding requests\n  });\n\n  it('should fetch all users', (done) => {\n    service.getAll().subscribe(users => {\n      expect(users).toEqual(mockUsers);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('GET');\n    req.flush(mockUsers);\n  });\n\n  it('should create a user', (done) => {\n    const newUser: User = { id: '3', name: 'Bob', email: 'bob@example.com' };\n\n    service.create(newUser).subscribe(user => {\n      expect(user).toEqual(newUser);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('POST');\n    expect(req.request.body).toEqual(newUser);\n    req.flush(newUser);\n  });\n\n  it('should handle error', (done) => {\n    service.getAll().subscribe({\n      next: () => fail('should have failed'),\n      error: (error) => {\n        expect(error.status).toBe(500);\n        done();\n      }\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    req.flush('Server error', { status: 500, statusText: 'Internal Server Error' });\n  });\n});\n```\n\n## RxJS Marble Testing\n\n```typescript\nimport { TestScheduler } from 'rxjs/testing';\nimport { delay, map } from 'rxjs/operators';\n\ndescribe('RxJS Operators', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should map values', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--c--|', { a: 1, b: 2, c: 3 });\n      const expected = '    --x--y--z--|';\n      const result$ = source$.pipe(map(x => x * 10));\n\n      expectObservable(result$).toBe(expected, { x: 10, y: 20, z: 30 });\n    });\n  });\n\n  it('should delay emissions', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--|', { a: 1, b: 2 });\n      const expected = '    ----a--b--|';\n      const result$ = source$.pipe(delay(20));\n\n      expectObservable(result$).toBe(expected, { a: 1, b: 2 });\n    });\n  });\n});\n```\n\n## Testing with Signals\n\n```typescript\nimport { signal } from '@angular/core';\n\ndescribe('Counter Component', () => {\n  it('should update signal value', () => {\n    const count = signal(0);\n\n    expect(count()).toBe(0);\n\n    count.set(5);\n    expect(count()).toBe(5);\n\n    count.update(val => val + 1);\n    expect(count()).toBe(6);\n  });\n\n  it('should compute derived value', () => {\n    const count = signal(5);\n    const doubled = computed(() => count() * 2);\n\n    expect(doubled()).toBe(10);\n\n    count.set(10);\n    expect(doubled()).toBe(20);\n  });\n});\n```\n\n## Testing NgRx\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockStore, MockStore } from '@ngrx/store/testing';\nimport { UsersComponent } from './users.component';\nimport { selectAllUsers, selectUsersLoading } from './store/users.selectors';\n\ndescribe('UsersComponent with NgRx', () => {\n  let component: UsersComponent;\n  let fixture: ComponentFixture<UsersComponent>;\n  let store: MockStore;\n\n  const initialState = {\n    users: {\n      ids: ['1', '2'],\n      entities: {\n        '1': { id: '1', name: 'John' },\n        '2': { id: '2', name: 'Jane' }\n      },\n      loading: false\n    }\n  };\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [UsersComponent],\n      providers: [\n        provideMockStore({ initialState })\n      ]\n    }).compileComponents();\n\n    store = TestBed.inject(MockStore);\n    fixture = TestBed.createComponent(UsersComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should select users from store', () => {\n    store.overrideSelector(selectAllUsers, [\n      { id: '1', name: 'John' },\n      { id: '2', name: 'Jane' }\n    ]);\n\n    fixture.detectChanges();\n\n    expect(component.users().length).toBe(2);\n  });\n\n  it('should dispatch action on delete', () => {\n    const dispatchSpy = spyOn(store, 'dispatch');\n\n    component.onDelete('1');\n\n    expect(dispatchSpy).toHaveBeenCalledWith(\n      UsersActions.deleteUser({ id: '1' })\n    );\n  });\n});\n```\n\n## Testing Effects\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockActions } from '@ngrx/effects/testing';\nimport { Observable, of, throwError } from 'rxjs';\nimport { UsersEffects } from './users.effects';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\nimport { hot, cold } from 'jasmine-marbles';\n\ndescribe('UsersEffects', () => {\n  let actions$: Observable<any>;\n  let effects: UsersEffects;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  beforeEach(() => {\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        UsersEffects,\n        provideMockActions(() => actions$),\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    });\n\n    effects = TestBed.inject(UsersEffects);\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n  });\n\n  it('should load users successfully', () => {\n    const users = [{ id: '1', name: 'John' }];\n    const action = UsersActions.loadUsers();\n    const outcome = UsersActions.loadUsersSuccess({ users });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-b|', { b: users });\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n\n  it('should handle load users failure', () => {\n    const action = UsersActions.loadUsers();\n    const error = new Error('Failed to load');\n    const outcome = UsersActions.loadUsersFailure({ error: error.message });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-#|', {}, error);\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n});\n```\n\n## Testing Guards\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { Router } from '@angular/router';\nimport { authGuard } from './auth.guard';\nimport { AuthService } from './auth.service';\n\ndescribe('authGuard', () => {\n  let authService: jasmine.SpyObj<AuthService>;\n  let router: jasmine.SpyObj<Router>;\n\n  beforeEach(() => {\n    const authServiceSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated']);\n    const routerSpy = jasmine.createSpyObj('Router', ['createUrlTree']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        { provide: AuthService, useValue: authServiceSpy },\n        { provide: Router, useValue: routerSpy }\n      ]\n    });\n\n    authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;\n    router = TestBed.inject(Router) as jasmine.SpyObj<Router>;\n  });\n\n  it('should allow access when authenticated', () => {\n    authService.isAuthenticated.and.returnValue(true);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, {} as any)\n    );\n\n    expect(result).toBe(true);\n  });\n\n  it('should redirect when not authenticated', () => {\n    authService.isAuthenticated.and.returnValue(false);\n    const urlTree = {} as any;\n    router.createUrlTree.and.returnValue(urlTree);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, { url: '/protected' } as any)\n    );\n\n    expect(result).toBe(urlTree);\n    expect(router.createUrlTree).toHaveBeenCalledWith(\n      ['/login'],\n      { queryParams: { returnUrl: '/protected' } }\n    );\n  });\n});\n```\n\n## Quick Reference\n\n| Test Type | Key Tools |\n|-----------|-----------|\n| Component | `TestBed`, `ComponentFixture`, `detectChanges()` |\n| Service | `HttpClientTestingModule`, `HttpTestingController` |\n| RxJS | `TestScheduler`, marble diagrams |\n| NgRx Store | `provideMockStore`, `MockStore` |\n| Effects | `provideMockActions`, jasmine-marbles |\n| Guards | `TestBed.runInInjectionContext()` |\n| Signals | Direct value checks with `()` |\n| Spies | `jasmine.createSpyObj()`, `spyOn()` |\n","skills/api-designer/SKILL.md":"---\nname: api-designer\ndescription: Use when designing REST or GraphQL APIs, creating OpenAPI specifications, or planning API architecture. Invoke for resource modeling, versioning strategies, pagination patterns, error handling standards.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: api-architecture\n  triggers: API design, REST API, OpenAPI, API specification, API architecture, resource modeling, API versioning, GraphQL schema, API documentation\n  role: architect\n  scope: design\n  output-format: specification\n  related-skills: graphql-architect, fastapi-expert, nestjs-expert, spring-boot-engineer, security-reviewer\n---\n\n# API Designer\n\nSenior API architect specializing in REST and GraphQL APIs with comprehensive OpenAPI 3.1 specifications.\n\n## Core Workflow\n\n1. **Analyze domain** — Understand business requirements, data models, and client needs\n2. **Model resources** — Identify resources, relationships, and operations; sketch entity diagram before writing any spec\n3. **Design endpoints** — Define URI patterns, HTTP methods, request/response schemas\n4. **Specify contract** — Create OpenAPI 3.1 spec; validate before proceeding: `npx @redocly/cli lint openapi.yaml`\n5. **Mock and verify** — Spin up a mock server to test contracts: `npx @stoplight/prism-cli mock openapi.yaml`\n6. **Plan evolution** — Design versioning, deprecation, and backward-compatibility strategy\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| REST Patterns | `references/rest-patterns.md` | Resource design, HTTP methods, HATEOAS |\n| Versioning | `references/versioning.md` | API versions, deprecation, breaking changes |\n| Pagination | `references/pagination.md` | Cursor, offset, keyset pagination |\n| Error Handling | `references/error-handling.md` | Error responses, RFC 7807, status codes |\n| OpenAPI | `references/openapi.md` | OpenAPI 3.1, documentation, code generation |\n\n## Constraints\n\n### MUST DO\n- Follow REST principles (resource-oriented, proper HTTP methods)\n- Use consistent naming conventions (snake_case or camelCase — pick one, apply everywhere)\n- Include comprehensive OpenAPI 3.1 specification\n- Design proper error responses with actionable messages (RFC 7807)\n- Implement pagination for all collection endpoints\n- Version APIs with clear deprecation policies\n- Document authentication and authorization\n- Provide request/response examples\n\n### MUST NOT DO\n- Use verbs in resource URIs (use `/users/{id}`, not `/getUser/{id}`)\n- Return inconsistent response structures\n- Skip error code documentation\n- Ignore HTTP status code semantics\n- Design APIs without a versioning strategy\n- Expose implementation details in the API surface\n- Create breaking changes without a migration path\n- Omit rate limiting considerations\n\n## Templates\n\n### OpenAPI 3.1 Resource Endpoint (copy-paste starter)\n\n```yaml\nopenapi: \"3.1.0\"\ninfo:\n  title: Example API\n  version: \"1.1.0\"\npaths:\n  /users:\n    get:\n      summary: List users\n      operationId: listUsers\n      tags: [Users]\n      parameters:\n        - name: cursor\n          in: query\n          schema: { type: string }\n          description: Opaque cursor for pagination\n        - name: limit\n          in: query\n          schema: { type: integer, default: 20, maximum: 100 }\n      responses:\n        \"200\":\n          description: Paginated list of users\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [data, pagination]\n                properties:\n                  data:\n                    type: array\n                    items: { $ref: \"#/components/schemas/User\" }\n                  pagination:\n                    $ref: \"#/components/schemas/CursorPage\"\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"429\": { $ref: \"#/components/responses/TooManyRequests\" }\n  /users/{id}:\n    get:\n      summary: Get a user\n      operationId: getUser\n      tags: [Users]\n      parameters:\n        - name: id\n          in: path\n          required: true\n          schema: { type: string, format: uuid }\n      responses:\n        \"200\":\n          description: User found\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/User\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required: [id, email, created_at]\n      properties:\n        id:    { type: string, format: uuid, readOnly: true }\n        email: { type: string, format: email }\n        name:  { type: string }\n        created_at: { type: string, format: date-time, readOnly: true }\n\n    CursorPage:\n      type: object\n      required: [next_cursor, has_more]\n      properties:\n        next_cursor: { type: string, nullable: true }\n        has_more:    { type: boolean }\n\n    Problem:                       # RFC 7807 Problem Details\n      type: object\n      required: [type, title, status]\n      properties:\n        type:     { type: string, format: uri, example: \"https://api.example.com/errors/validation-error\" }\n        title:    { type: string, example: \"Validation Error\" }\n        status:   { type: integer, example: 400 }\n        detail:   { type: string, example: \"The 'email' field must be a valid email address.\" }\n        instance: { type: string, format: uri, example: \"/users/req-abc123\" }\n\n  responses:\n    BadRequest:\n      description: Invalid request parameters\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    Unauthorized:\n      description: Missing or invalid authentication\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    NotFound:\n      description: Resource not found\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    TooManyRequests:\n      description: Rate limit exceeded\n      headers:\n        Retry-After: { schema: { type: integer } }\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\nsecurity:\n  - BearerAuth: []\n```\n\n### RFC 7807 Error Response (copy-paste)\n\n```json\n{\n  \"type\": \"https://api.example.com/errors/validation-error\",\n  \"title\": \"Validation Error\",\n  \"status\": 422,\n  \"detail\": \"The 'email' field must be a valid email address.\",\n  \"instance\": \"/users/req-abc123\",\n  \"errors\": [\n    { \"field\": \"email\", \"message\": \"Must be a valid email address.\" }\n  ]\n}\n```\n\n- Always use `Content-Type: application/problem+json` for error responses.\n- `type` must be a stable, documented URI — never a generic string.\n- `detail` must be human-readable and actionable.\n- Extend with `errors[]` for field-level validation failures.\n\n## Output Checklist\n\nWhen delivering an API design, provide:\n1. Resource model and relationships (diagram or table)\n2. Endpoint specifications with URIs and HTTP methods\n3. OpenAPI 3.1 specification (YAML)\n4. Authentication and authorization flows\n5. Error response catalog (all 4xx/5xx with `type` URIs)\n6. Pagination and filtering patterns\n7. Versioning and deprecation strategy\n8. Validation result: `npx @redocly/cli lint openapi.yaml` passes with no errors\n\n## Knowledge Reference\n\nREST architecture, OpenAPI 3.1, GraphQL, HTTP semantics, JSON:API, HATEOAS, OAuth 2.0, JWT, RFC 7807 Problem Details, API versioning patterns, pagination strategies, rate limiting, webhook design, SDK generation\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/api-designer/)\n","skills/api-designer/references/error-handling.md":"# API Error Handling\n\n## Error Response Design\n\nConsistent, informative error responses are critical for API usability.\n\n## Standard Error Format\n\n### Basic Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 123 not found\",\n    \"details\": null\n  }\n}\n```\n\n### RFC 7807 Problem Details\n\nStandardized error format (application/problem+json):\n\n```http\nHTTP/1.1 404 Not Found\nContent-Type: application/problem+json\n\n{\n  \"type\": \"https://api.example.com/errors/resource-not-found\",\n  \"title\": \"Resource Not Found\",\n  \"status\": 404,\n  \"detail\": \"User with ID 123 does not exist\",\n  \"instance\": \"/users/123\"\n}\n```\n\n**Fields:**\n- `type` - URI reference identifying error type\n- `title` - Short, human-readable summary\n- `status` - HTTP status code\n- `detail` - Human-readable explanation specific to this occurrence\n- `instance` - URI reference for this specific occurrence\n\n### Extended Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be between 18 and 120\"\n      }\n    ],\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\",\n    \"documentation_url\": \"https://api.example.com/docs/errors#validation-error\"\n  }\n}\n```\n\n## Error Categories\n\n### 1. Validation Errors (400 Bad Request)\n\nClient sent invalid data.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"\",\n  \"email\": \"invalid-email\",\n  \"age\": 15\n}\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"name\",\n        \"code\": \"REQUIRED\",\n        \"message\": \"Name is required\"\n      },\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be at least 18\",\n        \"constraints\": {\n          \"min\": 18,\n          \"max\": 120\n        }\n      }\n    ]\n  }\n}\n```\n\n### 2. Authentication Errors (401 Unauthorized)\n\nMissing or invalid authentication credentials.\n\n```http\nGET /users/123\nAuthorization: Bearer <invalid-access-token>\n\nResponse: 401 Unauthorized\nWWW-Authenticate: Bearer realm=\"api\", error=\"invalid_token\"\n\n{\n  \"error\": {\n    \"code\": \"INVALID_TOKEN\",\n    \"message\": \"The access token is invalid or has expired\",\n    \"details\": {\n      \"reason\": \"token_expired\",\n      \"expired_at\": \"2024-01-15T10:00:00Z\"\n    }\n  }\n}\n```\n\n**Common auth error codes:**\n- `MISSING_TOKEN` - No auth token provided\n- `INVALID_TOKEN` - Token is malformed or invalid\n- `EXPIRED_TOKEN` - Token has expired\n- `REVOKED_TOKEN` - Token has been revoked\n\n### 3. Authorization Errors (403 Forbidden)\n\nAuthenticated but not authorized to perform action.\n\n```http\nDELETE /users/123\nAuthorization: Bearer <access-token>\n\nResponse: 403 Forbidden\n{\n  \"error\": {\n    \"code\": \"INSUFFICIENT_PERMISSIONS\",\n    \"message\": \"You do not have permission to delete this user\",\n    \"details\": {\n      \"required_permission\": \"users:delete\",\n      \"your_permissions\": [\"users:read\", \"users:update\"]\n    }\n  }\n}\n```\n\n### 4. Not Found Errors (404 Not Found)\n\nResource doesn't exist.\n\n```http\nGET /users/99999\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 99999 not found\",\n    \"details\": {\n      \"resource_type\": \"User\",\n      \"resource_id\": \"99999\"\n    }\n  }\n}\n```\n\n### 5. Conflict Errors (409 Conflict)\n\nRequest conflicts with current state.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"email\": \"existing@example.com\",\n  \"name\": \"John Doe\"\n}\n\nResponse: 409 Conflict\n{\n  \"error\": {\n    \"code\": \"RESOURCE_ALREADY_EXISTS\",\n    \"message\": \"User with email 'existing@example.com' already exists\",\n    \"details\": {\n      \"field\": \"email\",\n      \"value\": \"existing@example.com\",\n      \"existing_resource\": \"/users/123\"\n    }\n  }\n}\n```\n\n### 6. Rate Limiting (429 Too Many Requests)\n\nClient exceeded rate limit.\n\n```http\nGET /users\n\nResponse: 429 Too Many Requests\nRetry-After: 60\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1705320000\n\n{\n  \"error\": {\n    \"code\": \"RATE_LIMIT_EXCEEDED\",\n    \"message\": \"You have exceeded the rate limit\",\n    \"details\": {\n      \"limit\": 100,\n      \"window\": \"1 hour\",\n      \"retry_after\": 60,\n      \"reset_at\": \"2024-01-15T11:00:00Z\"\n    }\n  }\n}\n```\n\n### 7. Server Errors (500 Internal Server Error)\n\nUnexpected server error.\n\n```http\nGET /users/123\n\nResponse: 500 Internal Server Error\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\",\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\"\n  }\n}\n```\n\n**Never expose:**\n- Stack traces\n- Database errors\n- Internal paths\n- Sensitive configuration\n\n### 8. Service Unavailable (503 Service Unavailable)\n\nService temporarily unavailable.\n\n```http\nGET /users\n\nResponse: 503 Service Unavailable\nRetry-After: 300\n\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service is temporarily unavailable due to maintenance\",\n    \"details\": {\n      \"retry_after\": 300,\n      \"maintenance_end\": \"2024-01-15T12:00:00Z\"\n    }\n  }\n}\n```\n\n## Error Code Catalog\n\nDefine standard error codes for your API:\n\n```json\n{\n  \"VALIDATION_ERROR\": {\n    \"status\": 400,\n    \"description\": \"Request validation failed\",\n    \"subcodes\": {\n      \"REQUIRED\": \"Required field is missing\",\n      \"INVALID_FORMAT\": \"Field has invalid format\",\n      \"OUT_OF_RANGE\": \"Value is out of allowed range\",\n      \"INVALID_ENUM\": \"Value is not in allowed set\"\n    }\n  },\n  \"AUTHENTICATION_ERROR\": {\n    \"status\": 401,\n    \"description\": \"Authentication failed\",\n    \"subcodes\": {\n      \"MISSING_TOKEN\": \"No authentication token provided\",\n      \"INVALID_TOKEN\": \"Token is invalid\",\n      \"EXPIRED_TOKEN\": \"Token has expired\"\n    }\n  },\n  \"AUTHORIZATION_ERROR\": {\n    \"status\": 403,\n    \"description\": \"Insufficient permissions\",\n    \"subcodes\": {\n      \"INSUFFICIENT_PERMISSIONS\": \"Missing required permission\",\n      \"RESOURCE_FORBIDDEN\": \"Access to resource is forbidden\"\n    }\n  },\n  \"RESOURCE_NOT_FOUND\": {\n    \"status\": 404,\n    \"description\": \"Resource not found\"\n  },\n  \"CONFLICT_ERROR\": {\n    \"status\": 409,\n    \"description\": \"Request conflicts with current state\",\n    \"subcodes\": {\n      \"RESOURCE_ALREADY_EXISTS\": \"Resource already exists\",\n      \"CONCURRENT_MODIFICATION\": \"Resource was modified by another request\"\n    }\n  },\n  \"RATE_LIMIT_EXCEEDED\": {\n    \"status\": 429,\n    \"description\": \"Rate limit exceeded\"\n  },\n  \"INTERNAL_SERVER_ERROR\": {\n    \"status\": 500,\n    \"description\": \"Internal server error\"\n  }\n}\n```\n\n## Validation Error Details\n\n### Field-Level Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"credit_card.number\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Credit card number must be 16 digits\",\n        \"value_provided\": \"1234\",\n        \"constraints\": {\n          \"pattern\": \"^[0-9]{16}$\"\n        }\n      },\n      {\n        \"field\": \"items[0].quantity\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Quantity must be at least 1\",\n        \"value_provided\": 0,\n        \"constraints\": {\n          \"min\": 1,\n          \"max\": 1000\n        }\n      }\n    ]\n  }\n}\n```\n\n### Cross-Field Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"fields\": [\"start_date\", \"end_date\"],\n        \"code\": \"INVALID_RANGE\",\n        \"message\": \"End date must be after start date\",\n        \"values_provided\": {\n          \"start_date\": \"2024-01-20\",\n          \"end_date\": \"2024-01-15\"\n        }\n      }\n    ]\n  }\n}\n```\n\n## Request ID Tracking\n\nAlways include request ID for debugging:\n\n```http\nResponse Headers:\nX-Request-ID: req_abc123\n\nResponse Body:\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred\",\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\nClients can reference request ID in support tickets.\n\n## Error Documentation\n\nDocument all possible errors for each endpoint:\n\n```yaml\n/users/{id}:\n  get:\n    responses:\n      '200':\n        description: Success\n      '401':\n        description: Authentication failed\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              missing_token:\n                value:\n                  error:\n                    code: MISSING_TOKEN\n                    message: No authentication token provided\n              invalid_token:\n                value:\n                  error:\n                    code: INVALID_TOKEN\n                    message: Token is invalid or expired\n      '404':\n        description: User not found\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              not_found:\n                value:\n                  error:\n                    code: RESOURCE_NOT_FOUND\n                    message: User with ID 123 not found\n```\n\n## Retry Guidance\n\nHelp clients understand if they should retry:\n\n```json\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service temporarily unavailable\",\n    \"retry\": {\n      \"retryable\": true,\n      \"retry_after\": 60,\n      \"max_retries\": 3,\n      \"backoff\": \"exponential\"\n    }\n  }\n}\n```\n\n### Retryable Errors\n\n- 408 Request Timeout\n- 429 Too Many Requests (with Retry-After)\n- 500 Internal Server Error (sometimes)\n- 502 Bad Gateway\n- 503 Service Unavailable\n- 504 Gateway Timeout\n\n### Non-Retryable Errors\n\n- 400 Bad Request\n- 401 Unauthorized\n- 403 Forbidden\n- 404 Not Found\n- 409 Conflict\n- 422 Unprocessable Entity\n\n## Multi-Language Support\n\nSupport error messages in multiple languages:\n\n```http\nGET /users/invalid\nAccept-Language: es\n\nResponse: 404 Not Found\nContent-Language: es\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"Usuario con ID 'invalid' no encontrado\"\n  }\n}\n```\n\nAlways include `code` so clients can implement their own translations.\n\n## Best Practices\n\n1. **Use standard HTTP status codes** - Don't return 200 for errors\n2. **Include machine-readable codes** - Error codes for client logic\n3. **Provide human-readable messages** - Clear explanations\n4. **Be specific but safe** - Don't expose sensitive information\n5. **Include request ID** - For tracking and debugging\n6. **Document all errors** - Every possible error for each endpoint\n7. **Be consistent** - Same format across all endpoints\n8. **Help clients retry** - Indicate if error is retryable\n9. **Validate early** - Return validation errors immediately\n10. **Log errors server-side** - Track errors for monitoring\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Generic error messages** - \"Error occurred\" without details\n- **Exposing stack traces** - Security risk\n- **Inconsistent error format** - Different structure per endpoint\n- **Missing error codes** - Only human-readable messages\n- **Wrong status codes** - Returning 200 with error in body\n- **No request ID** - Makes debugging impossible\n- **Undocumented errors** - Clients don't know what to expect\n- **Too much information** - Exposing internal implementation\n","skills/api-designer/references/openapi.md":"# OpenAPI 3.1 Specification\n\n## What is OpenAPI?\n\nOpenAPI (formerly Swagger) is a standard for describing REST APIs. It enables:\n- Interactive documentation\n- Code generation (SDKs, clients, servers)\n- API testing tools\n- Contract validation\n- Mock servers\n\n## Basic Structure\n\n### Minimal OpenAPI 3.1 Spec\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 1.0.0\n  description: A sample API\n  contact:\n    name: API Support\n    email: support@example.com\n    url: https://example.com/support\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n\nservers:\n  - url: https://api.example.com/v1\n    description: Production server\n  - url: https://staging-api.example.com/v1\n    description: Staging server\n  - url: http://localhost:3000/v1\n    description: Local development\n\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users\n      operationId: listUsers\n      tags:\n        - Users\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  data:\n                    type: array\n                    items:\n                      $ref: '#/components/schemas/User'\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n      properties:\n        id:\n          type: integer\n          format: int64\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          example: John Doe\n```\n\n## Info Object\n\nMetadata about the API:\n\n```yaml\ninfo:\n  title: Users API\n  version: 1.0.0\n  description: |\n    # Users API\n\n    This API manages user accounts and profiles.\n\n    ## Features\n    - User CRUD operations\n    - Authentication with JWT\n    - Role-based authorization\n\n  termsOfService: https://example.com/terms\n\n  contact:\n    name: API Support Team\n    email: api-support@example.com\n    url: https://example.com/support\n\n  license:\n    name: MIT\n    url: https://opensource.org/licenses/MIT\n\n  x-api-id: users-api-v1\n  x-audience: external\n```\n\n## Servers\n\nDefine API base URLs:\n\n```yaml\nservers:\n  - url: https://api.example.com/v1\n    description: Production\n    variables:\n      version:\n        default: v1\n        enum:\n          - v1\n          - v2\n\n  - url: https://{environment}.example.com/v1\n    description: Dynamic environment\n    variables:\n      environment:\n        default: api\n        enum:\n          - api\n          - staging\n          - dev\n```\n\n## Paths and Operations\n\n### Complete Endpoint Example\n\n```yaml\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users with optional filtering\n      operationId: listUsers\n      tags:\n        - Users\n\n      parameters:\n        - name: offset\n          in: query\n          description: Number of items to skip\n          required: false\n          schema:\n            type: integer\n            minimum: 0\n            default: 0\n\n        - name: limit\n          in: query\n          description: Maximum number of items to return\n          required: false\n          schema:\n            type: integer\n            minimum: 1\n            maximum: 100\n            default: 20\n\n        - name: status\n          in: query\n          description: Filter by user status\n          required: false\n          schema:\n            type: string\n            enum:\n              - active\n              - inactive\n              - suspended\n\n      security:\n        - bearerAuth: []\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/UserListResponse'\n              examples:\n                success:\n                  $ref: '#/components/examples/UserListSuccess'\n\n        '401':\n          $ref: '#/components/responses/Unauthorized'\n\n        '429':\n          $ref: '#/components/responses/RateLimitExceeded'\n\n    post:\n      summary: Create user\n      description: Create a new user account\n      operationId: createUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/CreateUserRequest'\n            examples:\n              basic:\n                $ref: '#/components/examples/CreateUserBasic'\n\n      responses:\n        '201':\n          description: User created successfully\n          headers:\n            Location:\n              description: URL of the created user\n              schema:\n                type: string\n                format: uri\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '400':\n          $ref: '#/components/responses/ValidationError'\n\n        '409':\n          description: User already exists\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Error'\n\n  /users/{userId}:\n    parameters:\n      - name: userId\n        in: path\n        description: User ID\n        required: true\n        schema:\n          type: integer\n          format: int64\n\n    get:\n      summary: Get user\n      description: Retrieve a specific user by ID\n      operationId: getUser\n      tags:\n        - Users\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    put:\n      summary: Update user\n      description: Replace user data\n      operationId: updateUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/UpdateUserRequest'\n\n      responses:\n        '200':\n          description: User updated successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    delete:\n      summary: Delete user\n      description: Delete a user account\n      operationId: deleteUser\n      tags:\n        - Users\n\n      responses:\n        '204':\n          description: User deleted successfully\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n```\n\n## Components\n\nReusable components for your API spec.\n\n### Schemas\n\n```yaml\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n        - name\n      properties:\n        id:\n          type: integer\n          format: int64\n          readOnly: true\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n          example: John Doe\n        status:\n          type: string\n          enum:\n            - active\n            - inactive\n            - suspended\n          default: active\n        created_at:\n          type: string\n          format: date-time\n          readOnly: true\n          example: \"2024-01-15T10:30:00Z\"\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    CreateUserRequest:\n      type: object\n      required:\n        - email\n        - name\n      properties:\n        email:\n          type: string\n          format: email\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    UserListResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/User'\n        pagination:\n          $ref: '#/components/schemas/Pagination'\n\n    Pagination:\n      type: object\n      properties:\n        offset:\n          type: integer\n          minimum: 0\n        limit:\n          type: integer\n          minimum: 1\n        total:\n          type: integer\n          minimum: 0\n        has_more:\n          type: boolean\n\n    Error:\n      type: object\n      required:\n        - error\n      properties:\n        error:\n          type: object\n          required:\n            - code\n            - message\n          properties:\n            code:\n              type: string\n              example: RESOURCE_NOT_FOUND\n            message:\n              type: string\n              example: User with ID 123 not found\n            details:\n              type: object\n            request_id:\n              type: string\n              example: req_abc123\n```\n\n### Security Schemes\n\n```yaml\ncomponents:\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: JWT access token\n\n    apiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: API key for authentication\n\n    oauth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://auth.example.com/oauth/authorize\n          tokenUrl: https://auth.example.com/oauth/token\n          scopes:\n            users:read: Read user data\n            users:write: Create and update users\n            users:delete: Delete users\n```\n\nApply security globally or per-operation:\n\n```yaml\n# Global security\nsecurity:\n  - bearerAuth: []\n\n# Or per-operation\npaths:\n  /users:\n    get:\n      security:\n        - bearerAuth: []\n        - apiKey: []  # Alternative auth method\n```\n\n### Responses\n\nReusable response definitions:\n\n```yaml\ncomponents:\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: RESOURCE_NOT_FOUND\n              message: The requested resource was not found\n\n    Unauthorized:\n      description: Authentication required\n      headers:\n        WWW-Authenticate:\n          schema:\n            type: string\n          description: Authentication method\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    ValidationError:\n      description: Validation failed\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: VALIDATION_ERROR\n              message: Request validation failed\n              details:\n                - field: email\n                  code: INVALID_FORMAT\n                  message: Email must be a valid email address\n\n    RateLimitExceeded:\n      description: Rate limit exceeded\n      headers:\n        X-RateLimit-Limit:\n          schema:\n            type: integer\n          description: Request limit per hour\n        X-RateLimit-Remaining:\n          schema:\n            type: integer\n          description: Remaining requests\n        X-RateLimit-Reset:\n          schema:\n            type: integer\n            format: int64\n          description: Time when limit resets (Unix timestamp)\n        Retry-After:\n          schema:\n            type: integer\n          description: Seconds to wait before retry\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n```\n\n### Examples\n\n```yaml\ncomponents:\n  examples:\n    UserListSuccess:\n      summary: Successful user list response\n      value:\n        data:\n          - id: 1\n            email: john@example.com\n            name: John Doe\n            status: active\n            created_at: \"2024-01-15T10:30:00Z\"\n          - id: 2\n            email: jane@example.com\n            name: Jane Smith\n            status: active\n            created_at: \"2024-01-16T14:20:00Z\"\n        pagination:\n          offset: 0\n          limit: 20\n          total: 150\n          has_more: true\n\n    CreateUserBasic:\n      summary: Create user with minimal fields\n      value:\n        email: newuser@example.com\n        name: New User\n```\n\n## Data Types\n\n### Primitive Types\n\n```yaml\n# String\ntype: string\nexample: \"Hello World\"\n\n# String with format\ntype: string\nformat: email\nexample: \"user@example.com\"\n\n# Integer\ntype: integer\nformat: int64\nexample: 123\n\n# Number (float)\ntype: number\nformat: double\nexample: 99.99\n\n# Boolean\ntype: boolean\nexample: true\n\n# Date-time\ntype: string\nformat: date-time\nexample: \"2024-01-15T10:30:00Z\"\n\n# Date\ntype: string\nformat: date\nexample: \"2024-01-15\"\n\n# UUID\ntype: string\nformat: uuid\nexample: \"550e8400-e29b-41d4-a716-446655440000\"\n\n# URI\ntype: string\nformat: uri\nexample: \"https://example.com/users/123\"\n```\n\n### Arrays\n\n```yaml\ntype: array\nitems:\n  type: string\nminItems: 1\nmaxItems: 10\nuniqueItems: true\nexample: [\"tag1\", \"tag2\", \"tag3\"]\n\n# Array of objects\ntype: array\nitems:\n  $ref: '#/components/schemas/User'\n```\n\n### Objects\n\n```yaml\ntype: object\nrequired:\n  - name\n  - email\nproperties:\n  name:\n    type: string\n  email:\n    type: string\n    format: email\n  age:\n    type: integer\n    minimum: 0\n    maximum: 120\n\n# Additional properties\nadditionalProperties: false  # Strict - no extra properties\nadditionalProperties: true   # Allow any extra properties\nadditionalProperties:        # Extra properties must be strings\n  type: string\n```\n\n### Enums\n\n```yaml\ntype: string\nenum:\n  - active\n  - inactive\n  - suspended\ndefault: active\n```\n\n### OneOf / AnyOf / AllOf\n\n```yaml\n# OneOf - exactly one schema matches\noneOf:\n  - $ref: '#/components/schemas/CreditCard'\n  - $ref: '#/components/schemas/BankAccount'\n\n# AnyOf - one or more schemas match\nanyOf:\n  - $ref: '#/components/schemas/User'\n  - $ref: '#/components/schemas/Organization'\n\n# AllOf - all schemas must match (inheritance)\nallOf:\n  - $ref: '#/components/schemas/BaseUser'\n  - type: object\n    properties:\n      admin_level:\n        type: integer\n```\n\n## Validation\n\n### String Validation\n\n```yaml\ntype: string\nminLength: 1\nmaxLength: 100\npattern: \"^[a-zA-Z0-9_-]+$\"\nformat: email\n```\n\n### Number Validation\n\n```yaml\ntype: integer\nminimum: 0\nmaximum: 100\nexclusiveMinimum: true  # > 0 instead of >= 0\nmultipleOf: 5\n```\n\n### Array Validation\n\n```yaml\ntype: array\nminItems: 1\nmaxItems: 10\nuniqueItems: true\n```\n\n## Tags\n\nOrganize endpoints into logical groups:\n\n```yaml\ntags:\n  - name: Users\n    description: User management operations\n  - name: Orders\n    description: Order management\n  - name: Products\n    description: Product catalog\n\npaths:\n  /users:\n    get:\n      tags:\n        - Users\n```\n\n## Documentation\n\n### Markdown Support\n\n```yaml\ndescription: |\n  # User Management\n\n  This endpoint allows you to manage users.\n\n  ## Features\n  - Create users\n  - Update profiles\n  - Delete accounts\n\n  ## Authentication\n  Requires JWT bearer token.\n\n  ## Example\n  ```json\n  {\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\"\n  }\n  ```\n```\n\n## Code Generation\n\nGenerate SDKs from OpenAPI spec:\n\n```bash\n# Generate TypeScript client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g typescript-axios \\\n  -o ./client\n\n# Generate Python client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g python \\\n  -o ./python-client\n\n# Generate server stub\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g nodejs-express-server \\\n  -o ./server\n```\n\n## Validation Tools\n\nValidate OpenAPI spec:\n\n```bash\n# Using Swagger CLI\nswagger-cli validate openapi.yaml\n\n# Using Spectral (advanced linting)\nspectral lint openapi.yaml\n```\n\n## Best Practices\n\n1. **Use components** - Reuse schemas, responses, parameters\n2. **Add examples** - Include realistic examples for all schemas\n3. **Document thoroughly** - Every endpoint, parameter, response\n4. **Version your spec** - Track changes to the specification\n5. **Validate regularly** - Use tools to catch errors\n6. **Use $ref** - Reference components instead of duplicating\n7. **Include error responses** - Document all possible errors\n8. **Add operationId** - Unique ID for each operation (for code gen)\n9. **Tag endpoints** - Organize into logical groups\n10. **Provide security schemes** - Document authentication clearly\n","skills/api-designer/references/pagination.md":"# Pagination Patterns\n\n## Why Paginate?\n\nLarge collections can't be returned all at once due to:\n- Performance (slow queries, large payloads)\n- Memory constraints (server and client)\n- Network timeouts\n- Poor user experience\n\nAlways paginate collection endpoints.\n\n## Pagination Strategies\n\n### 1. Offset-Based Pagination\n\nMost common and intuitive. Uses `offset` (skip) and `limit` (page size).\n\n**Request:**\n```http\nGET /users?offset=20&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"offset\": 20,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=10&limit=10\",\n    \"next\": \"/users?offset=30&limit=10\",\n    \"last\": \"/users?offset=140&limit=10\"\n  }\n}\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to understand\n- Random access (jump to any page)\n- Shows total count\n\n**Disadvantages:**\n- Performance degrades with large offsets (database scans many rows)\n- Inconsistent results if data changes during pagination\n- Inefficient for real-time data\n- Database must count total rows (expensive)\n\n**Use when:**\n- Small to medium datasets\n- Data doesn't change frequently\n- Need random page access\n- Need total count\n\n### 2. Page-Based Pagination\n\nSimplified offset pagination using page numbers.\n\n**Request:**\n```http\nGET /users?page=3&per_page=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"page\": 3,\n    \"per_page\": 10,\n    \"total_pages\": 15,\n    \"total_count\": 150\n  },\n  \"links\": {\n    \"first\": \"/users?page=1&per_page=10\",\n    \"prev\": \"/users?page=2&per_page=10\",\n    \"next\": \"/users?page=4&per_page=10\",\n    \"last\": \"/users?page=15&per_page=10\"\n  }\n}\n```\n\n**Calculation:**\n- `offset = (page - 1) * per_page`\n- `total_pages = ceil(total_count / per_page)`\n\n**Same pros/cons as offset-based, but:**\n- More intuitive for users (page 1, page 2)\n- Common in web applications\n\n### 3. Cursor-Based Pagination\n\nUses an opaque cursor (pointer) to the next set of results.\n\n**Request:**\n```http\nGET /users?limit=10\nGET /users?cursor=<cursor>&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"next_cursor\": \"<next-cursor>\",\n    \"prev_cursor\": \"<prev-cursor>\",\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?cursor=<next-cursor>&limit=10\",\n    \"prev\": \"/users?cursor=<prev-cursor>&limit=10\"\n  }\n}\n```\n\n**Cursor structure (base64 encoded):**\n```json\n{\"id\": 30, \"sort\": \"created_at\"}\n```\n\n**Implementation:**\n```sql\n-- First page\nSELECT * FROM users ORDER BY created_at DESC LIMIT 10;\n\n-- Next page (cursor points to last item)\nSELECT * FROM users\nWHERE created_at < '2024-01-15T10:30:00Z'\nORDER BY created_at DESC\nLIMIT 10;\n```\n\n**Advantages:**\n- Consistent results (no skipped/duplicate items)\n- Efficient for large datasets\n- Works well with real-time data\n- No expensive COUNT query\n- Better database performance\n\n**Disadvantages:**\n- No random access (can't jump to page 10)\n- No total count\n- More complex to implement\n- Cursor is opaque (users can't modify it)\n\n**Use when:**\n- Large datasets\n- Data changes frequently\n- Infinite scroll UI\n- Real-time feeds\n- Performance is critical\n\n### 4. Keyset Pagination\n\nSimilar to cursor but uses actual field values instead of opaque cursor.\n\n**Request:**\n```http\nGET /users?after_id=20&limit=10\nGET /users?after_created_at=2024-01-15T10:30:00Z&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\", \"created_at\": \"2024-01-15T11:00:00Z\"},\n    {\"id\": 22, \"name\": \"User 22\", \"created_at\": \"2024-01-15T11:30:00Z\"}\n  ],\n  \"pagination\": {\n    \"after_id\": 30,\n    \"limit\": 10,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?after_id=30&limit=10\"\n  }\n}\n```\n\n**Implementation:**\n```sql\nSELECT * FROM users\nWHERE id > 20\nORDER BY id ASC\nLIMIT 10;\n```\n\n**Advantages:**\n- Very efficient (uses index)\n- Transparent cursor (human readable)\n- Consistent results\n- Simple implementation\n\n**Disadvantages:**\n- Requires indexed column\n- No random access\n- Sorting limited to cursor field\n- Complex for multi-field sorting\n\n**Use when:**\n- Simple ordering (by ID, timestamp)\n- Need efficient pagination\n- Want transparent cursor\n- Have proper indexes\n\n### 5. Seek Pagination (Time-Based)\n\nSpecialized keyset pagination for time-series data.\n\n**Request:**\n```http\nGET /events?since=2024-01-15T10:00:00Z&until=2024-01-15T11:00:00Z&limit=100\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"since\": \"2024-01-15T10:00:00Z\",\n    \"until\": \"2024-01-15T11:00:00Z\",\n    \"limit\": 100,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/events?since=2024-01-15T11:00:00Z&until=2024-01-15T12:00:00Z&limit=100\"\n  }\n}\n```\n\n**Use for:**\n- Time-series data\n- Logs and events\n- Activity streams\n- Analytics data\n\n## Default Limits\n\nAlways set reasonable defaults and maximum limits:\n\n```json\n{\n  \"default_limit\": 20,\n  \"max_limit\": 100,\n  \"min_limit\": 1\n}\n```\n\n**Validation:**\n```http\nGET /users?limit=1000\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"INVALID_LIMIT\",\n    \"message\": \"Limit must be between 1 and 100. Default is 20.\"\n  }\n}\n```\n\n## Response Format\n\n### Standard Pagination Object\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"limit\": 10,\n    \"offset\": 20,\n    \"total\": 150,\n    \"has_more\": true,\n    \"has_previous\": true\n  }\n}\n```\n\n### Link Header (RFC 5988)\n\n```http\nLink: </users?offset=0&limit=10>; rel=\"first\",\n      </users?offset=10&limit=10>; rel=\"prev\",\n      </users?offset=30&limit=10>; rel=\"next\",\n      </users?offset=140&limit=10>; rel=\"last\"\n```\n\n**Used by:** GitHub API\n\n### Embedded Links\n\n```json\n{\n  \"data\": [...],\n  \"_links\": {\n    \"self\": { \"href\": \"/users?offset=20&limit=10\" },\n    \"first\": { \"href\": \"/users?offset=0&limit=10\" },\n    \"prev\": { \"href\": \"/users?offset=10&limit=10\" },\n    \"next\": { \"href\": \"/users?offset=30&limit=10\" },\n    \"last\": { \"href\": \"/users?offset=140&limit=10\" }\n  }\n}\n```\n\n## Sorting with Pagination\n\nAlways support sorting when paginating:\n\n```http\nGET /users?sort=created_at&order=desc&limit=10\nGET /users?sort=-created_at&limit=10                    # Descending\nGET /users?sort=last_name,first_name&limit=10           # Multi-field\n```\n\n**For cursor pagination, cursor must include sort fields:**\n```json\n{\n  \"cursor\": {\n    \"id\": 123,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"sort_fields\": [\"created_at\", \"id\"]\n  }\n}\n```\n\n## Filtering with Pagination\n\nCombine filtering with pagination:\n\n```http\nGET /users?status=active&role=admin&offset=0&limit=10\n```\n\n**Important:** Apply filters before pagination:\n1. Filter records\n2. Count filtered results\n3. Apply pagination\n4. Return paginated subset\n\n## Total Count\n\n### Include Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"total\": 1523,\n    \"limit\": 10,\n    \"offset\": 20\n  }\n}\n```\n\n**Pros:**\n- Clients know total results\n- Can calculate total pages\n- Better UX (show \"Page 3 of 153\")\n\n**Cons:**\n- COUNT query is expensive\n- Slows down response\n- Inaccurate for large/changing datasets\n\n### Omit Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"has_more\": true,\n    \"limit\": 10\n  }\n}\n```\n\n**Use when:**\n- Large datasets (COUNT is too slow)\n- Real-time data (count changes constantly)\n- Cursor pagination\n- Infinite scroll UI\n\n### Optional Total Count\n\nLet client request total count:\n\n```http\nGET /users?limit=10&include_total=true\n```\n\n## Edge Cases\n\n### Empty Results\n\n```json\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 0,\n    \"limit\": 10,\n    \"total\": 0,\n    \"has_more\": false\n  }\n}\n```\n\n### Last Page\n\n```json\n{\n  \"data\": [{\"id\": 150, \"name\": \"Last User\"}],\n  \"pagination\": {\n    \"offset\": 140,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=130&limit=10\",\n    \"next\": null\n  }\n}\n```\n\n### Out of Range\n\n```http\nGET /users?offset=10000&limit=10\n\nResponse: 200 OK (empty results)\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 10000,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  }\n}\n```\n\nOr return 404 for pages that don't exist:\n```http\nGET /users?page=1000&per_page=10\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"PAGE_NOT_FOUND\",\n    \"message\": \"Page 1000 does not exist. Total pages: 15\"\n  }\n}\n```\n\n## Best Practices\n\n1. **Always paginate collections** - Never return unbounded lists\n2. **Set reasonable defaults** - Default limit of 20-50 items\n3. **Enforce maximum limits** - Prevent excessive loads (max 100-1000)\n4. **Include has_more flag** - Tell clients if more results exist\n5. **Provide navigation links** - Make it easy to get next/prev pages\n6. **Document pagination** - Explain cursor format, limits, defaults\n7. **Be consistent** - Use same pagination pattern across all endpoints\n8. **Consider performance** - Choose strategy based on data size/type\n9. **Support sorting** - Let clients control result order\n10. **Handle edge cases** - Empty results, last page, invalid cursors\n\n## Comparison Matrix\n\n| Feature | Offset | Page | Cursor | Keyset |\n|---------|--------|------|--------|--------|\n| Performance | Poor for large offsets | Poor | Excellent | Excellent |\n| Random access | Yes | Yes | No | No |\n| Total count | Yes | Yes | No | Optional |\n| Consistency | Poor | Poor | Excellent | Excellent |\n| Complexity | Simple | Simple | Medium | Medium |\n| Real-time data | Poor | Poor | Excellent | Excellent |\n| Database load | High | High | Low | Low |\n| Use case | Small datasets | Web UIs | Feeds/streams | Large datasets |\n","skills/api-designer/references/rest-patterns.md":"# REST Design Patterns\n\n## Resource-Oriented Architecture\n\nREST APIs are built around resources, not actions. Resources are the nouns of your API.\n\n### Resource Identification\n\n**Good Resource URIs:**\n```\nGET    /users                  # Collection\nGET    /users/{id}             # Individual resource\nGET    /users/{id}/orders      # Nested collection\nPOST   /users                  # Create resource\nPUT    /users/{id}             # Replace resource\nPATCH  /users/{id}             # Update resource\nDELETE /users/{id}             # Delete resource\n```\n\n**Bad Resource URIs:**\n```\nPOST   /getUser                # Verb in URI\nPOST   /createUser             # Verb in URI\nGET    /user?action=delete     # Action as query param\n```\n\n### Resource Naming Conventions\n\n- Use plural nouns for collections: `/users`, `/orders`, `/products`\n- Use lowercase and hyphens for readability: `/shipping-addresses`\n- Avoid deep nesting (max 2-3 levels): `/users/{id}/orders/{orderId}`\n- Use query parameters for filtering: `/users?status=active&role=admin`\n\n## HTTP Method Semantics\n\n### Safe and Idempotent Methods\n\n| Method | Safe | Idempotent | Use Case |\n|--------|------|------------|----------|\n| GET | Yes | Yes | Retrieve resource(s) |\n| POST | No | No | Create resource, non-idempotent operations |\n| PUT | No | Yes | Replace entire resource |\n| PATCH | No | No | Partial update |\n| DELETE | No | Yes | Remove resource |\n| HEAD | Yes | Yes | Get metadata only |\n| OPTIONS | Yes | Yes | Get allowed methods |\n\n### Method Usage\n\n**GET - Retrieve Resources**\n```http\nGET /users/123\nAccept: application/json\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"created_at\": \"2024-01-15T10:30:00Z\"\n}\n```\n\n**POST - Create Resources**\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\"\n}\n\nResponse: 201 Created\nLocation: /users/124\n{\n  \"id\": 124,\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\",\n  \"created_at\": \"2024-01-16T14:20:00Z\"\n}\n```\n\n**PUT - Replace Resource**\n```http\nPUT /users/123\nContent-Type: application/json\n\n{\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\",\n  \"updated_at\": \"2024-01-17T09:15:00Z\"\n}\n```\n\n**PATCH - Partial Update**\n```http\nPATCH /users/123\nContent-Type: application/json\n\n{\n  \"email\": \"john.updated@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john.updated@example.com\",\n  \"updated_at\": \"2024-01-17T10:00:00Z\"\n}\n```\n\n**DELETE - Remove Resource**\n```http\nDELETE /users/123\n\nResponse: 204 No Content\n```\n\n## HTTP Status Codes\n\n### Success Codes (2xx)\n\n- **200 OK** - Request succeeded (GET, PUT, PATCH)\n- **201 Created** - Resource created (POST), include Location header\n- **202 Accepted** - Request accepted for async processing\n- **204 No Content** - Success with no response body (DELETE)\n\n### Redirection (3xx)\n\n- **301 Moved Permanently** - Resource permanently moved\n- **302 Found** - Temporary redirect\n- **304 Not Modified** - Cached version is still valid\n\n### Client Errors (4xx)\n\n- **400 Bad Request** - Invalid request syntax or validation error\n- **401 Unauthorized** - Authentication required or failed\n- **403 Forbidden** - Authenticated but not authorized\n- **404 Not Found** - Resource doesn't exist\n- **405 Method Not Allowed** - HTTP method not supported for resource\n- **409 Conflict** - Request conflicts with current state (e.g., duplicate)\n- **422 Unprocessable Entity** - Valid syntax but semantic errors\n- **429 Too Many Requests** - Rate limit exceeded\n\n### Server Errors (5xx)\n\n- **500 Internal Server Error** - Unexpected server error\n- **502 Bad Gateway** - Invalid response from upstream server\n- **503 Service Unavailable** - Server temporarily unavailable\n- **504 Gateway Timeout** - Upstream server timeout\n\n## HATEOAS (Hypermedia)\n\n### Hypermedia-Driven APIs\n\nInclude links to related resources and available actions:\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" },\n    \"orders\": { \"href\": \"/users/123/orders\" },\n    \"update\": { \"href\": \"/users/123\", \"method\": \"PATCH\" },\n    \"delete\": { \"href\": \"/users/123\", \"method\": \"DELETE\" }\n  }\n}\n```\n\n### HAL (Hypertext Application Language)\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" }\n  },\n  \"_embedded\": {\n    \"orders\": [\n      {\n        \"id\": 456,\n        \"total\": 99.99,\n        \"_links\": {\n          \"self\": { \"href\": \"/orders/456\" }\n        }\n      }\n    ]\n  }\n}\n```\n\n## Content Negotiation\n\n### Accept Headers\n\n```http\nGET /users/123\nAccept: application/json\n\nGET /users/123\nAccept: application/xml\n\nGET /users/123\nAccept: application/hal+json\n```\n\n### Response Content-Type\n\n```http\nContent-Type: application/json; charset=utf-8\nContent-Type: application/problem+json\nContent-Type: application/hal+json\n```\n\n## Idempotency\n\n### Idempotent Operations\n\n**PUT - Always idempotent:**\nMultiple identical PUT requests produce the same result as a single request.\n\n**DELETE - Idempotent:**\nFirst DELETE returns 204, subsequent DELETEs return 404 (same end state).\n\n**POST - Not idempotent by default:**\nUse `Idempotency-Key` header for idempotent POST:\n\n```http\nPOST /payments\nIdempotency-Key: 550e8400-e29b-41d4-a716-446655440000\nContent-Type: application/json\n\n{\n  \"amount\": 100.00,\n  \"currency\": \"USD\"\n}\n```\n\nServer stores idempotency key and returns same response for duplicate requests.\n\n## Cache Control\n\n### Cache Headers\n\n```http\nCache-Control: public, max-age=3600\nCache-Control: private, no-cache\nCache-Control: no-store\nETag: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nLast-Modified: Wed, 15 Jan 2024 10:30:00 GMT\n```\n\n### Conditional Requests\n\n```http\nGET /users/123\nIf-None-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\n\nResponse: 304 Not Modified\n```\n\n```http\nPUT /users/123\nIf-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nContent-Type: application/json\n\n{\n  \"name\": \"Updated Name\"\n}\n\nResponse: 412 Precondition Failed (if ETag doesn't match)\n```\n\n## URI Patterns\n\n### Consistent URI Structure\n\n```\n/{version}/{resource}\n/{version}/{resource}/{id}\n/{version}/{resource}/{id}/{sub-resource}\n/{version}/{resource}/{id}/{sub-resource}/{sub-id}\n```\n\n### Query Parameters\n\n**Filtering:**\n```\nGET /users?status=active&role=admin\nGET /products?category=electronics&price_min=100&price_max=500\n```\n\n**Sorting:**\n```\nGET /users?sort=created_at\nGET /users?sort=-created_at          # Descending\nGET /users?sort=name,created_at      # Multiple fields\n```\n\n**Field Selection:**\n```\nGET /users?fields=id,name,email\nGET /users?exclude=password,social_security_number\n```\n\n**Search:**\n```\nGET /users?q=john\nGET /products?search=laptop\n```\n\n## Best Practices\n\n1. **Use nouns, not verbs** - Resources are nouns, methods are verbs\n2. **Plural collections** - Use `/users` not `/user`\n3. **Consistent naming** - Choose snake_case or camelCase and stick to it\n4. **Proper status codes** - Use appropriate HTTP status codes\n5. **Include metadata** - Pagination, filtering, sorting info in responses\n6. **Version your API** - Plan for evolution from day one\n7. **Document everything** - OpenAPI specs, examples, error codes\n8. **Security by default** - HTTPS, authentication, rate limiting\n9. **Support filtering** - Enable clients to get exactly what they need\n10. **Implement HATEOAS** - Make APIs self-documenting and discoverable\n","skills/api-designer/references/versioning.md":"# API Versioning Strategies\n\n## Why Version APIs?\n\nAPI versioning allows you to evolve your API while maintaining backward compatibility for existing clients. Breaking changes require a new version.\n\n### Breaking Changes\n\nChanges that require a new version:\n- Removing or renaming fields\n- Changing field types (string to integer)\n- Adding required fields to requests\n- Changing response structure\n- Removing endpoints\n- Changing HTTP status codes for same scenario\n- Changing authentication mechanisms\n\n### Non-Breaking Changes\n\nSafe changes that don't require a new version:\n- Adding new endpoints\n- Adding optional request fields\n- Adding new fields to responses (clients should ignore unknown fields)\n- Fixing bugs\n- Performance improvements\n- Adding new HTTP methods to existing resources\n\n## Versioning Strategies\n\n### 1. URI Versioning\n\nMost common and visible approach. Version is part of the URL path.\n\n```http\nGET /v1/users/123\nGET /v2/users/123\n```\n\n**Advantages:**\n- Clear and visible in URLs\n- Easy to understand and implement\n- Simple routing and caching\n- Can run multiple versions simultaneously\n\n**Disadvantages:**\n- Violates REST principle (same resource, different URIs)\n- Requires updating client code to change version\n- Can lead to URI proliferation\n\n**Implementation:**\n```\n/v1/users\n/v1/products\n/v2/users      # New version with breaking changes\n/v2/products\n```\n\n### 2. Header Versioning\n\nVersion specified in HTTP headers (Accept header or custom header).\n\n**Accept Header:**\n```http\nGET /users/123\nAccept: application/vnd.myapi.v1+json\n\nGET /users/123\nAccept: application/vnd.myapi.v2+json\n```\n\n**Custom Header:**\n```http\nGET /users/123\nAPI-Version: 1\n\nGET /users/123\nAPI-Version: 2\n```\n\n**Advantages:**\n- URIs remain stable\n- More RESTful (same resource, same URI)\n- Separates versioning from resource identification\n\n**Disadvantages:**\n- Less visible (harder to debug)\n- More complex routing\n- Difficult to test in browser\n- Cache complexity\n\n### 3. Query Parameter Versioning\n\nVersion specified as query parameter.\n\n```http\nGET /users/123?version=1\nGET /users/123?version=2\n\n# or\nGET /users/123?api-version=1\nGET /users/123?api-version=2\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to test\n- Visible in URLs\n\n**Disadvantages:**\n- Pollutes query string\n- Not semantic (version not a filter)\n- Can interfere with other query params\n\n### 4. Content Negotiation\n\nClient specifies desired version through content negotiation.\n\n```http\nGET /users/123\nAccept: application/vnd.myapi+json; version=1\n\nGET /users/123\nAccept: application/vnd.myapi+json; version=2\n```\n\n**Advantages:**\n- Very RESTful\n- Flexible content type negotiation\n- Stable URIs\n\n**Disadvantages:**\n- Complex implementation\n- Less intuitive for developers\n- Harder to test\n\n## Recommended Approach\n\n**URI versioning is recommended for most APIs** because:\n- It's the most explicit and discoverable\n- Easy to understand and debug\n- Simple to implement and maintain\n- Clear separation between versions\n\n```\n/v1/users\n/v2/users\n/v3/users\n```\n\n## Version Format\n\n### Major Versions Only\n\nUse simple major versions (v1, v2, v3) for public APIs:\n```\n/v1/users\n/v2/users\n```\n\n**Advantages:**\n- Simple and clear\n- Easy to communicate\n- Forces thoughtful breaking changes\n\n### Date-Based Versions\n\nSome APIs use dates for versions:\n```\n/2024-01-01/users\n/2024-06-15/users\n```\n\n**Used by:** Stripe, GitHub API\n\n**Advantages:**\n- Clear when version was released\n- Easy to understand timeline\n- No confusion about major/minor\n\n**Disadvantages:**\n- Less intuitive for clients\n- Harder to understand what changed\n\n## Version Lifecycle\n\n### 1. Introduction Phase\n\nNew version is released alongside existing version:\n```\n/v1/users  # Still supported\n/v2/users  # New version available\n```\n\nAnnounce new version:\n- Blog post explaining changes\n- Migration guide\n- Breaking changes list\n- Timeline for v1 deprecation\n\n### 2. Deprecation Phase\n\nMark old version as deprecated but keep it running:\n\n```http\nGET /v1/users/123\n\nResponse:\nDeprecation: true\nSunset: Wed, 15 Jan 2025 00:00:00 GMT\nLink: </v2/users/123>; rel=\"successor-version\"\n\n{\n  \"id\": 123,\n  \"name\": \"John Doe\"\n}\n```\n\n**Deprecation Headers:**\n- `Deprecation: true` - Indicates version is deprecated\n- `Sunset: <date>` - When version will be removed (RFC 8594)\n- `Link: <url>; rel=\"successor-version\"` - Points to new version\n\n### 3. Sunset Phase\n\nOld version is shut down on announced date.\n\nReturn 410 Gone for deprecated endpoints:\n```http\nGET /v1/users/123\n\nResponse: 410 Gone\n{\n  \"error\": {\n    \"code\": \"VERSION_SUNSET\",\n    \"message\": \"API v1 was sunset on 2025-01-15. Please use v2.\",\n    \"documentation_url\": \"https://api.example.com/docs/migration-v1-to-v2\"\n  }\n}\n```\n\n## Deprecation Policy\n\n### Recommended Timeline\n\n1. **Announce deprecation** - At least 6 months before sunset\n2. **Support period** - Run both versions for 6-12 months\n3. **Sunset date** - Clear date communicated in advance\n4. **Grace period** - 30 days of 410 Gone responses before complete shutdown\n\n### Communication Channels\n\n- API response headers\n- Email to registered developers\n- Blog posts and changelog\n- Dashboard notifications\n- Documentation updates\n- Status page announcements\n\n## Migration Strategy\n\n### Provide Migration Guide\n\n```markdown\n# Migrating from v1 to v2\n\n## Breaking Changes\n\n### User Resource Changes\n\n**v1:**\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**v2:**\n```json\n{\n  \"id\": 123,\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**Migration:**\n- Split `name` field into `first_name` and `last_name`\n- Update client code to use new fields\n```\n\n### Offer Tools\n\n- Migration scripts\n- SDK updates\n- API diff viewer\n- Compatibility layer (temporary)\n\n## Version Discovery\n\n### Root Endpoint\n\n```http\nGET /\n\nResponse:\n{\n  \"versions\": {\n    \"v1\": {\n      \"status\": \"deprecated\",\n      \"sunset_date\": \"2025-01-15\",\n      \"documentation_url\": \"https://api.example.com/docs/v1\"\n    },\n    \"v2\": {\n      \"status\": \"current\",\n      \"documentation_url\": \"https://api.example.com/docs/v2\"\n    },\n    \"v3\": {\n      \"status\": \"beta\",\n      \"documentation_url\": \"https://api.example.com/docs/v3\"\n    }\n  }\n}\n```\n\n### Version Info Endpoint\n\n```http\nGET /v2/version\n\nResponse:\n{\n  \"version\": \"v2\",\n  \"released\": \"2024-01-15\",\n  \"status\": \"stable\",\n  \"sunset_date\": null\n}\n```\n\n## OpenAPI Versioning\n\n### Separate Specs per Version\n\n```\nopenapi-v1.yaml\nopenapi-v2.yaml\nopenapi-v3.yaml\n```\n\nEach spec is complete and independent.\n\n### Single Spec with Servers\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 2.0.0\nservers:\n  - url: https://api.example.com/v1\n    description: Version 1 (deprecated)\n  - url: https://api.example.com/v2\n    description: Version 2 (current)\n```\n\n## Best Practices\n\n1. **Version from day one** - Start with /v1, not /api\n2. **Major versions only** - Use v1, v2, v3 (not v1.1, v1.2)\n3. **Long deprecation periods** - Give clients time to migrate (6-12 months)\n4. **Clear communication** - Use headers, docs, emails\n5. **Maintain old versions** - Support at least 2 versions simultaneously\n6. **Document changes** - Provide detailed migration guides\n7. **Use semantic versioning** - For internal/SDK versioning\n8. **Never break without warning** - Always announce breaking changes\n9. **Provide tools** - Migration scripts, updated SDKs\n10. **Monitor usage** - Track which versions are being used\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Breaking changes without version bump** - Breaks existing clients\n- **Too many versions** - Maintenance nightmare (max 2-3 active versions)\n- **Short deprecation periods** - Frustrates developers\n- **No migration path** - Makes upgrades painful\n- **Surprise sunsets** - Breaks production apps without warning\n- **Inconsistent versioning** - Different strategies for different endpoints\n- **Versioning individual endpoints** - Use consistent version across API\n","skills/architecture-designer/SKILL.md":"---\nname: architecture-designer\ndescription: Use when designing new high-level system architecture, reviewing existing designs, or making architectural decisions. Invoke to create architecture diagrams, write Architecture Decision Records (ADRs), evaluate technology trade-offs, design component interactions, and plan for scalability. Use for system design, architecture review, microservices structuring, ADR authoring, scalability planning, and infrastructure pattern selection — distinct from code-level design patterns or database-only design tasks.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.1\"\n  domain: api-architecture\n  triggers: architecture, system design, design pattern, microservices, scalability, ADR, technical design, infrastructure\n  role: expert\n  scope: design\n  output-format: document\n  related-skills: fullstack-guardian, devops-engineer, secure-code-guardian, microservices-architect, code-reviewer\n---\n\n# Architecture Designer\n\nSenior software architect specializing in system design, design patterns, and architectural decision-making.\n\n## Role Definition\n\nYou are a principal architect with 15+ years of experience designing scalable, distributed systems. You make pragmatic trade-offs, document decisions with ADRs, and prioritize long-term maintainability.\n\n## When to Use This Skill\n\n- Designing new system architecture\n- Choosing between architectural patterns\n- Reviewing existing architecture\n- Creating Architecture Decision Records (ADRs)\n- Planning for scalability\n- Evaluating technology choices\n\n## Core Workflow\n\n1. **Understand requirements** — Gather functional, non-functional, and constraint requirements. _Verify full requirements coverage before proceeding._\n2. **Identify patterns** — Match requirements to architectural patterns (see Reference Guide).\n3. **Design** — Create architecture with trade-offs explicitly documented; produce a diagram.\n4. **Document** — Write ADRs for all key decisions.\n5. **Review** — Validate with stakeholders. _If review fails, return to step 3 with recorded feedback._\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Architecture Patterns | `references/architecture-patterns.md` | Choosing monolith vs microservices |\n| ADR Template | `references/adr-template.md` | Documenting decisions |\n| System Design | `references/system-design.md` | Full system design template |\n| Database Selection | `references/database-selection.md` | Choosing database technology |\n| NFR Checklist | `references/nfr-checklist.md` | Gathering non-functional requirements |\n\n## Constraints\n\n### MUST DO\n- Document all significant decisions with ADRs\n- Consider non-functional requirements explicitly\n- Evaluate trade-offs, not just benefits\n- Plan for failure modes\n- Consider operational complexity\n- Review with stakeholders before finalizing\n\n### MUST NOT DO\n- Over-engineer for hypothetical scale\n- Choose technology without evaluating alternatives\n- Ignore operational costs\n- Design without understanding requirements\n- Skip security considerations\n\n## Output Templates\n\nWhen designing architecture, provide:\n1. Requirements summary (functional + non-functional)\n2. High-level architecture diagram (Mermaid preferred — see example below)\n3. Key decisions with trade-offs (ADR format — see example below)\n4. Technology recommendations with rationale\n5. Risks and mitigation strategies\n\n### Architecture Diagram (Mermaid)\n\n```mermaid\ngraph TD\n    Client[\"Client (Web/Mobile)\"] --> Gateway[\"API Gateway\"]\n    Gateway --> AuthSvc[\"Auth Service\"]\n    Gateway --> OrderSvc[\"Order Service\"]\n    OrderSvc --> DB[(\"Orders DB\\n(PostgreSQL)\")]\n    OrderSvc --> Queue[\"Message Queue\\n(RabbitMQ)\"]\n    Queue --> NotifySvc[\"Notification Service\"]\n```\n\n### ADR Example\n\n```markdown\n# ADR-001: Use PostgreSQL for Order Storage\n\n## Status\nAccepted\n\n## Context\nThe Order Service requires ACID-compliant transactions and complex relational queries\nacross orders, line items, and customers.\n\n## Decision\nUse PostgreSQL as the primary datastore for the Order Service.\n\n## Alternatives Considered\n- **MongoDB** — flexible schema, but lacks strong ACID guarantees across documents.\n- **DynamoDB** — excellent scalability, but complex query patterns require denormalization.\n\n## Consequences\n- Positive: Strong consistency, mature tooling, complex query support.\n- Negative: Vertical scaling limits; horizontal sharding adds operational complexity.\n\n## Trade-offs\nConsistency and query flexibility are prioritised over unlimited horizontal write scalability.\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/architecture-designer/)\n","skills/architecture-designer/references/adr-template.md":"# ADR Template\n\n## ADR Format\n\n```markdown\n# ADR-{number}: {Title}\n\n## Status\n[Proposed | Accepted | Deprecated | Superseded by ADR-XXX]\n\n## Context\n[Describe the situation and forces at play. What is the problem?\nWhat constraints exist? What are we trying to achieve?]\n\n## Decision\n[State the decision clearly. What are we going to do?]\n\n## Consequences\n\n### Positive\n- [Benefit 1]\n- [Benefit 2]\n\n### Negative\n- [Drawback 1]\n- [Drawback 2]\n\n### Neutral\n- [Side effect that is neither good nor bad]\n\n## Alternatives Considered\n[What other options were evaluated and why were they rejected?]\n\n## References\n- [Link to relevant documentation]\n- [Link to discussion/RFC]\n```\n\n## Example: Database Selection\n\n```markdown\n# ADR-001: Use PostgreSQL for primary database\n\n## Status\nAccepted\n\n## Context\nWe need a relational database for our e-commerce platform that:\n- Handles complex transactions with strong consistency\n- Supports JSON for flexible product attributes\n- Scales to millions of products and orders\n- Works well with our existing Python/Node stack\n\nTeam has experience with PostgreSQL and MySQL.\nBudget allows for managed database service.\n\n## Decision\nUse PostgreSQL as the primary database, hosted on AWS RDS.\n\n## Consequences\n\n### Positive\n- ACID compliance for financial transactions\n- Rich feature set (JSON, full-text search, CTEs)\n- Strong community and tooling\n- Excellent performance with proper indexing\n- Free and open source\n\n### Negative\n- Vertical scaling has limits (addressed with read replicas)\n- Requires DBA expertise for optimization\n- AWS RDS costs for high availability\n\n### Neutral\n- Team will need to learn PostgreSQL-specific features\n- Migration from current SQLite dev database needed\n\n## Alternatives Considered\n\n**MySQL**\n- Rejected: Less feature-rich for JSON operations\n- Considered: Similar cost, familiar to team\n\n**MongoDB**\n- Rejected: Relational data model needed for orders/inventory\n- Considered: Great for product catalog flexibility\n\n**CockroachDB**\n- Rejected: Higher cost, team unfamiliar\n- Considered: Better horizontal scaling\n\n## References\n- https://www.postgresql.org/docs/current/\n- Internal RFC: Database Selection for E-commerce Platform\n```\n\n## ADR Naming Convention\n\n```\ndocs/\n└── adr/\n    ├── 0001-use-postgresql-database.md\n    ├── 0002-adopt-microservices.md\n    ├── 0003-implement-event-sourcing.md\n    └── README.md\n```\n\n## Quick Reference\n\n| Section | Purpose | Key Question |\n|---------|---------|--------------|\n| Status | Current state | Is this active? |\n| Context | Background | Why are we deciding? |\n| Decision | The choice | What did we choose? |\n| Consequences | Impact | What happens now? |\n| Alternatives | Options | What else was considered? |\n","skills/architecture-designer/references/architecture-patterns.md":"# Architecture Patterns\n\n## Pattern Comparison\n\n| Pattern | Best For | Team Size | Trade-offs |\n|---------|----------|-----------|------------|\n| **Monolith** | Simple domain, small team | 1-10 | Simple deploy; hard to scale parts |\n| **Modular Monolith** | Growing complexity | 5-20 | Module boundaries; still single deploy |\n| **Microservices** | Complex domain, large org | 20+ | Independent scale; operational complexity |\n| **Serverless** | Variable load, event-driven | Any | Auto-scale; cold starts, vendor lock |\n| **Event-Driven** | Async processing | 10+ | Loose coupling; debugging complexity |\n\n## Monolith\n\n```\n┌─────────────────────────────────────┐\n│            Application              │\n│  ┌─────┐  ┌─────┐  ┌─────┐         │\n│  │Users│  │Orders│ │Products│       │\n│  └─────┘  └─────┘  └─────┘         │\n│  └──────────┬──────────────┘        │\n│          Database                    │\n└─────────────────────────────────────┘\n```\n\n**When to Use**:\n- Starting a new project\n- Small team (< 10 developers)\n- Simple domain\n- Rapid iteration needed\n\n**Pros**: Simple deployment, easy debugging, no network latency\n**Cons**: Hard to scale independently, technology locked, deployment risk\n\n## Microservices\n\n```\n┌──────────┐  ┌──────────┐  ┌──────────┐\n│  Users   │  │  Orders  │  │ Products │\n│ Service  │  │ Service  │  │ Service  │\n└────┬─────┘  └────┬─────┘  └────┬─────┘\n     │             │             │\n┌────▼────┐  ┌────▼────┐  ┌────▼────┐\n│ User DB │  │Order DB │  │ Prod DB │\n└─────────┘  └─────────┘  └─────────┘\n```\n\n**When to Use**:\n- Large team (20+ developers)\n- Complex domain with clear boundaries\n- Different scaling requirements per service\n- Polyglot technology needs\n\n**Pros**: Independent scaling, team autonomy, fault isolation\n**Cons**: Distributed system complexity, eventual consistency, operational overhead\n\n## Event-Driven\n\n```\n┌──────────┐     ┌─────────────┐     ┌──────────┐\n│ Producer │────▶│ Message Bus │────▶│ Consumer │\n└──────────┘     │  (Kafka)    │     └──────────┘\n                 └─────────────┘\n                       │\n                       ▼\n                 ┌──────────┐\n                 │ Consumer │\n                 └──────────┘\n```\n\n**When to Use**:\n- Async processing required\n- Loose coupling between services\n- Event sourcing needs\n- High throughput messaging\n\n**Pros**: Decoupled services, scalable, audit trail\n**Cons**: Eventual consistency, debugging complexity, message ordering\n\n## CQRS (Command Query Responsibility Segregation)\n\n```\n┌─────────┐         ┌─────────────┐\n│ Commands│────────▶│ Write Model │──┐\n└─────────┘         └─────────────┘  │\n                                     ▼\n                              ┌──────────┐\n                              │  Events  │\n                              └──────────┘\n                                     │\n┌─────────┐         ┌─────────────┐  │\n│ Queries │◀────────│ Read Model  │◀─┘\n└─────────┘         └─────────────┘\n```\n\n**When to Use**:\n- Read/write ratio heavily skewed\n- Complex read queries\n- Event sourcing architecture\n- Different optimization needs\n\n## Quick Reference\n\n| Requirement | Recommended Pattern |\n|-------------|---------------------|\n| Simple CRUD app | Monolith |\n| Growing startup | Modular Monolith |\n| Enterprise scale | Microservices |\n| Variable load | Serverless |\n| Async processing | Event-Driven |\n| Read-heavy | CQRS |\n","skills/architecture-designer/references/database-selection.md":"# Database Selection\n\n## Database Types\n\n| Type | Examples | Best For |\n|------|----------|----------|\n| **Relational** | PostgreSQL, MySQL | Transactions, complex queries, relationships |\n| **Document** | MongoDB, Firestore | Flexible schemas, rapid iteration |\n| **Key-Value** | Redis, DynamoDB | Caching, sessions, high throughput |\n| **Time-Series** | TimescaleDB, InfluxDB | Metrics, IoT, analytics |\n| **Graph** | Neo4j, Neptune | Relationships, social networks |\n| **Search** | Elasticsearch, Meilisearch | Full-text search, logs |\n\n## Relational (PostgreSQL, MySQL)\n\n```\nBest For:\n- Financial transactions (ACID compliance)\n- Complex queries with joins\n- Data integrity requirements\n- Structured, predictable schemas\n\nWhen to Avoid:\n- Highly variable schemas\n- Massive horizontal scaling needs\n- Simple key-value access patterns\n```\n\n| Feature | PostgreSQL | MySQL |\n|---------|------------|-------|\n| JSON support | Excellent (JSONB) | Good (JSON) |\n| Full-text search | Built-in | Basic |\n| Extensions | Rich ecosystem | Limited |\n| Replication | Streaming, logical | Statement, row-based |\n\n## Document (MongoDB, Firestore)\n\n```\nBest For:\n- Flexible, evolving schemas\n- Hierarchical data (nested documents)\n- Rapid prototyping\n- Content management\n\nWhen to Avoid:\n- Complex transactions across documents\n- Heavy relational queries\n- Strict schema requirements\n```\n\n## Key-Value (Redis, DynamoDB)\n\n```\nBest For:\n- Session storage\n- Caching layer\n- Real-time leaderboards\n- Rate limiting counters\n\nWhen to Avoid:\n- Complex queries\n- Relational data\n- Large value sizes (>1MB)\n```\n\n## Time-Series (TimescaleDB, InfluxDB)\n\n```\nBest For:\n- Metrics and monitoring\n- IoT sensor data\n- Financial tick data\n- Event logging with timestamps\n\nWhen to Avoid:\n- Frequent updates to existing records\n- Complex relational queries\n- Non-time-based access patterns\n```\n\n## Decision Matrix\n\n| Requirement | Recommended |\n|-------------|-------------|\n| ACID transactions | PostgreSQL, MySQL |\n| Flexible schema | MongoDB, Firestore |\n| High-speed caching | Redis |\n| Time-series data | TimescaleDB, InfluxDB |\n| Social relationships | Neo4j |\n| Full-text search | Elasticsearch |\n| Serverless scale | DynamoDB, Firestore |\n\n## Quick Reference\n\n| Question | If Yes → |\n|----------|----------|\n| Need ACID transactions? | Relational (PostgreSQL) |\n| Schema changes frequently? | Document (MongoDB) |\n| Sub-millisecond reads? | Key-Value (Redis) |\n| Time-based queries? | Time-Series |\n| Traversing relationships? | Graph (Neo4j) |\n| Full-text search primary? | Elasticsearch |\n","skills/architecture-designer/references/nfr-checklist.md":"# Non-Functional Requirements Checklist\n\n## NFR Categories\n\n### Scalability\n\n| Question | Common Targets |\n|----------|----------------|\n| Expected concurrent users? | 100 / 1K / 10K / 100K |\n| Requests per second? | 10 / 100 / 1000 / 10000 |\n| Data volume? | GB / TB / PB |\n| Growth rate? | 10% / 50% / 100% per year |\n| Peak vs average load? | 2x / 5x / 10x |\n\n### Performance\n\n| Question | Common Targets |\n|----------|----------------|\n| API response time? | < 100ms / 200ms / 500ms p95 |\n| Page load time? | < 1s / 2s / 3s |\n| Database query time? | < 10ms / 50ms / 100ms |\n| Batch processing throughput? | 1K / 10K / 100K records/hour |\n\n### Availability\n\n| Target | Downtime/Year | Use Case |\n|--------|---------------|----------|\n| 99% | 3.65 days | Internal tools |\n| 99.9% | 8.76 hours | Business apps |\n| 99.95% | 4.38 hours | E-commerce |\n| 99.99% | 52.6 minutes | Financial systems |\n| 99.999% | 5.26 minutes | Life-critical |\n\n### Security\n\n| Question | Considerations |\n|----------|----------------|\n| Authentication required? | JWT, OAuth, SAML, MFA |\n| Authorization model? | RBAC, ABAC, ACL |\n| Data sensitivity? | Public, internal, confidential, PII |\n| Compliance requirements? | GDPR, HIPAA, PCI DSS, SOC 2 |\n| Encryption needs? | At rest, in transit, end-to-end |\n\n### Reliability\n\n| Question | Considerations |\n|----------|----------------|\n| Acceptable data loss? | RPO: 0 / 1hr / 24hr |\n| Recovery time target? | RTO: 1hr / 4hr / 24hr |\n| Backup frequency? | Real-time / hourly / daily |\n| Disaster recovery? | Single region / multi-region |\n\n### Maintainability\n\n| Question | Considerations |\n|----------|----------------|\n| Deployment frequency? | Daily / weekly / monthly |\n| Deployment strategy? | Blue-green, canary, rolling |\n| Monitoring requirements? | Logs, metrics, traces, alerts |\n| On-call requirements? | 24/7, business hours |\n\n### Cost\n\n| Question | Considerations |\n|----------|----------------|\n| Infrastructure budget? | $/month, $/user, $/request |\n| Operational budget? | FTE for maintenance |\n| Cost optimization? | Reserved instances, spot instances |\n| Cost alerts? | Thresholds for notification |\n\n## Template\n\n```markdown\n## Non-Functional Requirements\n\n### Performance\n- API response time: < 200ms p95\n- Page load time: < 2s\n- Database query time: < 50ms\n\n### Scalability\n- Concurrent users: 10,000\n- Requests per second: 1,000\n- Data volume: 1TB\n\n### Availability\n- Target: 99.9% (8.76 hours/year downtime)\n- RPO: 1 hour\n- RTO: 4 hours\n\n### Security\n- Authentication: JWT with refresh tokens\n- Authorization: Role-based (admin, user, guest)\n- Compliance: GDPR, SOC 2\n\n### Observability\n- Logging: Structured JSON to ELK\n- Metrics: Prometheus + Grafana\n- Tracing: OpenTelemetry\n- Alerts: PagerDuty integration\n```\n\n## Quick Reference\n\n| Category | Key Metric |\n|----------|------------|\n| Performance | Response time (p95) |\n| Scalability | Concurrent users, RPS |\n| Availability | Uptime percentage |\n| Reliability | RPO, RTO |\n| Security | Compliance requirements |\n| Cost | $/month budget |\n","skills/architecture-designer/references/system-design.md":"# System Design Template\n\n## Design Template\n\n```markdown\n# System: {System Name}\n\n## Requirements\n\n### Functional\n- [What the system must do]\n- [Core features and capabilities]\n\n### Non-Functional\n- **Performance**: Response time < 200ms p95\n- **Availability**: 99.9% uptime (8.76 hours downtime/year)\n- **Scalability**: Support 10,000 concurrent users\n- **Security**: PCI DSS compliance required\n\n### Constraints\n- Budget: $X/month for infrastructure\n- Timeline: MVP in 3 months\n- Team: 5 backend, 3 frontend engineers\n\n## High-Level Architecture\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│   Client    │────▶│  API Gateway │────▶│  Service    │\n│   (Web)     │     │   (Kong)    │     │  (Node.js)  │\n└─────────────┘     └─────────────┘     └─────────────┘\n                           │                   │\n                           ▼                   ▼\n                    ┌─────────────┐     ┌─────────────┐\n                    │    Auth     │     │  Database   │\n                    │  (Auth0)    │     │ (PostgreSQL)│\n                    └─────────────┘     └─────────────┘\n```\n\n## Component Details\n\n### API Layer\n- Technology: Node.js with Express/NestJS\n- Responsibilities: Request routing, validation, auth\n- Scaling: Horizontal via load balancer\n\n### Data Layer\n- Primary: PostgreSQL (transactions, relationships)\n- Cache: Redis (sessions, hot data)\n- Storage: S3 (files, images)\n\n### External Services\n- Auth: Auth0 (SSO, MFA)\n- Email: SendGrid (transactional)\n- Monitoring: Datadog (APM, logs)\n\n## Key Decisions\n\n| Decision | Rationale |\n|----------|-----------|\n| PostgreSQL over MongoDB | Relational data, ACID needed |\n| Redis for caching | Sub-ms latency required |\n| Auth0 over custom | Reduce security risk |\n\n## Scaling Strategy\n\n### Current (MVP)\n- Single region deployment\n- 2 API instances behind ALB\n- Single RDS instance\n\n### Future (10x growth)\n- Multi-region with CDN\n- Auto-scaling API (2-10 instances)\n- RDS read replicas\n\n## Security Considerations\n- All traffic over TLS 1.3\n- JWT tokens with 15-min expiry\n- Rate limiting: 100 req/min per user\n- WAF for common attacks\n\n## Failure Modes\n\n| Failure | Impact | Mitigation |\n|---------|--------|------------|\n| DB down | Full outage | Multi-AZ failover |\n| Cache down | Degraded perf | Fallback to DB |\n| Auth down | No new logins | Cache valid tokens |\n```\n\n## Quick Reference\n\n| Section | Key Questions |\n|---------|---------------|\n| Requirements | What must it do? How well? |\n| Architecture | What components? How connected? |\n| Decisions | Why these choices? |\n| Scaling | How to grow? |\n| Failures | What can break? How to recover? |\n","skills/atlassian-mcp/SKILL.md":"---\nname: atlassian-mcp\ndescription: Integrates with Atlassian products to manage project tracking and documentation via MCP protocol. Use when querying Jira issues with JQL filters, creating and updating tickets with custom fields, searching or editing Confluence pages with CQL, managing sprints and backlogs, setting up MCP server authentication, syncing documentation, or debugging Atlassian API integrations.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.2.0\"\n  domain: platform\n  triggers: Jira, Confluence, Atlassian, MCP, tickets, issues, wiki, JQL, CQL, sprint, backlog, project management\n  role: expert\n  scope: implementation\n  output-format: code\n  related-skills: mcp-developer, api-designer, security-reviewer\n---\n\n# Atlassian MCP Expert\n\n## When to Use This Skill\n\n- Querying Jira issues with JQL filters\n- Searching or creating Confluence pages\n- Automating sprint workflows and backlog management\n- Setting up MCP server authentication (OAuth/API tokens)\n- Syncing meeting notes to Jira tickets\n- Generating documentation from issue data\n- Debugging Atlassian API integration issues\n- Choosing between official vs open-source MCP servers\n\n## Core Workflow\n\n1. **Select server** - Choose official cloud, open-source, or self-hosted MCP server\n2. **Authenticate** - Configure OAuth 2.1, API tokens, or PAT credentials\n3. **Design queries** - Write JQL for Jira, CQL for Confluence; validate with `maxResults=1` before full execution\n4. **Implement workflow** - Build tool calls, handle pagination, error recovery\n5. **Verify permissions** - Confirm required scopes with a read-only probe before any write or bulk operation\n6. **Deploy** - Configure IDE integration, test permissions, monitor rate limits\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Server Setup | `references/mcp-server-setup.md` | Installation, choosing servers, configuration |\n| Jira Operations | `references/jira-queries.md` | JQL syntax, issue CRUD, sprints, boards, issue linking |\n| Confluence Ops | `references/confluence-operations.md` | CQL search, page creation, spaces, comments |\n| Authentication | `references/authentication-patterns.md` | OAuth 2.0, API tokens, permission scopes |\n| Common Workflows | `references/common-workflows.md` | Issue triage, doc sync, sprint automation |\n\n## Quick-Start Examples\n\n### JQL Query Samples\n```\n# Open issues assigned to current user in a sprint\nproject = PROJ AND status = \"In Progress\" AND assignee = currentUser() ORDER BY priority DESC\n\n# Unresolved bugs created in the last 7 days\nproject = PROJ AND issuetype = Bug AND status != Done AND created >= -7d ORDER BY created DESC\n\n# Validate before bulk: test with maxResults=1 first\nproject = PROJ AND sprint in openSprints() AND status = Open ORDER BY created DESC\n```\n\n### CQL Query Samples\n```\n# Find pages updated in a specific space recently\nspace = \"ENG\" AND type = page AND lastModified >= \"2024-01-01\" ORDER BY lastModified DESC\n\n# Search page text for a keyword\nspace = \"ENG\" AND type = page AND text ~ \"deployment runbook\"\n```\n\n### Minimal MCP Server Configuration\nAtlassian Cloud: connect to the hosted Atlassian Rovo MCP Server, then run `/mcp` to sign in with OAuth.\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nServer/Data Center: run the community `mcp-atlassian` server (sooperset) with personal access tokens.\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.example.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"${JIRA_PERSONAL_TOKEN}\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.example.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"${CONFLUENCE_PERSONAL_TOKEN}\"\n      }\n    }\n  }\n}\n```\n> **Note:** Always load tokens from environment variables or a secrets manager; never hardcode credentials. Tool names differ between servers, see `references/mcp-server-setup.md`.\n\n## Constraints\n\n### MUST DO\n- Respect user permissions and workspace access controls\n- Validate JQL/CQL queries before execution (use `maxResults=1` probe first)\n- Handle rate limits with exponential backoff\n- Use pagination for large result sets (50-100 items per page)\n- Implement error recovery for network failures\n- Log API calls for debugging and audit trails\n- Test with read-only operations first\n- Document required permission scopes\n- Confirm before any write or bulk operation against production data\n\n### MUST NOT DO\n- Hardcode API tokens or OAuth secrets in code\n- Ignore rate limit headers from Atlassian APIs\n- Create issues without validating required fields\n- Skip input sanitization on user-provided query strings\n- Deploy without testing permission boundaries\n- Update production data without confirmation prompts\n- Mix different authentication methods in same session\n- Expose sensitive issue data in logs or error messages\n\n## Output Templates\n\nWhen implementing Atlassian MCP features, provide:\n1. MCP server configuration (JSON/environment vars)\n2. Query examples (JQL/CQL with explanations)\n3. Tool call implementation with error handling\n4. Authentication setup instructions\n5. Brief explanation of permission requirements\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/platform/atlassian-mcp/)\n","skills/atlassian-mcp/references/authentication-patterns.md":"# Authentication Patterns\n\n---\n\n## Authentication Methods Overview\n\n| Method | Platform | Use Case | Security Level |\n|--------|----------|----------|----------------|\n| OAuth 2.1 | Cloud | User-facing apps, integrations | Highest |\n| API Token | Cloud | Personal automation, scripts | Medium |\n| PAT | Server/DC | Server integrations | Medium |\n| Basic Auth | Legacy | Deprecated, avoid | Low |\n\n## OAuth 2.1 (Atlassian Cloud)\n\n### Authorization Code Flow\n\nFor applications that act on behalf of users.\n\n**Step 1: Register Your App**\n\n1. Go to [developer.atlassian.com](https://developer.atlassian.com/console/myapps/)\n2. Create new app\n3. Configure OAuth 2.0 (3LO)\n4. Add callback URL\n5. Request necessary scopes\n\n**Step 2: Authorization Request**\n\n```typescript\nconst authUrl = new URL('https://auth.atlassian.com/authorize');\nauthUrl.searchParams.set('audience', 'api.atlassian.com');\nauthUrl.searchParams.set('client_id', CLIENT_ID);\nauthUrl.searchParams.set('scope', 'read:jira-work write:jira-work read:confluence-content.all write:confluence-content');\nauthUrl.searchParams.set('redirect_uri', REDIRECT_URI);\nauthUrl.searchParams.set('state', generateState());\nauthUrl.searchParams.set('response_type', 'code');\nauthUrl.searchParams.set('prompt', 'consent');\n\n// Redirect user to authUrl.toString()\n```\n\n**Step 3: Exchange Code for Token**\n\n```typescript\nasync function exchangeCodeForToken(code: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'authorization_code',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      code,\n      redirect_uri: REDIRECT_URI,\n    }),\n  });\n\n  if (!response.ok) {\n    throw new Error(`Token exchange failed: ${response.statusText}`);\n  }\n\n  return response.json();\n}\n\ninterface TokenResponse {\n  access_token: string;\n  refresh_token: string;\n  expires_in: number;\n  scope: string;\n  token_type: 'Bearer';\n}\n```\n\n**Step 4: Refresh Token**\n\n```typescript\nasync function refreshAccessToken(refreshToken: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'refresh_token',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      refresh_token: refreshToken,\n    }),\n  });\n\n  return response.json();\n}\n```\n\n**Step 5: Get Accessible Resources**\n\n```typescript\nasync function getAccessibleResources(accessToken: string): Promise<Resource[]> {\n  const response = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    {\n      headers: {\n        Authorization: `Bearer ${accessToken}`,\n        Accept: 'application/json',\n      },\n    }\n  );\n\n  return response.json();\n}\n\ninterface Resource {\n  id: string;        // Cloud ID\n  name: string;      // Site name\n  url: string;       // https://your-site.atlassian.net\n  scopes: string[];\n  avatarUrl: string;\n}\n```\n\n### OAuth Scopes Reference\n\n**Jira Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:jira-work` | Read issues, projects, boards |\n| `write:jira-work` | Create/update issues |\n| `manage:jira-project` | Manage project settings |\n| `manage:jira-configuration` | Manage global settings |\n| `read:jira-user` | Read user profiles |\n| `manage:jira-data-provider` | Data provider integrations |\n\n**Confluence Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:confluence-content.all` | Read all content |\n| `write:confluence-content` | Create/update content |\n| `read:confluence-content.summary` | Read content summaries |\n| `read:confluence-space.summary` | Read space summaries |\n| `write:confluence-space` | Create/manage spaces |\n| `read:confluence-user` | Read user profiles |\n\n**Granular Scopes (v2):**\n\n```\nread:issue-details:jira\nwrite:issue:jira\nread:sprint:jira-software\nwrite:sprint:jira-software\nread:page:confluence\nwrite:page:confluence\nread:comment:confluence\nwrite:comment:confluence\n```\n\n## API Tokens (Atlassian Cloud)\n\n### Creating API Token\n\n1. Go to [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens)\n2. Click \"Create API token\"\n3. Give it a descriptive label\n4. Copy token immediately (shown only once)\n\n### Using API Token\n\n```typescript\n// Basic authentication with API token\nconst credentials = Buffer.from(`${email}:${apiToken}`).toString('base64');\n\nconst response = await fetch('https://your-site.atlassian.net/rest/api/3/myself', {\n  headers: {\n    Authorization: `Basic ${credentials}`,\n    Accept: 'application/json',\n  },\n});\n\n// For MCP server configuration\nconst config = {\n  JIRA_URL: 'https://your-site.atlassian.net',\n  JIRA_USERNAME: 'your-email@company.com',\n  JIRA_API_TOKEN: '<jira-api-token>',\n};\n```\n\n### Token Security Best Practices\n\n```typescript\n// Store tokens securely\nimport { SecretManagerServiceClient } from '@google-cloud/secret-manager';\nimport { SecretsManager } from '@aws-sdk/client-secrets-manager';\n\n// Option 1: Environment variables (development only)\nconst token = process.env.ATLASSIAN_API_TOKEN;\n\n// Option 2: GCP Secret Manager\nasync function getTokenFromGCP(secretName: string): Promise<string> {\n  const client = new SecretManagerServiceClient();\n  const [version] = await client.accessSecretVersion({\n    name: `projects/my-project/secrets/${secretName}/versions/latest`,\n  });\n  return version.payload?.data?.toString() || '';\n}\n\n// Option 3: AWS Secrets Manager\nasync function getTokenFromAWS(secretName: string): Promise<string> {\n  const client = new SecretsManager({ region: 'us-east-1' });\n  const response = await client.getSecretValue({ SecretId: secretName });\n  return response.SecretString || '';\n}\n\n// Option 4: HashiCorp Vault\nasync function getTokenFromVault(path: string): Promise<string> {\n  const response = await fetch(`${VAULT_ADDR}/v1/${path}`, {\n    headers: { 'X-Vault-Token': VAULT_TOKEN },\n  });\n  const data = await response.json();\n  return data.data.data.token;\n}\n```\n\n## Personal Access Tokens (Server/Data Center)\n\n### Creating PAT\n\n**Jira Server/DC:**\n1. Profile > Personal Access Tokens\n2. Create token\n3. Set expiration date\n4. Select permissions\n\n**Confluence Server/DC:**\n1. Profile > Settings > Personal Access Tokens\n2. Create token\n3. Configure permissions\n\n### Using PAT\n\n```typescript\n// Bearer token authentication\nconst response = await fetch('https://jira.internal.company.com/rest/api/2/myself', {\n  headers: {\n    Authorization: `Bearer ${personalAccessToken}`,\n    Accept: 'application/json',\n  },\n});\n\n// MCP server configuration\nconst config = {\n  JIRA_URL: 'https://jira.internal.company.com',\n  JIRA_PERSONAL_TOKEN: '<jira-personal-access-token>',\n};\n```\n\n### PAT Permissions\n\n| Permission | Jira | Confluence |\n|------------|------|------------|\n| Read | Browse projects, view issues | View pages |\n| Write | Create/edit issues | Create/edit pages |\n| Admin | Project administration | Space administration |\n\n## Token Management\n\n### Token Lifecycle Manager\n\n```typescript\ninterface TokenInfo {\n  accessToken: string;\n  refreshToken?: string;\n  expiresAt: Date;\n  scopes: string[];\n}\n\nclass TokenManager {\n  private tokenInfo: TokenInfo | null = null;\n  private refreshThreshold = 5 * 60 * 1000; // 5 minutes\n\n  async getValidToken(): Promise<string> {\n    if (!this.tokenInfo) {\n      throw new Error('Not authenticated');\n    }\n\n    // Check if token needs refresh\n    const timeUntilExpiry = this.tokenInfo.expiresAt.getTime() - Date.now();\n\n    if (timeUntilExpiry < this.refreshThreshold) {\n      await this.refreshToken();\n    }\n\n    return this.tokenInfo.accessToken;\n  }\n\n  private async refreshToken(): Promise<void> {\n    if (!this.tokenInfo?.refreshToken) {\n      throw new Error('No refresh token available');\n    }\n\n    const response = await refreshAccessToken(this.tokenInfo.refreshToken);\n\n    this.tokenInfo = {\n      accessToken: response.access_token,\n      refreshToken: response.refresh_token || this.tokenInfo.refreshToken,\n      expiresAt: new Date(Date.now() + response.expires_in * 1000),\n      scopes: response.scope.split(' '),\n    };\n  }\n\n  isAuthenticated(): boolean {\n    return this.tokenInfo !== null && this.tokenInfo.expiresAt > new Date();\n  }\n\n  getScopes(): string[] {\n    return this.tokenInfo?.scopes || [];\n  }\n\n  hasScope(scope: string): boolean {\n    return this.getScopes().includes(scope);\n  }\n}\n```\n\n### Token Rotation Strategy\n\n```typescript\nclass TokenRotationManager {\n  private rotationInterval = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n  async checkAndRotate(tokenCreatedAt: Date): Promise<boolean> {\n    const age = Date.now() - tokenCreatedAt.getTime();\n\n    if (age > this.rotationInterval) {\n      console.warn('API token is due for rotation');\n      return true;\n    }\n\n    return false;\n  }\n\n  async sendRotationReminder(email: string, tokenLabel: string): Promise<void> {\n    // Integrate with your notification system\n    await sendEmail({\n      to: email,\n      subject: 'Atlassian API Token Rotation Reminder',\n      body: `Your API token \"${tokenLabel}\" is due for rotation.\n             Please create a new token and update your integrations.`,\n    });\n  }\n}\n```\n\n## Permission Verification\n\n### Check Current Permissions\n\n```typescript\nasync function verifyPermissions(\n  client: MCPClient,\n  requiredOperations: string[]\n): Promise<PermissionReport> {\n  const report: PermissionReport = {\n    hasAllPermissions: true,\n    details: [],\n  };\n\n  for (const operation of requiredOperations) {\n    try {\n      switch (operation) {\n        case 'read:jira':\n          await client.callTool({\n            name: 'jira_get_issue',\n            arguments: { issue_key: 'TEST-1' },\n          });\n          break;\n        case 'write:jira':\n          // Create and immediately delete a test issue\n          const created = await client.callTool({\n            name: 'jira_create_issue',\n            arguments: {\n              project_key: 'TEST',\n              issue_type: 'Task',\n              summary: '[Permission Test] Delete me',\n            },\n          });\n          // Clean up\n          await client.callTool({\n            name: 'jira_delete_issue',\n            arguments: { issue_key: JSON.parse(created.content[0].text).key },\n          });\n          break;\n        case 'read:confluence':\n          await client.callTool({\n            name: 'confluence_search',\n            arguments: { cql: 'type = page', limit: 1 },\n          });\n          break;\n      }\n\n      report.details.push({ operation, status: 'granted' });\n    } catch (error: any) {\n      report.hasAllPermissions = false;\n      report.details.push({\n        operation,\n        status: 'denied',\n        error: error.message,\n      });\n    }\n  }\n\n  return report;\n}\n```\n\n## Security Checklist\n\n### Do:\n- Use OAuth 2.1 for user-facing applications\n- Store secrets in dedicated secrets management systems\n- Implement token rotation policies\n- Use minimal required scopes\n- Log authentication events (without secrets)\n- Implement rate limiting at application level\n- Validate tokens before use\n\n### Don't:\n- Hardcode tokens in source code\n- Log tokens or secrets\n- Share tokens between environments\n- Use Basic Auth (deprecated)\n- Request more scopes than needed\n- Store tokens in browser localStorage\n- Commit `.env` files with real credentials\n\n### Environment Configuration Template\n\n```bash\n# .env.example (commit this)\nATLASSIAN_SITE_URL=https://your-site.atlassian.net\nATLASSIAN_AUTH_TYPE=oauth  # or 'api_token' or 'pat'\n\n# OAuth settings (if using OAuth)\nATLASSIAN_CLIENT_ID=\nATLASSIAN_CLIENT_SECRET=\n\n# API Token settings (if using API token)\nATLASSIAN_USERNAME=\nATLASSIAN_API_TOKEN=\n\n# PAT settings (if using Server/DC)\nATLASSIAN_PERSONAL_TOKEN=\n```\n\n```bash\n# .gitignore\n.env\n.env.local\n.env.*.local\ncredentials.json\n**/secrets/**\n```\n\n## Troubleshooting\n\n### Common Authentication Errors\n\n**401 Unauthorized:**\n- Invalid or expired token\n- Wrong authentication method\n- Missing Authorization header\n\n**403 Forbidden:**\n- Token valid but lacks required scope\n- Resource-level permission denied\n- IP allowlist blocking request\n\n**Token refresh fails:**\n- Refresh token expired (after 90 days of inactivity)\n- Client secret changed\n- App permissions revoked\n\n### Debug Authentication\n\n```typescript\nasync function debugAuth(token: string): Promise<void> {\n  // Check token validity\n  const meResponse = await fetch(\n    'https://api.atlassian.com/me',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  console.log('Token status:', meResponse.status);\n\n  if (meResponse.ok) {\n    const me = await meResponse.json();\n    console.log('Authenticated as:', me.email);\n  }\n\n  // Check accessible resources\n  const resourcesResponse = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  if (resourcesResponse.ok) {\n    const resources = await resourcesResponse.json();\n    console.log('Accessible sites:', resources.map((r: any) => r.name));\n  }\n}\n```\n\n## Related References\n\n- `mcp-server-setup.md` - Server configuration with credentials\n- `jira-queries.md` - Operations that require authentication\n- `confluence-operations.md` - Content operations with auth\n","skills/atlassian-mcp/references/common-workflows.md":"# Common Workflows\n\n---\n\n## Issue Triage Workflow\n\nAutomatically categorize, prioritize, and route incoming issues.\n\n### Triage Bot Implementation\n\n```typescript\ninterface TriageResult {\n  issueKey: string;\n  category: string;\n  priority: string;\n  assignee: string | null;\n  labels: string[];\n  actions: string[];\n}\n\nasync function triageNewIssue(\n  client: MCPClient,\n  issueKey: string\n): Promise<TriageResult> {\n  // Get issue details\n  const issueResult = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: {\n      issue_key: issueKey,\n      expand: [\"changelog\"]\n    }\n  });\n\n  const issue = JSON.parse(issueResult.content[0].text);\n  const result: TriageResult = {\n    issueKey,\n    category: \"uncategorized\",\n    priority: issue.fields.priority?.name || \"Medium\",\n    assignee: null,\n    labels: [],\n    actions: []\n  };\n\n  // Categorize based on content\n  const summary = issue.fields.summary.toLowerCase();\n  const description = (issue.fields.description?.content?.[0]?.content?.[0]?.text || \"\").toLowerCase();\n  const text = `${summary} ${description}`;\n\n  // Category detection\n  if (text.match(/\\b(crash|down|outage|critical|emergency)\\b/)) {\n    result.category = \"incident\";\n    result.priority = \"Highest\";\n    result.labels.push(\"incident\", \"urgent\");\n  } else if (text.match(/\\b(bug|error|fail|broken|doesn't work)\\b/)) {\n    result.category = \"bug\";\n    result.labels.push(\"bug\", \"needs-investigation\");\n  } else if (text.match(/\\b(feature|enhancement|request|add|improve)\\b/)) {\n    result.category = \"feature\";\n    result.labels.push(\"feature-request\");\n  } else if (text.match(/\\b(docs|documentation|readme|guide)\\b/)) {\n    result.category = \"documentation\";\n    result.labels.push(\"documentation\");\n  }\n\n  // Component detection for routing\n  const componentMap: Record<string, { component: string; team: string }> = {\n    \"api|rest|endpoint\": { component: \"API\", team: \"backend-team\" },\n    \"ui|button|page|screen|css\": { component: \"Frontend\", team: \"frontend-team\" },\n    \"database|sql|query|migration\": { component: \"Database\", team: \"data-team\" },\n    \"auth|login|password|sso\": { component: \"Authentication\", team: \"security-team\" },\n    \"deploy|ci|cd|pipeline\": { component: \"DevOps\", team: \"platform-team\" }\n  };\n\n  for (const [pattern, { component, team }] of Object.entries(componentMap)) {\n    if (text.match(new RegExp(`\\\\b(${pattern})\\\\b`))) {\n      result.labels.push(component.toLowerCase());\n      result.actions.push(`Route to ${team}`);\n      break;\n    }\n  }\n\n  // Apply triage results\n  await client.callTool({\n    name: \"jira_update_issue\",\n    arguments: {\n      issue_key: issueKey,\n      fields: {\n        priority: { name: result.priority },\n        labels: [...new Set([...issue.fields.labels || [], ...result.labels])]\n      }\n    }\n  });\n\n  // Add triage comment\n  await client.callTool({\n    name: \"jira_add_comment\",\n    arguments: {\n      issue_key: issueKey,\n      body: `*Automated Triage Results*\n\nCategory: ${result.category}\nPriority: ${result.priority}\nLabels added: ${result.labels.join(\", \")}\n\n${result.actions.length > 0 ? `Recommended actions:\\n${result.actions.map(a => `- ${a}`).join(\"\\n\")}` : \"\"}`\n    }\n  });\n\n  return result;\n}\n```\n\n### Bulk Triage New Issues\n\n```typescript\nasync function triageBacklog(client: MCPClient, projectKey: string): Promise<void> {\n  // Find untriaged issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND labels IS EMPTY AND created >= -7d AND resolution IS EMPTY`,\n      max_results: 50,\n      fields: [\"summary\", \"description\", \"priority\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  console.log(`Found ${response.total} issues to triage`);\n\n  for (const issue of response.issues) {\n    const result = await triageNewIssue(client, issue.key);\n    console.log(`Triaged ${issue.key}: ${result.category} (${result.priority})`);\n\n    // Rate limiting\n    await delay(500);\n  }\n}\n```\n\n## Documentation Sync Workflow\n\nKeep Confluence documentation in sync with Jira issues.\n\n### Generate Release Notes\n\n```typescript\nasync function generateReleaseNotes(\n  client: MCPClient,\n  projectKey: string,\n  version: string,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get all issues in the release\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND fixVersion = \"${version}\" AND resolution = Done ORDER BY issuetype, priority DESC`,\n      max_results: 200,\n      fields: [\"summary\", \"issuetype\", \"priority\", \"assignee\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n\n  // Group by issue type\n  const grouped: Record<string, any[]> = {};\n  for (const issue of response.issues) {\n    const type = issue.fields.issuetype.name;\n    if (!grouped[type]) grouped[type] = [];\n    grouped[type].push(issue);\n  }\n\n  // Build Confluence page content\n  let content = `\n<h2>Release ${version}</h2>\n<p>Released on ${new Date().toISOString().split('T')[0]}</p>\n\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">2</ac:parameter>\n</ac:structured-macro>\n\n<h2>Summary</h2>\n<p>This release includes ${response.total} changes.</p>\n<table>\n  <tr><th>Type</th><th>Count</th></tr>\n  ${Object.entries(grouped).map(([type, issues]) =>\n    `<tr><td>${type}</td><td>${issues.length}</td></tr>`\n  ).join('')}\n</table>\n`;\n\n  // Add sections for each issue type\n  const typeOrder = [\"New Feature\", \"Improvement\", \"Bug\", \"Task\"];\n  const orderedTypes = [...new Set([...typeOrder, ...Object.keys(grouped)])];\n\n  for (const type of orderedTypes) {\n    if (!grouped[type]) continue;\n\n    content += `\n<h2>${type}s</h2>\n<table>\n  <tr><th>Key</th><th>Summary</th><th>Assignee</th></tr>\n  ${grouped[type].map(issue => `\n    <tr>\n      <td><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issue.key}</ac:parameter></ac:structured-macro></td>\n      <td>${escapeHtml(issue.fields.summary)}</td>\n      <td>${issue.fields.assignee?.displayName || 'Unassigned'}</td>\n    </tr>\n  `).join('')}\n</table>\n`;\n  }\n\n  // Create or update Confluence page\n  const pageTitle = `Release Notes - ${version}`;\n\n  // Check if page exists\n  const existingSearch = await client.callTool({\n    name: \"confluence_search\",\n    arguments: {\n      cql: `space = \"${confluenceSpaceKey}\" AND title = \"${pageTitle}\"`,\n      limit: 1\n    }\n  });\n\n  const existingResults = JSON.parse(existingSearch.content[0].text);\n\n  if (existingResults.results.length > 0) {\n    // Update existing page\n    const pageId = existingResults.results[0].id;\n    const currentVersion = existingResults.results[0].version.number;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: pageId,\n        title: pageTitle,\n        body: content,\n        version_number: currentVersion + 1,\n        version_message: `Updated release notes for ${version}`\n      }\n    });\n\n    return pageId;\n  } else {\n    // Create new page\n    const newPage = await client.callTool({\n      name: \"confluence_create_page\",\n      arguments: {\n        space_key: confluenceSpaceKey,\n        title: pageTitle,\n        body: content,\n        labels: [\"release-notes\", `version-${version.replace(/\\./g, '-')}`]\n      }\n    });\n\n    return JSON.parse(newPage.content[0].text).id;\n  }\n}\n```\n\n### Sync Meeting Notes to Jira\n\n```typescript\ninterface ActionItem {\n  description: string;\n  assignee: string;\n  dueDate?: string;\n}\n\nasync function syncMeetingNotes(\n  client: MCPClient,\n  confluencePageId: string,\n  jiraProjectKey: string\n): Promise<string[]> {\n  // Get meeting notes content\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: confluencePageId,\n      expand: [\"body.storage\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n  const content = page.body.storage.value;\n\n  // Extract action items (look for checkbox patterns)\n  const actionItems = extractActionItems(content);\n  const createdIssues: string[] = [];\n\n  for (const item of actionItems) {\n    // Create Jira task\n    const newIssue = await client.callTool({\n      name: \"jira_create_issue\",\n      arguments: {\n        project_key: jiraProjectKey,\n        issue_type: \"Task\",\n        summary: item.description,\n        description: {\n          type: \"doc\",\n          version: 1,\n          content: [{\n            type: \"paragraph\",\n            content: [{\n              type: \"text\",\n              text: `Action item from meeting: ${page.title}\\n\\nSource: ${page._links.webui}`\n            }]\n          }]\n        },\n        assignee: item.assignee,\n        due_date: item.dueDate,\n        labels: [\"meeting-action\", \"auto-created\"]\n      }\n    });\n\n    const issueKey = JSON.parse(newIssue.content[0].text).key;\n    createdIssues.push(issueKey);\n  }\n\n  // Update Confluence page with Jira links\n  if (createdIssues.length > 0) {\n    const jiraLinksSection = `\n<h2>Linked Jira Issues</h2>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">key IN (${createdIssues.join(',')})</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n`;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: confluencePageId,\n        title: page.title,\n        body: content + jiraLinksSection,\n        version_number: page.version.number + 1\n      }\n    });\n  }\n\n  return createdIssues;\n}\n\nfunction extractActionItems(html: string): ActionItem[] {\n  // Parse action items from common patterns\n  const items: ActionItem[] = [];\n\n  // Pattern: [x] or [ ] followed by @mention and task description\n  const taskPattern = /\\[[ x]\\]\\s*@([a-zA-Z.]+)\\s*[-:]\\s*(.+?)(?=<|$)/gi;\n  let match;\n\n  while ((match = taskPattern.exec(html)) !== null) {\n    items.push({\n      assignee: match[1],\n      description: match[2].trim()\n    });\n  }\n\n  return items;\n}\n```\n\n## Sprint Automation Workflow\n\nAutomate sprint ceremonies and reporting.\n\n### Sprint Planning Assistant\n\n```typescript\ninterface SprintPlanningReport {\n  totalPoints: number;\n  byAssignee: Record<string, number>;\n  byComponent: Record<string, number>;\n  riskyItems: string[];\n  recommendations: string[];\n}\n\nasync function analyzeSprintPlanning(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number\n): Promise<SprintPlanningReport> {\n  // Get sprint issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"assignee\", \"customfield_10001\", \"components\", \"labels\", \"priority\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  const issues = response.issues;\n\n  const report: SprintPlanningReport = {\n    totalPoints: 0,\n    byAssignee: {},\n    byComponent: {},\n    riskyItems: [],\n    recommendations: []\n  };\n\n  for (const issue of issues) {\n    const points = issue.fields.customfield_10001 || 0; // Story points\n    const assignee = issue.fields.assignee?.displayName || \"Unassigned\";\n    const components = issue.fields.components?.map((c: any) => c.name) || [\"No Component\"];\n\n    report.totalPoints += points;\n    report.byAssignee[assignee] = (report.byAssignee[assignee] || 0) + points;\n\n    for (const component of components) {\n      report.byComponent[component] = (report.byComponent[component] || 0) + points;\n    }\n\n    // Identify risks\n    if (points === 0) {\n      report.riskyItems.push(`${issue.key}: No story points estimated`);\n    }\n    if (points >= 8) {\n      report.riskyItems.push(`${issue.key}: Large story (${points} points) - consider breaking down`);\n    }\n    if (assignee === \"Unassigned\") {\n      report.riskyItems.push(`${issue.key}: No assignee`);\n    }\n  }\n\n  // Generate recommendations\n  const avgPointsPerPerson = report.totalPoints / Object.keys(report.byAssignee).length;\n\n  for (const [assignee, points] of Object.entries(report.byAssignee)) {\n    if (points > avgPointsPerPerson * 1.5) {\n      report.recommendations.push(`${assignee} has ${points} points (${Math.round((points / avgPointsPerPerson - 1) * 100)}% above average) - consider rebalancing`);\n    }\n  }\n\n  return report;\n}\n```\n\n### Sprint Retrospective Generator\n\n```typescript\nasync function generateRetroBoard(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get completed sprint data\n  const sprintResult = await client.callTool({\n    name: \"jira_get_sprint_report\",\n    arguments: {\n      board_id: boardId,\n      sprint_id: sprintId\n    }\n  });\n\n  const sprintReport = JSON.parse(sprintResult.content[0].text);\n\n  // Get sprint issues with details\n  const issuesResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"status\", \"resolution\", \"customfield_10001\", \"assignee\"]\n    }\n  });\n\n  const issues = JSON.parse(issuesResult.content[0].text).issues;\n\n  // Calculate metrics\n  const completed = issues.filter((i: any) => i.fields.resolution);\n  const incomplete = issues.filter((i: any) => !i.fields.resolution);\n  const completedPoints = completed.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n  const plannedPoints = issues.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n\n  // Create retrospective page\n  const content = `\n<h2>Sprint Retrospective: ${sprintReport.sprint.name}</h2>\n<p><em>Generated on ${new Date().toISOString().split('T')[0]}</em></p>\n\n<h3>Sprint Metrics</h3>\n<table>\n  <tr><th>Metric</th><th>Value</th></tr>\n  <tr><td>Start Date</td><td>${sprintReport.sprint.startDate}</td></tr>\n  <tr><td>End Date</td><td>${sprintReport.sprint.endDate}</td></tr>\n  <tr><td>Issues Planned</td><td>${issues.length}</td></tr>\n  <tr><td>Issues Completed</td><td>${completed.length}</td></tr>\n  <tr><td>Completion Rate</td><td>${Math.round(completed.length / issues.length * 100)}%</td></tr>\n  <tr><td>Points Planned</td><td>${plannedPoints}</td></tr>\n  <tr><td>Points Completed</td><td>${completedPoints}</td></tr>\n  <tr><td>Velocity</td><td>${completedPoints} points</td></tr>\n</table>\n\n<h3>Completed Items</h3>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS NOT EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee</ac:parameter>\n</ac:structured-macro>\n\n${incomplete.length > 0 ? `\n<h3>Incomplete Items (Spillover)</h3>\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>${incomplete.length} items were not completed and will spill over to the next sprint.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee,status</ac:parameter>\n</ac:structured-macro>\n` : ''}\n\n<h3>Discussion Points</h3>\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What went well?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What could be improved?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Action Items</ac:parameter>\n  <ac:rich-text-body>\n    <ac:task-list>\n      <ac:task><ac:task-body><em>Add action items here...</em></ac:task-body></ac:task>\n    </ac:task-list>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  const newPage = await client.callTool({\n    name: \"confluence_create_page\",\n    arguments: {\n      space_key: confluenceSpaceKey,\n      title: `Sprint Retro - ${sprintReport.sprint.name}`,\n      body: content,\n      labels: [\"sprint-retro\", \"team-meeting\"]\n    }\n  });\n\n  return JSON.parse(newPage.content[0].text).id;\n}\n```\n\n## Issue-Documentation Link Workflow\n\nBidirectional linking between code changes and documentation.\n\n### Link PR to Documentation\n\n```typescript\nasync function linkPRToDocumentation(\n  client: MCPClient,\n  issueKey: string,\n  docPageId: string\n): Promise<void> {\n  // Add remote link to Jira issue\n  await client.callTool({\n    name: \"jira_add_remote_link\",\n    arguments: {\n      issue_key: issueKey,\n      url: `https://your-site.atlassian.net/wiki/spaces/DEV/pages/${docPageId}`,\n      title: \"Related Documentation\",\n      icon_url: \"https://your-site.atlassian.net/wiki/favicon.ico\"\n    }\n  });\n\n  // Add Jira macro to Confluence page\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: docPageId,\n      expand: [\"body.storage\", \"version\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n\n  // Check if link already exists\n  if (page.body.storage.value.includes(issueKey)) {\n    return; // Already linked\n  }\n\n  const jiraLink = `\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Related Issue</ac:parameter>\n  <ac:rich-text-body>\n    <p><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issueKey}</ac:parameter></ac:structured-macro></p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  await client.callTool({\n    name: \"confluence_update_page\",\n    arguments: {\n      page_id: docPageId,\n      title: page.title,\n      body: jiraLink + page.body.storage.value,\n      version_number: page.version.number + 1\n    }\n  });\n}\n```\n\n## Utility Functions\n\n```typescript\nfunction escapeHtml(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#039;');\n}\n\nfunction delay(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function withRetry<T>(\n  operation: () => Promise<T>,\n  maxRetries = 3,\n  baseDelay = 1000\n): Promise<T> {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      if (attempt === maxRetries) throw error;\n\n      const delay = baseDelay * Math.pow(2, attempt - 1);\n      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n## Related References\n\n- `jira-queries.md` - JQL syntax for workflow queries\n- `confluence-operations.md` - Page creation and formatting\n- `authentication-patterns.md` - Secure API access for automated workflows\n","skills/atlassian-mcp/references/confluence-operations.md":"# Confluence Operations\n\n---\n\n## CQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `space = \"DEV\"` |\n| `!=` | Not equal | `type != attachment` |\n| `~` | Contains | `title ~ \"API\"` |\n| `!~` | Does not contain | `text !~ \"deprecated\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `lastModified >= \"2024-01-01\"` |\n| `IN` | Multiple values | `space IN (\"DEV\", \"OPS\")` |\n| `NOT IN` | Exclude | `creator NOT IN (\"bot\")` |\n\n### Field Reference\n\n**Content Fields:**\n```cql\ntype = page                        -- Pages only\ntype = blogpost                    -- Blog posts\ntype = attachment                  -- Attachments\ntype = comment                     -- Comments\nspace = \"DEVDOCS\"                  -- Specific space\nspace.type = global                -- Global spaces\nspace.type = personal              -- Personal spaces\n```\n\n**Search Fields:**\n```cql\ntitle ~ \"architecture\"             -- Title contains\ntext ~ \"kubernetes\"                -- Full text search\ncontent ~ \"deployment\"             -- Content body\nlabel = \"official\"                 -- Has label\nlabel IN (\"api\", \"reference\")      -- Multiple labels\n```\n\n**Date Fields:**\n```cql\ncreated >= \"2024-01-01\"            -- Created after date\nlastModified >= now(\"-30d\")        -- Modified in last 30 days\ncreated >= startOfYear()           -- Created this year\nlastModified >= startOfMonth()     -- Modified this month\n```\n\n**User Fields:**\n```cql\ncreator = currentUser()            -- Created by me\ncontributor = \"john.doe\"           -- Edited by user\nmention = currentUser()            -- Mentions me\nwatcher = currentUser()            -- Pages I watch\nfavourite = currentUser()          -- My favorites\n```\n\n## Essential CQL Patterns\n\n### Documentation Search\n\n```cql\n-- API documentation in dev space\nspace = \"DEV\" AND label = \"api-docs\" AND type = page\n\n-- Recently updated architecture docs\nlabel = \"architecture\" AND lastModified >= now(\"-7d\")\n\n-- Search for code examples\ntext ~ \"```\" AND label = \"tutorial\"\n\n-- Find outdated documentation\nlabel = \"needs-review\" OR lastModified <= now(\"-180d\")\n\n-- Meeting notes from this month\nlabel = \"meeting-notes\" AND created >= startOfMonth()\n```\n\n### Space Management\n\n```cql\n-- All pages in multiple spaces\nspace IN (\"DEV\", \"OPS\", \"PRODUCT\") AND type = page\n\n-- Personal space content\nspace.type = personal AND creator = currentUser()\n\n-- Archived content\nlabel = \"archived\" AND space = \"LEGACY\"\n\n-- Templates\ntype = page AND label = \"template\"\n```\n\n### Content Discovery\n\n```cql\n-- Popular pages (frequently viewed)\ntype = page AND space = \"DOCS\" ORDER BY lastModified DESC\n\n-- Draft pages\nlabel = \"draft\" AND type = page\n\n-- Pages without labels\ntype = page AND space = \"DEV\" AND label IS NULL\n\n-- Orphan pages (no parent)\ntype = page AND ancestor IS NULL AND space = \"DOCS\"\n```\n\n## MCP Tool Calls\n\n### Searching Content\n\n```typescript\n// Basic CQL search\nconst searchResult = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'space = \"DEV\" AND label = \"api-docs\"',\n    limit: 25,\n    expand: [\"body.storage\", \"version\", \"ancestors\"]\n  }\n});\n\n// Parse results\nconst results = JSON.parse(searchResult.content[0].text);\nfor (const page of results.results) {\n  console.log(`${page.title} - ${page._links.webui}`);\n}\n\n// Search with content preview\nconst searchWithContent = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'text ~ \"deployment\" AND space = \"OPS\"',\n    limit: 10,\n    excerpt: true\n  }\n});\n```\n\n### Getting Page Content\n\n```typescript\n// Get page by ID\nconst page = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"body.view\", \"version\", \"ancestors\", \"children.page\"]\n  }\n});\n\n// Get page by space and title\nconst pageByTitle = await client.callTool({\n  name: \"confluence_get_page_by_title\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Reference\"\n  }\n});\n\n// Get page children\nconst children = await client.callTool({\n  name: \"confluence_get_children\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"page\"]\n  }\n});\n```\n\n### Creating Pages\n\n```typescript\n// Create page with storage format (XHTML)\nconst newPage = await client.callTool({\n  name: \"confluence_create_page\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Authentication Guide\",\n    parent_id: \"123456\",  // Optional parent page\n    body: `\n      <h2>Overview</h2>\n      <p>This guide covers authentication methods for our API.</p>\n\n      <h2>OAuth 2.0</h2>\n      <p>We support OAuth 2.0 with the following grant types:</p>\n      <ul>\n        <li>Authorization Code</li>\n        <li>Client Credentials</li>\n      </ul>\n\n      <ac:structured-macro ac:name=\"code\">\n        <ac:parameter ac:name=\"language\">bash</ac:parameter>\n        <ac:plain-text-body><![CDATA[curl -X POST https://api.example.com/oauth/token \\\\\n  -d \"grant_type=client_credentials\" \\\\\n  -d \"client_id=<client-id>\" \\\\\n  -d \"client_secret=<client-secret>\"]]></ac:plain-text-body>\n      </ac:structured-macro>\n\n      <h2>API Tokens</h2>\n      <p>For simple integrations, use API tokens:</p>\n      <ac:structured-macro ac:name=\"info\">\n        <ac:rich-text-body>\n          <p>API tokens are tied to your user account and have the same permissions.</p>\n        </ac:rich-text-body>\n      </ac:structured-macro>\n    `,\n    labels: [\"api-docs\", \"authentication\", \"official\"]\n  }\n});\n\nconsole.log(`Created page: ${newPage.content[0].text}`);\n```\n\n### Updating Pages\n\n```typescript\n// Update page content\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: \"API Authentication Guide (Updated)\",\n    body: \"<h2>Updated Content</h2><p>New documentation here...</p>\",\n    version_number: 5,  // Current version + 1\n    version_message: \"Added OAuth 2.1 section\"\n  }\n});\n\n// Append to existing page\nconst currentPage = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\nconst pageData = JSON.parse(currentPage.content[0].text);\nconst currentBody = pageData.body.storage.value;\nconst newSection = `\n  <h2>New Section</h2>\n  <p>Additional content appended to the page.</p>\n`;\n\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: pageData.title,\n    body: currentBody + newSection,\n    version_number: pageData.version.number + 1\n  }\n});\n```\n\n### Working with Comments\n\n```typescript\n// Add comment to page\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    body: \"<p>This section needs to be updated for v2.0 changes.</p>\"\n  }\n});\n\n// Get page comments\nconst comments = await client.callTool({\n  name: \"confluence_get_comments\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\n// Reply to comment\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    parent_comment_id: \"789012\",\n    body: \"<p>Good catch! I'll update this section.</p>\"\n  }\n});\n```\n\n### Managing Labels\n\n```typescript\n// Add labels to page\nawait client.callTool({\n  name: \"confluence_add_labels\",\n  arguments: {\n    page_id: \"123456\",\n    labels: [\"reviewed\", \"q1-2024\", \"api-v2\"]\n  }\n});\n\n// Remove label\nawait client.callTool({\n  name: \"confluence_remove_label\",\n  arguments: {\n    page_id: \"123456\",\n    label: \"draft\"\n  }\n});\n\n// Get page labels\nconst labels = await client.callTool({\n  name: \"confluence_get_labels\",\n  arguments: {\n    page_id: \"123456\"\n  }\n});\n```\n\n### Space Operations\n\n```typescript\n// Get space information\nconst space = await client.callTool({\n  name: \"confluence_get_space\",\n  arguments: {\n    space_key: \"DEV\",\n    expand: [\"description\", \"homepage\"]\n  }\n});\n\n// List all spaces\nconst spaces = await client.callTool({\n  name: \"confluence_list_spaces\",\n  arguments: {\n    type: \"global\",\n    limit: 100\n  }\n});\n\n// Get space content\nconst spaceContent = await client.callTool({\n  name: \"confluence_get_space_content\",\n  arguments: {\n    space_key: \"DEV\",\n    depth: \"root\",  // or \"all\"\n    expand: [\"children.page\"]\n  }\n});\n```\n\n## Storage Format Reference\n\n### Common Macros\n\n```xml\n<!-- Code block -->\n<ac:structured-macro ac:name=\"code\">\n  <ac:parameter ac:name=\"language\">python</ac:parameter>\n  <ac:parameter ac:name=\"title\">Example</ac:parameter>\n  <ac:plain-text-body><![CDATA[print(\"Hello, World!\")]]></ac:plain-text-body>\n</ac:structured-macro>\n\n<!-- Info panel -->\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Note</ac:parameter>\n  <ac:rich-text-body>\n    <p>Important information here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Warning panel -->\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>Be careful with this operation!</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Table of contents -->\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">3</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issue link -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"key\">PROJ-123</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issues table -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">project = PROJ AND sprint in openSprints()</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n\n<!-- Expand section -->\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Click to expand</ac:parameter>\n  <ac:rich-text-body>\n    <p>Hidden content here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Include page -->\n<ac:structured-macro ac:name=\"include\">\n  <ac:parameter ac:name=\"\"><ri:page ri:content-title=\"Shared Footer\" /></ac:parameter>\n</ac:structured-macro>\n```\n\n### Formatting Elements\n\n```xml\n<!-- Status badge -->\n<ac:structured-macro ac:name=\"status\">\n  <ac:parameter ac:name=\"colour\">Green</ac:parameter>\n  <ac:parameter ac:name=\"title\">APPROVED</ac:parameter>\n</ac:structured-macro>\n\n<!-- User mention -->\n<ac:link><ri:user ri:account-id=\"557058:f3c7...\" /></ac:link>\n\n<!-- Page link -->\n<ac:link><ri:page ri:content-title=\"Target Page\" ri:space-key=\"DEV\" /></ac:link>\n\n<!-- Attachment -->\n<ac:link><ri:attachment ri:filename=\"diagram.png\" /></ac:link>\n\n<!-- Image from attachment -->\n<ac:image><ri:attachment ri:filename=\"screenshot.png\" /></ac:image>\n\n<!-- External image -->\n<ac:image><ri:url ri:value=\"https://example.com/image.png\" /></ac:image>\n```\n\n## Pagination Handling\n\n```typescript\nasync function getAllPages(cql: string): Promise<Page[]> {\n  const allPages: Page[] = [];\n  let start = 0;\n  const limit = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"confluence_search\",\n      arguments: {\n        cql,\n        start,\n        limit,\n        expand: [\"body.storage\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allPages.push(...response.results);\n\n    if (response.results.length < limit || !response._links.next) {\n      break;\n    }\n\n    start += limit;\n  }\n\n  return allPages;\n}\n```\n\n## Error Handling\n\n```typescript\nasync function safeConfluenceCall<T>(operation: () => Promise<T>): Promise<T> {\n  try {\n    return await operation();\n  } catch (error: any) {\n    const status = error.response?.status;\n\n    switch (status) {\n      case 404:\n        throw new Error(`Page or space not found: ${error.message}`);\n      case 403:\n        throw new Error(`Permission denied. Check space permissions.`);\n      case 409:\n        throw new Error(`Version conflict. Page was modified. Refresh and retry.`);\n      case 429:\n        const retryAfter = error.response?.headers?.['retry-after'] || 60;\n        throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);\n      default:\n        throw error;\n    }\n  }\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```cql\n-- Too broad (slow)\ntext ~ \"the\"\n\n-- Missing quotes\nspace = DEV DOCS  -- WRONG\nspace = \"DEV DOCS\"  -- CORRECT\n\n-- Invalid date format\ncreated >= 2024-01-01  -- WRONG\ncreated >= \"2024-01-01\"  -- CORRECT\n```\n\n**Best Practices:**\n- Always specify space when possible for faster queries\n- Use labels for categorization and filtering\n- Combine text search with specific fields\n- Cache frequently accessed page content\n- Handle version conflicts gracefully\n\n## Related References\n\n- `common-workflows.md` - Documentation sync and automation\n- `jira-queries.md` - Linking Confluence pages to Jira issues\n- `authentication-patterns.md` - API access configuration\n","skills/atlassian-mcp/references/jira-queries.md":"# Jira Queries and Operations\n\n---\n\n## JQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `project = \"PROJ\"` |\n| `!=` | Not equal | `status != Done` |\n| `~` | Contains (text search) | `summary ~ \"login bug\"` |\n| `!~` | Does not contain | `description !~ \"test\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `created >= -7d` |\n| `IN` | Multiple values | `status IN (Open, \"In Progress\")` |\n| `NOT IN` | Exclude values | `assignee NOT IN (john, jane)` |\n| `IS` | Null check | `assignee IS EMPTY` |\n| `IS NOT` | Not null | `resolution IS NOT EMPTY` |\n| `WAS` | Historical state | `status WAS \"In Progress\"` |\n| `CHANGED` | Field changed | `status CHANGED FROM Open` |\n\n### Field Reference\n\n**Standard Fields:**\n```jql\nproject = PROJ\nissuetype = Bug\nstatus = \"In Progress\"\npriority = High\nassignee = currentUser()\nreporter = \"john.doe\"\nresolution = Unresolved\nlabels = backend\ncomponent = \"API\"\nfixVersion = \"2.0\"\naffectsVersion = \"1.5\"\n```\n\n**Date Fields:**\n```jql\ncreated >= -30d                    -- Last 30 days\nupdated >= \"2024-01-01\"           -- Since specific date\ndue <= endOfWeek()                 -- Due this week\nresolved >= startOfMonth()         -- Resolved this month\n```\n\n**Text Search:**\n```jql\nsummary ~ \"authentication\"         -- Summary contains\ndescription ~ \"error AND login\"    -- Description search\ntext ~ \"payment failed\"            -- All text fields\ncomment ~ \"blocked\"                -- Comment contains\n```\n\n## Essential JQL Patterns\n\n### Sprint and Backlog Queries\n\n```jql\n-- Current sprint issues\nsprint in openSprints() AND project = PROJ\n\n-- Backlog items\nsprint IS EMPTY AND resolution IS EMPTY AND project = PROJ\n\n-- Sprint completion\nsprint = \"Sprint 23\" AND status = Done\n\n-- Spillover from last sprint\nsprint in closedSprints() AND resolution IS EMPTY\n\n-- Ready for sprint planning\nstatus = \"Ready for Dev\" AND sprint IS EMPTY\n```\n\n### Bug Tracking\n\n```jql\n-- Open bugs by priority\nissuetype = Bug AND resolution IS EMPTY ORDER BY priority DESC\n\n-- Critical production bugs\nissuetype = Bug AND priority IN (Highest, High)\n  AND labels = production AND resolution IS EMPTY\n\n-- Bugs created this week\nissuetype = Bug AND created >= startOfWeek()\n\n-- Bugs without reproduction steps\nissuetype = Bug AND \"Reproduction Steps\" IS EMPTY\n  AND resolution IS EMPTY\n\n-- Regression bugs\nissuetype = Bug AND labels = regression AND fixVersion = \"2.0\"\n```\n\n### Team Workload\n\n```jql\n-- My open issues\nassignee = currentUser() AND resolution IS EMPTY\n\n-- Unassigned high priority\nassignee IS EMPTY AND priority IN (Highest, High)\n  AND resolution IS EMPTY\n\n-- Team member workload\nassignee = \"jane.smith\" AND sprint in openSprints()\n\n-- Blocked issues\nstatus = Blocked OR labels = blocked\n\n-- Stale issues (no update in 14 days)\nupdated <= -14d AND resolution IS EMPTY\n```\n\n### Release Management\n\n```jql\n-- Release candidates\nfixVersion = \"2.0\" AND status = \"Ready for Release\"\n\n-- Missing fix version\nresolution = Done AND fixVersion IS EMPTY AND updated >= -30d\n\n-- Release blockers\nfixVersion = \"2.0\" AND priority = Blocker AND resolution IS EMPTY\n\n-- Changelog items\nfixVersion = \"2.0\" AND resolution = Done ORDER BY issuetype\n```\n\n## MCP Tool Calls\n\n### Searching Issues\n\n```typescript\n// Basic JQL search\nconst searchResult = await client.callTool({\n  name: \"jira_search\",\n  arguments: {\n    jql: \"project = PROJ AND sprint in openSprints()\",\n    max_results: 50,\n    fields: [\"summary\", \"status\", \"assignee\", \"priority\"]\n  }\n});\n\n// Parse response\nconst issues = JSON.parse(searchResult.content[0].text);\nfor (const issue of issues.issues) {\n  console.log(`${issue.key}: ${issue.fields.summary}`);\n}\n```\n\n### Getting Issue Details\n\n```typescript\n// Get single issue with all fields\nconst issue = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    expand: [\"changelog\", \"comments\", \"transitions\"]\n  }\n});\n\n// Get issue with specific fields\nconst issuePartial = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: [\"summary\", \"description\", \"customfield_10001\"]\n  }\n});\n```\n\n### Creating Issues\n\n```typescript\n// Create a bug\nconst newBug = await client.callTool({\n  name: \"jira_create_issue\",\n  arguments: {\n    project_key: \"PROJ\",\n    issue_type: \"Bug\",\n    summary: \"Login fails with SSO enabled\",\n    description: {\n      type: \"doc\",\n      version: 1,\n      content: [\n        {\n          type: \"paragraph\",\n          content: [{ type: \"text\", text: \"Users cannot log in when SSO is enabled.\" }]\n        },\n        {\n          type: \"heading\",\n          attrs: { level: 3 },\n          content: [{ type: \"text\", text: \"Steps to Reproduce\" }]\n        },\n        {\n          type: \"orderedList\",\n          content: [\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Enable SSO in settings\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Log out\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Attempt to log in via SSO\" }] }] }\n          ]\n        }\n      ]\n    },\n    priority: \"High\",\n    labels: [\"sso\", \"authentication\", \"production\"],\n    components: [\"Authentication\"],\n    assignee: \"jane.smith\"\n  }\n});\n\nconsole.log(`Created: ${newBug.content[0].text}`); // PROJ-456\n```\n\n### Updating Issues\n\n```typescript\n// Update issue fields\nawait client.callTool({\n  name: \"jira_update_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: {\n      summary: \"Updated summary\",\n      priority: { name: \"Highest\" },\n      labels: [\"urgent\", \"production\"]\n    }\n  }\n});\n\n// Add comment\nawait client.callTool({\n  name: \"jira_add_comment\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    body: \"Investigating this issue. Initial analysis suggests a race condition.\"\n  }\n});\n\n// Transition issue\nawait client.callTool({\n  name: \"jira_transition_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    transition: \"In Progress\"\n  }\n});\n```\n\n### Sprint Operations\n\n```typescript\n// Get active sprints\nconst sprints = await client.callTool({\n  name: \"jira_get_sprints\",\n  arguments: {\n    board_id: 42,\n    state: \"active\"\n  }\n});\n\n// Move issue to sprint\nawait client.callTool({\n  name: \"jira_move_to_sprint\",\n  arguments: {\n    sprint_id: 123,\n    issue_keys: [\"PROJ-100\", \"PROJ-101\", \"PROJ-102\"]\n  }\n});\n\n// Get sprint report\nconst report = await client.callTool({\n  name: \"jira_get_sprint_report\",\n  arguments: {\n    board_id: 42,\n    sprint_id: 123\n  }\n});\n```\n\n### Issue Linking\n\nUse `jira_create_issue_link` to create dependency relationships between issues.\n\n> **The parameter names are counterintuitive.** The naming reflects Jira's internal \"inward/outward\" link direction, not natural English. Verify every link call against the table below.\n\n#### Parameter Semantics for \"Blocks\" Links\n\n| Parameter | Role | Meaning |\n|-----------|------|---------|\n| `inward_issue_key` | **Blocker** | This issue blocks the other |\n| `outward_issue_key` | **Blocked** | This issue is blocked by the other |\n\n**Memory aid:** `inward_issue_key` = the issue receiving the inward description (\"is blocked by\") — but it is the *blocker*. Think: \"the inward key is where the arrow points FROM.\"\n\n#### Single Blocks Link\n\n```typescript\n// Make AUTH-1 block AUTH-2\n// AUTH-1 will show: \"blocks AUTH-2\"\n// AUTH-2 will show: \"is blocked by AUTH-1\"\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-1\",   // blocker\n    outward_issue_key: \"AUTH-2\"   // blocked\n  }\n});\n```\n\n#### Linking a Dependency Chain\n\nWhen creating a chain A → B → C (A blocks B, B blocks C):\n\n```typescript\nconst chain = [\n  { blocker: \"AUTH-1\", blocked: \"AUTH-2\" },\n  { blocker: \"AUTH-2\", blocked: \"AUTH-3\" },\n  { blocker: \"AUTH-3\", blocked: \"AUTH-4\" }\n];\n\nfor (const dep of chain) {\n  await client.callTool({\n    name: \"jira_create_issue_link\",\n    arguments: {\n      link_type: \"Blocks\",\n      inward_issue_key: dep.blocker,\n      outward_issue_key: dep.blocked\n    }\n  });\n\n  // Respect rate limits between link operations\n  await delay(100);\n}\n```\n\n#### Other Link Types\n\nThe same `inward`/`outward` pattern applies to all link types:\n\n| Link Type | `inward_issue_key` shows | `outward_issue_key` shows |\n|-----------|--------------------------|---------------------------|\n| `Blocks` | \"blocks [outward]\" | \"is blocked by [inward]\" |\n| `Duplicate` | \"duplicates [outward]\" | \"is duplicated by [inward]\" |\n| `Relates` | \"relates to [outward]\" | \"relates to [inward]\" |\n\n```typescript\n// Mark PROJ-10 as a duplicate of PROJ-5\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Duplicate\",\n    inward_issue_key: \"PROJ-10\",   // the duplicate\n    outward_issue_key: \"PROJ-5\"    // the original\n  }\n});\n```\n\n#### Anti-Pattern: Reversed Parameters\n\n```typescript\n// WRONG — This makes AUTH-2 block AUTH-1 (backwards!)\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-2\",   // accidentally made AUTH-2 the blocker\n    outward_issue_key: \"AUTH-1\"   // accidentally made AUTH-1 the blocked\n  }\n});\n```\n\nAlways verify: after creating a link, the blocker (`inward_issue_key`) should display \"blocks [outward]\" in its Jira issue view.\n\n## Pagination Handling\n\n```typescript\nasync function getAllIssues(jql: string): Promise<Issue[]> {\n  const allIssues: Issue[] = [];\n  let startAt = 0;\n  const maxResults = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"jira_search\",\n      arguments: {\n        jql,\n        start_at: startAt,\n        max_results: maxResults,\n        fields: [\"summary\", \"status\", \"assignee\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allIssues.push(...response.issues);\n\n    if (startAt + response.issues.length >= response.total) {\n      break;\n    }\n\n    startAt += maxResults;\n  }\n\n  return allIssues;\n}\n```\n\n## Bulk Operations\n\n```typescript\n// Bulk update with JQL\nasync function bulkUpdateLabels(jql: string, addLabels: string[]) {\n  const issues = await getAllIssues(jql);\n\n  for (const issue of issues) {\n    const existingLabels = issue.fields.labels || [];\n    await client.callTool({\n      name: \"jira_update_issue\",\n      arguments: {\n        issue_key: issue.key,\n        fields: {\n          labels: [...new Set([...existingLabels, ...addLabels])]\n        }\n      }\n    });\n\n    // Respect rate limits\n    await delay(100);\n  }\n}\n\n// Usage\nawait bulkUpdateLabels(\n  'project = PROJ AND sprint in openSprints() AND labels = backend',\n  ['q4-priority', 'needs-review']\n);\n```\n\n## Error Handling\n\n```typescript\nasync function safeJiraCall<T>(\n  operation: () => Promise<T>,\n  retries = 3\n): Promise<T> {\n  for (let attempt = 1; attempt <= retries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      const status = error.response?.status;\n\n      // Don't retry client errors (except rate limits)\n      if (status >= 400 && status < 500 && status !== 429) {\n        throw error;\n      }\n\n      // Rate limited - wait and retry\n      if (status === 429) {\n        const retryAfter = parseInt(error.response?.headers?.['retry-after'] || '60');\n        console.log(`Rate limited. Waiting ${retryAfter}s...`);\n        await delay(retryAfter * 1000);\n        continue;\n      }\n\n      // Server error - exponential backoff\n      if (attempt < retries) {\n        const backoff = Math.pow(2, attempt) * 1000;\n        console.log(`Attempt ${attempt} failed. Retrying in ${backoff}ms...`);\n        await delay(backoff);\n      } else {\n        throw error;\n      }\n    }\n  }\n  throw new Error('Unexpected end of retry loop');\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```jql\n-- Too broad (slow, may timeout)\nproject IS NOT EMPTY\n\n-- Missing quotes for multi-word values\nstatus = In Progress  -- WRONG\nstatus = \"In Progress\"  -- CORRECT\n\n-- Case sensitivity issues\nassignee = John  -- May fail\nassignee = \"john.doe@company.com\"  -- CORRECT\n\n-- Inefficient ordering\nORDER BY created  -- Missing direction\nORDER BY created DESC  -- CORRECT\n```\n\n## Related References\n\n- `common-workflows.md` - End-to-end workflow patterns\n- `authentication-patterns.md` - Credential setup for API calls\n- `confluence-operations.md` - Linking Jira issues to Confluence pages\n","skills/atlassian-mcp/references/mcp-server-setup.md":"# MCP Server Setup\n\n---\n\n## Server Options Overview\n\n### Atlassian Rovo MCP Server (official, recommended)\n\nAtlassian hosts the official MCP server. There is nothing to install: clients connect over Streamable HTTP to\n\n```\nhttps://mcp.atlassian.com/v2/mcp\n```\n\n**Capabilities:**\n- Jira, Confluence, Jira Service Management, Bitbucket Cloud, Compass, Loom, Goals, Projects, Teams, Focus, Talent and the Teamwork Graph\n- OAuth 2.1 browser sign-in, or API tokens for headless clients (admin-enabled)\n- Acts with the signed-in user's existing Atlassian permissions\n- Tools are grouped into permission groups (`read_jira`, `write_jira`, `search_jira`, `read_confluence`, ...) that org admins grant or revoke; `delete_jira` and `manage_jira` are off by default\n- Exposes a small set of primary tools and lets the client discover the rest on demand; append `?tools=all` for a flat list (useful behind MCP gateways)\n- Atlassian Cloud only\n\n**Endpoint versions:**\n\n| Endpoint | Status |\n|----------|--------|\n| `https://mcp.atlassian.com/v2/mcp` | Recommended for new setups |\n| `https://mcp.atlassian.com/v1/mcp`, `/v1/mcp/authv2` | Still supported; existing v1 connections start exposing v2 tools |\n| `https://mcp.atlassian.com/v1/sse` | Legacy SSE endpoint, unsupported after 30 June 2026; move to `/v2/mcp` |\n\nSources: [atlassian/atlassian-mcp-server](https://github.com/atlassian/atlassian-mcp-server), [Getting started with the Atlassian Rovo MCP Server](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/), [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/).\n\n### Community Servers\n\nUse these only when the official server does not fit, mainly for Jira or Confluence **Server/Data Center**.\n\n**mcp-atlassian (sooperset)** - Python, the most complete community option, and the only one here with Server/DC support. Published on PyPI as `mcp-atlassian` and as a Docker image:\n```bash\n# Run without installing (recommended)\nuvx mcp-atlassian --help\n\n# Or install\nuv tool install mcp-atlassian\npip install mcp-atlassian\n\n# Or Docker\ndocker pull ghcr.io/sooperset/mcp-atlassian:latest\n```\n\n**atlassian-mcp (xuanxt)** - TypeScript, Jira and Confluence Cloud only. Published on npm as `@xuandev/atlassian-mcp`:\n```bash\nnpx -y @xuandev/atlassian-mcp\n```\n\n### Comparison Matrix\n\n| Feature | Rovo MCP Server | sooperset | xuanxt |\n|---------|-----------------|-----------|--------|\n| Hosting | Atlassian-hosted | Self-run (stdio, Docker, HTTP) | Self-run |\n| Jira / Confluence Cloud | Yes | Yes | Yes |\n| Jira / Confluence Server/DC | No | Yes | No |\n| Other Atlassian products | JSM, Bitbucket, Compass, Loom, Goals, ... | Jira and Confluence only | Jira and Confluence only |\n| OAuth 2.1 | Yes | Yes (bring your own OAuth app) | No |\n| API token | Yes, if an admin enables it | Yes | Yes |\n| PAT (Server/DC) | No | Yes | No |\n| Admin controls | Permission groups, domain and IP allowlists, audit log | Env vars (`READ_ONLY_MODE`, filters) | Env vars |\n\n## Connecting to the Rovo MCP Server\n\n### Claude Code\n\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nThen run `/mcp` in a session and complete the OAuth sign-in in the browser.\n\n### Claude Desktop\n\nInstall it from **Settings → Extensions → Browse extensions → Plugins**, then search for **Atlassian**.\n\n### Other Clients\n\nAny client that speaks Streamable HTTP takes the server URL directly. Clients that only run local stdio servers can bridge with the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) proxy (Node.js 18+):\n\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-remote\", \"https://mcp.atlassian.com/v2/mcp\"]\n    }\n  }\n}\n```\n\n### Headless: API Token Authentication\n\nFor automations and other non-interactive clients. An org admin must first enable API token authentication (**Atlassian Administration → Rovo → Rovo MCP server → Authentication**). Jira Service Management tools require it.\n\n| Mechanism | Header |\n|-----------|--------|\n| Personal API token | `Authorization: Basic <base64(email:api_token)>` |\n| Service account API key | `Authorization: Bearer <api_key>` |\n\n```bash\n# Build the Basic credential from environment variables; never paste tokens inline\nexport ATLASSIAN_BASIC_AUTH=$(printf '%s:%s' \"$ATLASSIAN_EMAIL\" \"$ATLASSIAN_API_TOKEN\" | base64 -w0)\n\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp \\\n  --header \"Authorization: Basic ${ATLASSIAN_BASIC_AUTH}\"\n```\n\nCreate the token with only the scopes the tools need. See [Configuring authentication via API token](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/configuring-authentication-via-api-token/).\n\n### Admin Prerequisites\n\n- **Domain allowlist:** OAuth connections must come from a client domain allowed under Atlassian Administration → Rovo → Rovo MCP server. Atlassian-supported clients are allowed by default.\n- **IP allowlist:** if the org uses IP allowlisting, tool calls must come from an allowed IP. Hosted AI clients call from the provider's egress IPs, not the user's VPN.\n- **Audit:** every tool call is logged under Insights → Audit log (filter for *Rovo MCP User Actions*).\n\nSee [Control Atlassian Rovo MCP Server settings](https://support.atlassian.com/security-and-access-policies/docs/control-atlassian-rovo-mcp-server-settings/).\n\n## Configuring sooperset (Server/DC or Self-Hosted)\n\n**Cloud with API token:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"CONFLUENCE_URL\": \"https://your-company.atlassian.net/wiki\",\n        \"CONFLUENCE_USERNAME\": \"your-email@company.com\",\n        \"CONFLUENCE_API_TOKEN\": \"<confluence-api-token>\",\n        \"JIRA_URL\": \"https://your-company.atlassian.net\",\n        \"JIRA_USERNAME\": \"your-email@company.com\",\n        \"JIRA_API_TOKEN\": \"<jira-api-token>\"\n      }\n    }\n  }\n}\n```\n\n**Server/Data Center with PAT:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.company.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"<jira-personal-access-token>\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.company.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"<confluence-personal-access-token>\"\n      }\n    }\n  }\n}\n```\n\n### Environment Variables Reference\n\nFrom the project's [`.env.example`](https://github.com/sooperset/mcp-atlassian/blob/main/.env.example).\n\n**Jira:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `JIRA_URL` | Base URL of the Jira instance | Yes |\n| `JIRA_USERNAME` | Account email (Cloud) | Cloud only |\n| `JIRA_API_TOKEN` | API token (Cloud) | Cloud only |\n| `JIRA_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `JIRA_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Confluence:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `CONFLUENCE_URL` | Base URL, with `/wiki` for Cloud | Yes |\n| `CONFLUENCE_USERNAME` | Account email (Cloud) | Cloud only |\n| `CONFLUENCE_API_TOKEN` | API token (Cloud) | Cloud only |\n| `CONFLUENCE_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `CONFLUENCE_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Scope, safety and tuning:**\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `READ_ONLY_MODE` | Disable all create, update and delete tools | false |\n| `JIRA_PROJECTS_FILTER` | Comma-separated Jira project keys to limit operations to | All |\n| `CONFLUENCE_SPACES_FILTER` | Comma-separated Confluence space keys to limit operations to | All |\n| `TOOLSETS` | Tool groups to enable (`default`, `all`, or names such as `jira_agile`) | All |\n| `ENABLED_TOOLS` | Comma-separated tool names to enable | All in active toolsets |\n| `JIRA_TIMEOUT` / `CONFLUENCE_TIMEOUT` | HTTP timeout in seconds | 75 |\n| `ATLASSIAN_RETRY_TOTAL` | Retry attempts for read requests | Off |\n| `ATLASSIAN_REQUESTS_PER_SECOND` | Client-side request rate cap | Off |\n| `MCP_VERBOSE` / `MCP_VERY_VERBOSE` | INFO / DEBUG logging | WARNING |\n\n## Tool Names Differ by Server\n\nEach server names its tools differently. The examples in `jira-queries.md`, `confluence-operations.md` and `common-workflows.md` use sooperset's names (`jira_search`, `jira_get_issue`, ...). With the Rovo MCP Server, list the tools your client sees (or connect with `?tools=all`) and check [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/) before writing tool calls. Its tools take a `cloudId`; get it from `getAccessibleAtlassianResources`, or pin it in your agent instructions to skip that call.\n\n## Verification and Testing\n\n```bash\n# Rovo MCP Server: confirm the connection and auth status\nclaude mcp list\n\n# sooperset: confirm it starts and sees its environment\nuvx mcp-atlassian --help\nenv | grep -E \"^(JIRA|CONFLUENCE)_\" | sed 's/=.*/=***/'\n```\n\n### Test Connection (sooperset)\n\n```typescript\n// test-connection.ts\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nasync function testConnection(): Promise<void> {\n  const transport = new StdioClientTransport({\n    command: \"uvx\",\n    args: [\"mcp-atlassian\"],\n    env: process.env as Record<string, string>,\n  });\n\n  const client = new Client(\n    { name: \"test-client\", version: \"1.0.0\" },\n    { capabilities: {} }\n  );\n\n  await client.connect(transport);\n\n  // List available tools\n  const tools = await client.listTools();\n  console.log(\"Available tools:\", tools.tools.map(t => t.name));\n\n  // Test a simple read operation\n  const result = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: { issue_key: \"TEST-1\" }\n  });\n  console.log(\"Test result:\", result);\n\n  await client.close();\n}\n\ntestConnection().catch(console.error);\n```\n\n## When to Use Each Server\n\n**Choose the Rovo MCP Server when:**\n- Using Atlassian Cloud (the default choice)\n- Need products beyond Jira and Confluence\n- Want OAuth without running your own OAuth app\n- Need admin-managed permissions and audit logging\n\n**Choose sooperset when:**\n- Running Jira or Confluence Server/Data Center\n- Need PAT authentication\n- Must run the server inside your own network\n- Want local controls such as `READ_ONLY_MODE` or project and space filters\n\n**Choose xuanxt when:**\n- Want a small TypeScript server for Cloud that you can fork and extend\n\n## Troubleshooting\n\n**Rovo MCP Server: sign-in fails after moving from v1 to v2:**\nSome clients keep a cached client ID or `.well-known` credentials from v1. Clear them and authenticate again.\n\n**Rovo MCP Server: 403 or \"not allowed\":**\n- The client domain is not on the org's allowlist, or\n- IP allowlisting blocks the caller. Hosted clients call from the provider's IPs, so being on the VPN is not enough. Ask an org admin to review both.\n\n**Rovo MCP Server: a tool is missing:**\nIts permission group is not granted (`delete_jira` and `manage_jira` are off by default), or it needs the other auth method (JSM tools need an API token, Compass tools need OAuth).\n\n**\"Authentication failed\" with sooperset:**\n```bash\n# Verify API token is valid (Cloud)\ncurl -u \"<email>:<api-token>\" \\\n  \"https://your-company.atlassian.net/rest/api/3/myself\"\n\n# Verify PAT is valid (Server/DC)\ncurl -H \"Authorization: Bearer <personal-access-token>\" \\\n  \"https://jira.internal.company.com/rest/api/2/myself\"\n```\n\n**Rate limited (HTTP 429) with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"ATLASSIAN_REQUESTS_PER_SECOND\": \"5\",\n        \"ATLASSIAN_RETRY_TOTAL\": \"5\"\n      }\n    }\n  }\n}\n```\n\n**Debug logging with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"MCP_VERY_VERBOSE\": \"true\"\n      }\n    }\n  }\n}\n```\n\n## Security Best Practices\n\n1. **Prefer OAuth for people, scoped API tokens for automation** - Never share one user's token across a team\n2. **Never commit credentials** - Use environment variables or a secrets manager\n3. **Rotate API tokens regularly** - Set calendar reminders for 90-day rotation\n4. **Grant minimal permission groups** - Leave `delete_jira` and `manage_jira` off unless needed\n5. **Review the audit log** - Rovo MCP tool calls are recorded per user\n6. **Restrict network access** - Use domain and IP allowlists\n\n## Related References\n\n- `authentication-patterns.md` - OAuth 2.1 and API token setup details\n- `jira-queries.md` - JQL syntax after connection is established\n- `confluence-operations.md` - CQL and page operations\n","skills/chaos-engineer/SKILL.md":"---\nname: chaos-engineer\ndescription: Designs chaos experiments, creates failure injection frameworks, and facilitates game day exercises for distributed systems — producing runbooks, experiment manifests, rollback procedures, and post-mortem templates. Use when designing chaos experiments, implementing failure injection frameworks, or conducting game day exercises. Invoke for chaos experiments, resilience testing, blast radius control, game days, antifragile systems, fault injection, Chaos Monkey, Litmus Chaos.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: devops\n  triggers: chaos engineering, resilience testing, failure injection, game day, blast radius, chaos experiment, fault injection, Chaos Monkey, Litmus Chaos, antifragile\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: sre-engineer, devops-engineer, kubernetes-specialist\n---\n\n# Chaos Engineer\n\n## When to Use This Skill\n\n- Designing and executing chaos experiments\n- Implementing failure injection frameworks (Chaos Monkey, Litmus, etc.)\n- Planning and conducting game day exercises\n- Building blast radius controls and safety mechanisms\n- Setting up continuous chaos testing in CI/CD\n- Improving system resilience based on experiment findings\n\n## Core Workflow\n\n1. **System Analysis** - Map architecture, dependencies, critical paths, and failure modes\n2. **Experiment Design** - Define hypothesis, steady state, blast radius, and safety controls\n3. **Execute Chaos** - Run controlled experiments with monitoring and quick rollback\n4. **Learn & Improve** - Document findings, implement fixes, enhance monitoring\n5. **Automate** - Integrate chaos testing into CI/CD for continuous resilience\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Experiments | `references/experiment-design.md` | Designing hypothesis, blast radius, rollback |\n| Infrastructure | `references/infrastructure-chaos.md` | Server, network, zone, region failures |\n| Kubernetes | `references/kubernetes-chaos.md` | Pod, node, Litmus, chaos mesh experiments |\n| Tools & Automation | `references/chaos-tools.md` | Chaos Monkey, Gremlin, Pumba, CI/CD integration |\n| Game Days | `references/game-days.md` | Planning, executing, learning from game days |\n\n## Safety Checklist\n\nNon-obvious constraints that must be enforced on every experiment:\n\n- **Steady state first** — define and verify baseline metrics before injecting any failure\n- **Blast radius cap** — start with the smallest possible impact scope; expand only after validation\n- **Automated rollback ≤ 30 seconds** — abort path must be scripted and tested before the experiment begins\n- **Single variable** — change only one failure condition at a time until behaviour is well understood\n- **No production without safety nets** — customer-facing environments require circuit breakers, feature flags, or canary isolation\n- **Close the loop** — every experiment must produce a written learning summary and at least one tracked improvement\n\n## Output Templates\n\nWhen implementing chaos engineering, provide:\n1. Experiment design document (hypothesis, metrics, blast radius)\n2. Implementation code (failure injection scripts/manifests)\n3. Monitoring setup and alert configuration\n4. Rollback procedures and safety controls\n5. Learning summary and improvement recommendations\n\n## Concrete Example: Pod Failure Experiment (Litmus Chaos)\n\nThe following shows a complete experiment — from hypothesis to rollback — using Litmus Chaos on Kubernetes.\n\n### Step 1 — Define steady state and apply the experiment\n\n```bash\n# Verify baseline: p99 latency < 200ms, error rate < 0.1%\nkubectl get deploy my-service -n production\nkubectl top pods -n production -l app=my-service\n```\n\n### Step 2 — Create and apply a Litmus ChaosEngine manifest\n\n```yaml\n# chaos-pod-delete.yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: my-service-pod-delete\n  namespace: production\nspec:\n  appinfo:\n    appns: production\n    applabel: \"app=my-service\"\n    appkind: deployment\n  # Limit blast radius: only 1 replica at a time\n  engineState: active\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: \"60\"          # seconds\n            - name: CHAOS_INTERVAL\n              value: \"20\"          # delete one pod every 20s\n            - name: FORCE\n              value: \"false\"\n            - name: PODS_AFFECTED_PERC\n              value: \"33\"          # max 33% of replicas affected\n```\n\n```bash\n# Apply the experiment\nkubectl apply -f chaos-pod-delete.yaml\n\n# Watch experiment status\nkubectl describe chaosengine my-service-pod-delete -n production\nkubectl get chaosresult my-service-pod-delete-pod-delete -n production -w\n```\n\n### Step 3 — Monitor during the experiment\n\n```bash\n# Tail application logs for errors\nkubectl logs -l app=my-service -n production --since=2m -f\n\n# Check ChaosResult verdict when complete\nkubectl get chaosresult my-service-pod-delete-pod-delete \\\n  -n production -o jsonpath='{.status.experimentStatus.verdict}'\n```\n\n### Step 4 — Rollback / abort if steady state is violated\n\n```bash\n# Immediately stop the experiment\nkubectl patch chaosengine my-service-pod-delete \\\n  -n production --type merge -p '{\"spec\":{\"engineState\":\"stop\"}}'\n\n# Confirm all pods are healthy\nkubectl rollout status deployment/my-service -n production\n```\n\n## Concrete Example: Network Latency with toxiproxy\n\n```bash\n# Install toxiproxy CLI\nbrew install toxiproxy   # macOS; use the binary release on Linux\n\n# Start toxiproxy server (runs alongside your service)\ntoxiproxy-server &\n\n# Create a proxy for your downstream dependency\ntoxiproxy-cli create -l 0.0.0.0:22222 -u downstream-db:5432 db-proxy\n\n# Inject 300ms latency with 10% jitter — blast radius: this proxy only\ntoxiproxy-cli toxic add db-proxy -t latency -a latency=300 -a jitter=30\n\n# Run your load test / observe metrics here ...\n\n# Remove the toxic to restore normal behaviour\ntoxiproxy-cli toxic remove db-proxy -n latency_downstream\n```\n\n## Concrete Example: Chaos Monkey (Spinnaker / standalone)\n\n```bash\n# chaos-monkey-config.yml — restrict to a single ASG\ndeployment:\n  enabled: true\n  regionIndependence: false\nchaos:\n  enabled: true\n  meanTimeBetweenKillsInWorkDays: 2\n  minTimeBetweenKillsInWorkDays: 1\n  grouping: APP           # kill one instance per app, not per cluster\n  exceptions:\n    - account: production\n      region: us-east-1\n      detail: \"*-canary\"  # never kill canary instances\n\n# Apply and trigger a manual kill for testing\nchaos-monkey --app my-service --account staging --dry-run false\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/devops/chaos-engineer/)\n","skills/chaos-engineer/references/chaos-tools.md":"# Chaos Engineering Tools & Automation\n\n## Chaos Monkey (Netflix)\n\n```python\n# Chaos Monkey configuration for Spinnaker\n{\n  \"enabled\": true,\n  \"schedule\": {\n    \"enabled\": true,\n    \"frequency\": 1,  # Run once per day\n    \"frequencyUnit\": \"DAYS\",\n    \"start\": \"09:00\",\n    \"end\": \"15:00\",\n    \"timezone\": \"America/Los_Angeles\"\n  },\n  \"grouping\": \"cluster\",\n  \"regionsAreIndependent\": true,\n  \"exceptions\": [\n    {\n      \"type\": \"Opt-In\",\n      \"account\": \"production\",\n      \"stack\": \"*\",\n      \"detail\": \"*\"\n    }\n  ],\n  \"minTimeBetweenKillsInWorkDays\": 2,\n  \"maxAppsPerDay\": 5,\n  \"clusters\": [\n    {\n      \"app\": \"myapp\",\n      \"stack\": \"production\",\n      \"enabled\": true,\n      \"regions\": [\"us-east-1\", \"us-west-2\"],\n      \"meanTimeBetweenKillsInWorkDays\": 2,\n      \"minTimeBetweenKillsInWorkDays\": 1,\n      \"maxTerminationsPerDay\": 1\n    }\n  ]\n}\n```\n\n```bash\n#!/bin/bash\n# Simpl Chaos Monkey implementation\n\nINSTANCE_COUNT=5\nKILL_PERCENTAGE=20\n\n# Get running instances from ASG\nINSTANCES=$(aws autoscaling describe-auto-scaling-groups \\\n  --auto-scaling-group-names my-asg \\\n  --query 'AutoScalingGroups[0].Instances[?LifecycleState==`InService`].InstanceId' \\\n  --output text)\n\n# Calculate number to terminate\nTOTAL=$(echo \"$INSTANCES\" | wc -w)\nTO_KILL=$(( TOTAL * KILL_PERCENTAGE / 100 ))\n\nif [ $TO_KILL -eq 0 ]; then\n  TO_KILL=1\nfi\n\n# Randomly select and terminate instances\necho \"$INSTANCES\" | tr ' ' '\\n' | shuf | head -n $TO_KILL | while read instance; do\n  echo \"Terminating instance: $instance\"\n  aws ec2 terminate-instances --instance-ids \"$instance\"\n  sleep 30  # Wait between terminations\ndone\n```\n\n## Gremlin Integration\n\n```python\nimport requests\nfrom typing import Literal\n\nclass GremlinClient:\n    def __init__(self, api_key: str, team_id: str):\n        self.api_key = api_key\n        self.team_id = team_id\n        self.base_url = \"https://api.gremlin.com/v1\"\n        self.headers = {\n            \"Authorization\": f\"Key {api_key}\",\n            \"Content-Type\": \"application/json\"\n        }\n\n    def create_cpu_attack(\n        self,\n        targets: list[str],\n        length: int = 60,\n        cores: int = 1,\n        percent: int = 50\n    ):\n        \"\"\"Launch CPU resource attack.\"\"\"\n        payload = {\n            \"command\": {\n                \"type\": \"cpu\",\n                \"args\": [\n                    \"-l\", str(length),\n                    \"-c\", str(cores),\n                    \"-p\", str(percent)\n                ]\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def create_network_attack(\n        self,\n        targets: list[str],\n        attack_type: Literal[\"latency\", \"packet_loss\", \"blackhole\"],\n        length: int = 60,\n        **kwargs\n    ):\n        \"\"\"Launch network attack.\"\"\"\n        args = [\"-l\", str(length)]\n\n        if attack_type == \"latency\":\n            # kwargs: delay_ms, jitter_ms\n            args.extend([\"-m\", str(kwargs.get('delay_ms', 100))])\n            if 'jitter_ms' in kwargs:\n                args.extend([\"-j\", str(kwargs['jitter_ms'])])\n\n        elif attack_type == \"packet_loss\":\n            # kwargs: percent\n            args.extend([\"-p\", str(kwargs.get('percent', 10))])\n\n        elif attack_type == \"blackhole\":\n            # kwargs: port, protocol\n            if 'port' in kwargs:\n                args.extend([\"--port\", str(kwargs['port'])])\n            if 'protocol' in kwargs:\n                args.extend([\"--protocol\", kwargs['protocol']])\n\n        payload = {\n            \"command\": {\n                \"type\": attack_type,\n                \"args\": args\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def halt_attack(self, attack_id: str):\n        \"\"\"Stop running attack.\"\"\"\n        response = requests.delete(\n            f\"{self.base_url}/attacks/{attack_id}\",\n            headers=self.headers\n        )\n        return response.status_code == 200\n\n    def create_scenario(self, name: str, attacks: list[dict]):\n        \"\"\"Create reusable attack scenario.\"\"\"\n        payload = {\n            \"name\": name,\n            \"description\": f\"Chaos scenario: {name}\",\n            \"graph\": {\n                \"nodes\": attacks\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/scenarios\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n# Example usage\ngremlin = GremlinClient(api_key=\"<gremlin-api-key>\", team_id=\"<gremlin-team-id>\")\n\n# CPU attack on specific containers\ngremlin.create_cpu_attack(\n    targets=[\"container-id-123\", \"container-id-456\"],\n    length=300,  # 5 minutes\n    cores=2,\n    percent=80\n)\n\n# Network latency attack\ngremlin.create_network_attack(\n    targets=[\"host-abc\"],\n    attack_type=\"latency\",\n    length=180,\n    delay_ms=500,\n    jitter_ms=100\n)\n```\n\n## CI/CD Integration\n\n```yaml\n# GitHub Actions workflow for chaos testing\nname: Chaos Engineering Tests\n\non:\n  schedule:\n    - cron: '0 10 * * 1-5'  # Weekdays at 10 AM\n  workflow_dispatch:  # Manual trigger\n\njobs:\n  chaos-tests:\n    runs-on: ubuntu-latest\n    environment: staging\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v3\n\n      - name: Setup kubectl\n        uses: azure/setup-kubectl@v3\n\n      - name: Configure AWS credentials\n        uses: aws-actions/configure-aws-credentials@v2\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: us-east-1\n\n      - name: Update kubeconfig\n        run: |\n          aws eks update-kubeconfig --name staging-cluster --region us-east-1\n\n      - name: Install Litmus\n        run: |\n          kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v2.14.0.yaml\n          kubectl wait --for=condition=Ready pods -l app.kubernetes.io/component=operator --timeout=300s\n\n      - name: Run pod-delete chaos experiment\n        run: |\n          kubectl apply -f .github/chaos/pod-delete-experiment.yaml\n          kubectl wait --for=condition=Complete chaosengine/pod-delete-chaos --timeout=600s\n\n      - name: Verify system recovery\n        run: |\n          # Check all pods are running\n          kubectl wait --for=condition=Ready pods -l app=myapp --timeout=300s\n\n          # Verify no error rate spike\n          ERROR_RATE=$(curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\"5..\\\"}[5m])\" | jq -r '.data.result[0].value[1]')\n\n          if (( $(echo \"$ERROR_RATE > 0.01\" | bc -l) )); then\n            echo \"Error rate too high: $ERROR_RATE\"\n            exit 1\n          fi\n\n      - name: Cleanup chaos resources\n        if: always()\n        run: |\n          kubectl delete chaosengine --all\n          kubectl delete chaosexperiments --all\n\n      - name: Report results to Slack\n        if: failure()\n        uses: slackapi/slack-github-action@v1\n        with:\n          payload: |\n            {\n              \"text\": \"Chaos test failed in staging\",\n              \"blocks\": [\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"Chaos engineering test failed. System did not recover properly.\"\n                  }\n                }\n              ]\n            }\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}\n```\n\n## Jenkins Pipeline\n\n```groovy\n// Jenkinsfile for chaos testing\npipeline {\n    agent any\n\n    parameters {\n        choice(\n            name: 'ENVIRONMENT',\n            choices: ['dev', 'staging'],\n            description: 'Target environment'\n        )\n        choice(\n            name: 'CHAOS_TYPE',\n            choices: ['pod-delete', 'network-latency', 'cpu-stress'],\n            description: 'Type of chaos experiment'\n        )\n        string(\n            name: 'DURATION',\n            defaultValue: '300',\n            description: 'Chaos duration in seconds'\n        )\n    }\n\n    stages {\n        stage('Pre-flight Check') {\n            steps {\n                script {\n                    // Verify steady state before chaos\n                    def errorRate = sh(\n                        script: '''\n                            curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\\\"5..\\\\\"}[5m])\" | jq -r '.data.result[0].value[1]'\n                        ''',\n                        returnStdout: true\n                    ).trim()\n\n                    if (errorRate.toFloat() > 0.01) {\n                        error(\"System not in steady state. Error rate: ${errorRate}\")\n                    }\n                }\n            }\n        }\n\n        stage('Run Chaos Experiment') {\n            steps {\n                script {\n                    def chaosManifest = \"\"\"\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: jenkins-chaos-${env.BUILD_NUMBER}\n  namespace: ${params.ENVIRONMENT}\nspec:\n  appinfo:\n    appns: '${params.ENVIRONMENT}'\n    applabel: 'app=myapp'\n    appkind: 'deployment'\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: ${params.CHAOS_TYPE}\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '${params.DURATION}'\n\"\"\"\n\n                    writeFile file: 'chaos-manifest.yaml', text: chaosManifest\n\n                    sh '''\n                        kubectl apply -f chaos-manifest.yaml\n                        kubectl wait --for=condition=Complete chaosengine/jenkins-chaos-${BUILD_NUMBER} --timeout=900s\n                    '''\n                }\n            }\n        }\n\n        stage('Verify Recovery') {\n            steps {\n                sh '''\n                    # Wait for system to stabilize\n                    sleep 60\n\n                    # Check pod status\n                    kubectl get pods -n ${ENVIRONMENT} -l app=myapp\n\n                    # Verify all pods running\n                    READY_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '[.items[] | select(.status.phase==\"Running\")] | length')\n                    TOTAL_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '.items | length')\n\n                    if [ \"$READY_PODS\" -ne \"$TOTAL_PODS\" ]; then\n                        echo \"Not all pods recovered: $READY_PODS/$TOTAL_PODS ready\"\n                        exit 1\n                    fi\n                '''\n            }\n        }\n\n        stage('Extract Learnings') {\n            steps {\n                script {\n                    // Get chaos result\n                    def chaosResult = sh(\n                        script: \"kubectl get chaosresult -n ${params.ENVIRONMENT} -o json\",\n                        returnStdout: true\n                    )\n\n                    // Parse and store results\n                    writeFile file: \"chaos-result-${env.BUILD_NUMBER}.json\", text: chaosResult\n\n                    // Archive results\n                    archiveArtifacts artifacts: \"chaos-result-${env.BUILD_NUMBER}.json\"\n                }\n            }\n        }\n    }\n\n    post {\n        always {\n            // Cleanup\n            sh '''\n                kubectl delete chaosengine jenkins-chaos-${BUILD_NUMBER} -n ${ENVIRONMENT} || true\n            '''\n        }\n\n        failure {\n            // Notify team\n            slackSend(\n                color: 'danger',\n                message: \"Chaos test failed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}\"\n            )\n        }\n\n        success {\n            slackSend(\n                color: 'good',\n                message: \"Chaos test passed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}. System recovered successfully.\"\n            )\n        }\n    }\n}\n```\n\n## Continuous Chaos Dashboard\n\n```python\n# Flask app for chaos monitoring dashboard\nfrom flask import Flask, render_template, jsonify\nimport requests\nfrom datetime import datetime, timedelta\n\napp = Flask(__name__)\n\nclass ChaosDashboard:\n    def __init__(self, prometheus_url: str):\n        self.prometheus = prometheus_url\n\n    def get_experiment_metrics(self, hours: int = 24):\n        \"\"\"Get chaos experiment results from last N hours.\"\"\"\n        end = datetime.now()\n        start = end - timedelta(hours=hours)\n\n        query = f'''\n            sum by (experiment, verdict) (\n                increase(litmuschaos_experiment_verdict[{hours}h])\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n    def get_mttr_trend(self):\n        \"\"\"Get MTTR trend over time.\"\"\"\n        query = '''\n            avg_over_time(\n                avg(\n                    time() - timestamp(\n                        kube_pod_status_phase{phase=\"Running\"} == 1\n                    )\n                )[7d:]\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n@app.route('/api/chaos-summary')\ndef chaos_summary():\n    dashboard = ChaosDashboard(prometheus_url=\"http://prometheus:9090\")\n\n    return jsonify({\n        \"experiments\": dashboard.get_experiment_metrics(hours=24),\n        \"mttr_trend\": dashboard.get_mttr_trend(),\n        \"timestamp\": datetime.now().isoformat()\n    })\n\nif __name__ == '__main__':\n    app.run(host='0.0.0.0', port=5000)\n```\n\n## Quick Reference\n\n| Tool | Use Case | Integration |\n|------|----------|-------------|\n| Chaos Monkey | Random instance termination | Spinnaker/AWS ASG |\n| Gremlin | SaaS chaos platform | API/Web UI |\n| Litmus | Kubernetes chaos | Kubectl/Helm |\n| Chaos Mesh | Advanced K8s chaos | CRDs/Dashboard |\n| Toxiproxy | Network proxy chaos | Docker/API |\n| Pumba | Container chaos | Docker CLI |\n","skills/chaos-engineer/references/experiment-design.md":"# Chaos Experiment Design\n\n## Experiment Template\n\n```yaml\nname: \"Database Connection Pool Exhaustion\"\nhypothesis: \"When the database connection pool is exhausted, the application will gracefully degrade and return 503 errors without cascading failures\"\n\nsteady_state:\n  metrics:\n    - name: \"Error Rate\"\n      threshold: \"< 0.1%\"\n      source: \"prometheus\"\n      query: \"rate(http_requests_total{status=~'5..'}[5m])\"\n    - name: \"Latency P99\"\n      threshold: \"< 500ms\"\n      source: \"datadog\"\n    - name: \"Active Connections\"\n      threshold: \"> 10\"\n      query: \"pg_stat_activity_count\"\n\nblast_radius:\n  environment: \"staging\"\n  traffic_percentage: 10\n  duration_seconds: 300\n  max_error_rate: \"5%\"\n  auto_rollback: true\n\ninjection:\n  type: \"resource_exhaustion\"\n  target: \"database_connections\"\n  method: \"connection_leak\"\n  parameters:\n    leak_rate: 5  # connections per second\n    max_leaked: 50\n\nsafety:\n  rollback_triggers:\n    - \"error_rate > 5%\"\n    - \"manual_kill_switch\"\n    - \"duration_exceeded\"\n  rollback_time_limit_seconds: 30\n  alerts:\n    - slack: \"#chaos-engineering\"\n    - pagerduty: \"chaos-team\"\n\nsuccess_criteria:\n  - \"Circuit breakers activate within 10s\"\n  - \"503 errors returned (not 500)\"\n  - \"No cascading failures to other services\"\n  - \"System recovers within 60s of rollback\"\n```\n\n## Hypothesis Formulation\n\n```python\ndef create_hypothesis(component: str, failure: str, expected_behavior: str) -> dict:\n    \"\"\"\n    Create well-formed chaos hypothesis.\n\n    Format: \"Given [normal state], when [failure occurs],\n             then [expected behavior], measured by [metrics]\"\n    \"\"\"\n    return {\n        \"given\": f\"System is in steady state with {component} functioning normally\",\n        \"when\": f\"{failure} occurs\",\n        \"then\": expected_behavior,\n        \"measured_by\": [\n            \"Error rate remains below threshold\",\n            \"Latency stays within SLO\",\n            \"No data loss or corruption\",\n            \"Recovery time within RTO\"\n        ]\n    }\n\n# Example\nhypothesis = create_hypothesis(\n    component=\"payment service\",\n    failure=\"50% packet loss to payment gateway\",\n    expected_behavior=\"Requests timeout gracefully, retry queue activates, \"\n                     \"users see clear error messages\"\n)\n```\n\n## Blast Radius Control\n\n```python\nfrom dataclasses import dataclass\nfrom enum import Enum\n\nclass BlastRadiusLevel(Enum):\n    MINIMAL = \"single_instance_dev\"\n    LOW = \"single_instance_staging\"\n    MEDIUM = \"percentage_staging\"\n    HIGH = \"percentage_production\"\n    CRITICAL = \"full_production\"\n\n@dataclass\nclass BlastRadiusConfig:\n    level: BlastRadiusLevel\n    environment: str\n    target_percentage: float  # 0-100\n    canary_users: list[str]\n    feature_flag: str\n    auto_rollback: bool\n    max_duration_seconds: int\n\n    def validate(self):\n        \"\"\"Enforce safety rules.\"\"\"\n        if self.level == BlastRadiusLevel.CRITICAL:\n            raise ValueError(\"CRITICAL blast radius requires explicit approval\")\n\n        if self.environment == \"production\" and self.target_percentage > 10:\n            if not self.feature_flag or not self.auto_rollback:\n                raise ValueError(\"Production >10% requires feature flag AND auto-rollback\")\n\n        if self.max_duration_seconds > 600:\n            raise ValueError(\"Max duration cannot exceed 10 minutes without approval\")\n\n# Progressive blast radius expansion\ndef progressive_rollout() -> list[BlastRadiusConfig]:\n    return [\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MINIMAL,\n            environment=\"dev\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_dev\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.LOW,\n            environment=\"staging\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_staging\",\n            auto_rollback=True,\n            max_duration_seconds=600\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MEDIUM,\n            environment=\"production\",\n            target_percentage=1,\n            canary_users=[\"internal_team\"],\n            feature_flag=\"chaos_prod_canary\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        )\n    ]\n```\n\n## Safety Mechanisms\n\n```python\nimport asyncio\nfrom typing import Callable\n\nclass ChaosExperimentSafety:\n    def __init__(self, config: dict):\n        self.config = config\n        self.kill_switch_active = False\n        self.metrics = {}\n\n    async def run_with_safety(self, chaos_fn: Callable):\n        \"\"\"Execute chaos with automatic safety checks.\"\"\"\n        # Pre-flight checks\n        if not await self.verify_steady_state():\n            raise Exception(\"System not in steady state - aborting\")\n\n        # Set up rollback trigger\n        rollback_task = asyncio.create_task(self.monitor_for_rollback())\n        chaos_task = asyncio.create_task(chaos_fn())\n\n        try:\n            # Wait for either chaos completion or rollback trigger\n            done, pending = await asyncio.wait(\n                [chaos_task, rollback_task],\n                return_when=asyncio.FIRST_COMPLETED\n            )\n\n            if rollback_task in done:\n                # Rollback triggered - cancel chaos\n                chaos_task.cancel()\n                await self.rollback()\n\n        finally:\n            await self.ensure_system_recovery()\n\n    async def verify_steady_state(self) -> bool:\n        \"\"\"Check all steady state metrics are within threshold.\"\"\"\n        for metric in self.config['steady_state']['metrics']:\n            value = await self.query_metric(metric['query'])\n            if not self.within_threshold(value, metric['threshold']):\n                return False\n        return True\n\n    async def monitor_for_rollback(self):\n        \"\"\"Continuously monitor for rollback triggers.\"\"\"\n        start_time = asyncio.get_event_loop().time()\n\n        while True:\n            # Check duration limit\n            if asyncio.get_event_loop().time() - start_time > \\\n               self.config['blast_radius']['duration_seconds']:\n                return \"duration_exceeded\"\n\n            # Check manual kill switch\n            if self.kill_switch_active:\n                return \"manual_kill_switch\"\n\n            # Check error rate\n            error_rate = await self.query_metric(\"error_rate\")\n            if error_rate > float(self.config['blast_radius']['max_error_rate'].strip('%')):\n                return \"error_rate_exceeded\"\n\n            await asyncio.sleep(5)  # Check every 5 seconds\n```\n\n## Quick Reference\n\n| Phase | Key Actions | Time Limit |\n|-------|-------------|------------|\n| Design | Hypothesis, metrics, blast radius | 1 hour |\n| Review | Team review, safety check | 30 min |\n| Prepare | Setup monitoring, rollback | 1 hour |\n| Execute | Run experiment, monitor | 5-10 min |\n| Rollback | Restore steady state | < 30 sec |\n| Learn | Document findings, plan fixes | 2 hours |\n","skills/chaos-engineer/references/game-days.md":"# Game Day Planning & Execution\n\n## Game Day Planning Template\n\n```yaml\ngame_day:\n  name: \"Database Failover Drill\"\n  date: \"2025-01-15\"\n  time: \"10:00-12:00 PST\"\n  environment: \"staging\"  # Start in staging\n\n  objectives:\n    - \"Verify RDS failover to standby in under 2 minutes\"\n    - \"Validate application auto-reconnect logic\"\n    - \"Test monitoring and alerting effectiveness\"\n    - \"Practice incident response procedures\"\n\n  participants:\n    facilitator: \"chaos-engineer@company.com\"\n    observers:\n      - \"sre-team@company.com\"\n      - \"dev-team@company.com\"\n    responders:\n      - \"on-call-engineer@company.com\"\n      - \"database-admin@company.com\"\n    stakeholders:\n      - \"engineering-manager@company.com\"\n\n  scenarios:\n    - name: \"Primary database instance failure\"\n      duration_minutes: 30\n      steps:\n        - action: \"Force RDS instance reboot with failover\"\n          expected: \"Failover to standby in <2 min\"\n          success_criteria:\n            - \"Downtime < 2 minutes\"\n            - \"No data loss\"\n            - \"Alerts fired correctly\"\n\n    - name: \"Network partition to database\"\n      duration_minutes: 20\n      steps:\n        - action: \"Block network traffic to RDS security group\"\n          expected: \"Application switches to read replica\"\n          success_criteria:\n            - \"Read-only mode activated\"\n            - \"User-facing error messages clear\"\n\n  communication_plan:\n    announcement_channel: \"#game-day-announcements\"\n    war_room: \"Zoom link: https://...\"\n    status_updates_every: \"5 minutes\"\n    escalation_contacts:\n      - name: \"VP Engineering\"\n        phone: \"+1-555-0100\"\n        threshold: \"downtime > 5 minutes\"\n\n  rollback_plan:\n    automatic_rollback_triggers:\n      - \"production traffic affected\"\n      - \"customer complaints received\"\n      - \"error_rate > 10%\"\n    manual_rollback_command: \"aws rds reboot-db-instance --db-instance-identifier primary --force-failover\"\n    rollback_time_limit_seconds: 60\n\n  success_metrics:\n    - metric: \"RTO (Recovery Time Objective)\"\n      target: \"< 2 minutes\"\n      measurement: \"time between failure and full recovery\"\n    - metric: \"Alert accuracy\"\n      target: \"100%\"\n      measurement: \"all expected alerts fired\"\n    - metric: \"Team response time\"\n      target: \"< 5 minutes\"\n      measurement: \"time to acknowledge incident\"\n\n  post_mortem:\n    scheduled_for: \"2025-01-16 14:00\"\n    template: \"game-day-retro.md\"\n    required_attendees: \"all participants\"\n```\n\n## Game Day Runbook\n\n```markdown\n# Database Failover Game Day Runbook\n\n**Date**: January 15, 2025\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Pre-Game Checklist (T-30 min)\n\n- [ ] Verify all participants joined war room\n- [ ] Confirm monitoring dashboards accessible\n- [ ] Test rollback procedures work\n- [ ] Announce game day start in #engineering\n- [ ] Verify staging environment healthy\n- [ ] Set up screen recording for timeline\n- [ ] Prepare incident timeline spreadsheet\n\n## Timeline\n\n### 10:00 - Introduction (10 min)\n- Facilitator explains objectives\n- Review scenarios and success criteria\n- Confirm roles and communication channels\n- Remind everyone: this is a learning exercise\n\n### 10:10 - Scenario 1: Primary DB Failure (30 min)\n\n**T+0 (10:10)** - Inject failure\n```bash\naws rds reboot-db-instance \\\n  --db-instance-identifier staging-primary \\\n  --force-failover\n```\n\n**Expected Timeline**:\n- T+0: Reboot initiated\n- T+30s: Primary becomes unavailable\n- T+60s: DNS updated to standby\n- T+90s: Application reconnects\n- T+120s: Full recovery\n\n**Observer Tasks**:\n- [ ] Record exact time of failure injection\n- [ ] Monitor application error logs\n- [ ] Track alert notifications\n- [ ] Document team response actions\n- [ ] Screenshot dashboard states\n\n**Questions to Answer**:\n- How long until first alert?\n- Did application auto-reconnect?\n- Were customers impacted?\n- What manual interventions needed?\n\n### 10:40 - Debrief Scenario 1 (10 min)\n- What went well?\n- What could improve?\n- Any surprises?\n- Action items identified\n\n### 10:50 - Scenario 2: Network Partition (20 min)\n\n**T+0 (10:50)** - Inject failure\n```bash\n# Block database security group ingress\naws ec2 revoke-security-group-ingress \\\n  --group-id sg-xxxxx \\\n  --protocol tcp \\\n  --port 5432 \\\n  --cidr 10.0.0.0/16\n```\n\n**Expected Behavior**:\n- Connection timeouts occur\n- Circuit breaker opens\n- Read-only mode activates\n- Clear error messages shown\n\n**Observer Tasks**:\n- [ ] Monitor circuit breaker state\n- [ ] Verify read-replica failover\n- [ ] Check user-facing error messages\n- [ ] Track degraded service duration\n\n### 11:10 - Debrief Scenario 2 (10 min)\n\n### 11:20 - Scenario 3: Surprise! (20 min)\n\n**Facilitator Note**: Don't announce this scenario details beforehand.\nTest true incident response capability.\n\n**Hidden Scenario**: Combination failure\n1. Database connection pool leak\n2. Simultaneous cache invalidation\n\n```python\n# Connection leak simulator\nimport psycopg2\nconnections = []\nfor i in range(100):\n    conn = psycopg2.connect(DATABASE_URL)\n    connections.append(conn)\n    # Intentionally don't close\n```\n\n**Observer Tasks**:\n- [ ] How long to identify root cause?\n- [ ] Communication effectiveness\n- [ ] Cross-team coordination\n- [ ] Escalation decisions\n\n### 11:40 - Final Debrief & Wrap-up (20 min)\n\n**Debrief Questions**:\n1. What worked well?\n2. What didn't work?\n3. What surprised us?\n4. What are our top 3 action items?\n5. When should we run this again?\n\n## Post-Game Checklist\n\n- [ ] Restore all services to normal state\n- [ ] Verify no lingering issues\n- [ ] Collect all observer notes\n- [ ] Export metrics and dashboards\n- [ ] Schedule post-mortem meeting\n- [ ] Send thank-you to participants\n- [ ] Create action item tickets\n- [ ] Update runbooks based on learnings\n```\n\n## Game Day Observation Template\n\n```python\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom typing import List\n\n@dataclass\nclass GameDayObservation:\n    timestamp: datetime\n    observer: str\n    scenario: str\n    observation: str\n    category: str  # technical, process, communication, surprise\n    severity: str  # info, concern, critical\n    photo_url: str = \"\"\n\n@dataclass\nclass GameDayMetrics:\n    scenario_name: str\n    start_time: datetime\n    end_time: datetime\n\n    # Technical metrics\n    time_to_detect_seconds: float\n    time_to_respond_seconds: float\n    time_to_recover_seconds: float\n    error_rate_peak: float\n    alerts_fired: List[str] = field(default_factory=list)\n    alerts_missed: List[str] = field(default_factory=list)\n\n    # Team metrics\n    responders_involved: int\n    escalations_needed: int\n    communication_gaps: List[str] = field(default_factory=list)\n\n    # Success criteria\n    met_rto: bool = False\n    met_rpo: bool = False\n    zero_customer_impact: bool = False\n\n    def calculate_mttr(self) -> float:\n        \"\"\"Mean Time To Recovery\"\"\"\n        return (self.end_time - self.start_time).total_seconds()\n\n    def success_rate(self) -> float:\n        \"\"\"Percentage of success criteria met\"\"\"\n        criteria = [\n            self.met_rto,\n            self.met_rpo,\n            self.zero_customer_impact,\n            len(self.alerts_missed) == 0\n        ]\n        return sum(criteria) / len(criteria) * 100\n\n# Example usage\nmetrics = GameDayMetrics(\n    scenario_name=\"Database Failover\",\n    start_time=datetime(2025, 1, 15, 10, 10, 0),\n    end_time=datetime(2025, 1, 15, 10, 12, 30),\n    time_to_detect_seconds=15.0,\n    time_to_respond_seconds=45.0,\n    time_to_recover_seconds=150.0,\n    error_rate_peak=0.05,\n    alerts_fired=[\"DatabaseConnectionError\", \"HighLatency\"],\n    alerts_missed=[\"FailoverInitiated\"],\n    responders_involved=3,\n    escalations_needed=0,\n    met_rto=True,\n    met_rpo=True,\n    zero_customer_impact=True\n)\n\nprint(f\"MTTR: {metrics.calculate_mttr()}s\")\nprint(f\"Success Rate: {metrics.success_rate()}%\")\n```\n\n## Surprise Scenarios Library\n\n```yaml\n# Keep these secret until game day!\nsurprise_scenarios:\n  - name: \"Cascading Failure\"\n    description: \"Primary failure triggers secondary issue\"\n    injection:\n      - \"Database failover (expected)\"\n      - \"Cache eviction due to new primary IP (surprise!)\"\n    learning_goals:\n      - \"Do we understand our dependencies?\"\n      - \"Can we handle multiple simultaneous issues?\"\n\n  - name: \"Monitoring Blind Spot\"\n    description: \"Failure that doesn't trigger alerts\"\n    injection:\n      - \"Gradual connection pool leak\"\n      - \"No immediate alerts fire\"\n    learning_goals:\n      - \"How do we discover issues without alerts?\"\n      - \"Do we have adequate monitoring coverage?\"\n\n  - name: \"Documentation Failure\"\n    description: \"Runbook is outdated or incorrect\"\n    setup:\n      - \"Modify runbook to have incorrect commands\"\n      - \"Or remove runbook entirely\"\n    learning_goals:\n      - \"Can team problem-solve without docs?\"\n      - \"How quickly can we update documentation?\"\n\n  - name: \"Key Person Unavailable\"\n    description: \"Subject matter expert is unreachable\"\n    setup:\n      - \"Ask SME to not respond for 15 minutes\"\n    learning_goals:\n      - \"Is knowledge properly distributed?\"\n      - \"Can team succeed without specific person?\"\n\n  - name: \"Partial Degradation\"\n    description: \"Service works but slowly\"\n    injection:\n      - \"Add 5 second latency instead of complete failure\"\n    learning_goals:\n      - \"Do we detect performance degradation?\"\n      - \"What are our latency SLOs?\"\n```\n\n## Post-Game Report Template\n\n```markdown\n# Game Day Report: Database Failover\n\n**Date**: January 15, 2025\n**Participants**: 12\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Executive Summary\n\nConducted database failover game day to test RDS high availability and\napplication resilience. Successfully failed over database in 2.5 minutes\n(target: 2 min). Discovered 3 critical gaps in monitoring and 2 process\nimprovements needed.\n\n## Metrics\n\n| Metric | Target | Actual | Status |\n|--------|--------|--------|--------|\n| Time to Detect | < 30s | 15s | PASS |\n| Time to Respond | < 5min | 4min 20s | PASS |\n| Time to Recover | < 2min | 2min 30s | FAIL |\n| Alert Accuracy | 100% | 66% | FAIL |\n| Zero Customer Impact | Yes | Yes | PASS |\n\n## What Went Well\n\n1. Team responded quickly (4m 20s vs 5m target)\n2. Runbooks were accurate and helpful\n3. Communication was clear and frequent\n4. No customer impact during any scenario\n5. Application auto-reconnect worked perfectly\n\n## What Didn't Go Well\n\n1. Missing alert for failover initiation\n2. Took 30s longer than target to recover\n3. Connection pool exhaustion not detected\n4. Dashboard didn't show replica lag clearly\n5. Escalation contacts list was outdated\n\n## Surprises\n\n1. Cache invalidation cascaded from DB failover (unexpected)\n2. Read replica had 45s replication lag we didn't know about\n3. Application retried too aggressively during failover\n4. Team found a workaround we hadn't documented\n\n## Action Items\n\n| Action | Owner | Due Date | Priority |\n|--------|-------|----------|----------|\n| Add alert for RDS failover events | @sre-team | Jan 20 | P0 |\n| Update dashboard with replica lag | @platform | Jan 22 | P1 |\n| Document cache invalidation behavior | @dev-team | Jan 25 | P1 |\n| Add connection pool monitoring | @sre-team | Jan 27 | P0 |\n| Update escalation contact list | @manager | Jan 18 | P2 |\n| Tune application retry backoff | @dev-team | Feb 1 | P1 |\n\n## Lessons Learned\n\n1. **Monitoring Gaps**: We had blind spots in replica monitoring\n2. **Cascading Effects**: DB changes affect cache in non-obvious ways\n3. **Team Knowledge**: Cross-training is working well\n4. **Documentation**: Runbooks saved time, keep them updated\n\n## Next Game Day\n\n**Proposed Date**: March 15, 2025\n**Scenario**: Multi-region failover\n**Scope**: Production (with safeguards)\n\n## Appendix\n\n- Full timeline spreadsheet: [link]\n- Screen recordings: [link]\n- Metrics dashboard export: [link]\n- Raw observation notes: [link]\n```\n\n## Quick Reference\n\n| Phase | Duration | Key Activities |\n|-------|----------|----------------|\n| Planning | 2 weeks | Define scenarios, invite participants |\n| Pre-game | 30 min | Setup, verify environment, brief team |\n| Execution | 2 hours | Run scenarios, observe, document |\n| Debrief | 30 min | Immediate learnings, quick wins |\n| Post-mortem | 1 week later | Detailed analysis, action items |\n| Follow-up | 1 month | Verify improvements, plan next game day |\n","skills/chaos-engineer/references/infrastructure-chaos.md":"# Infrastructure Chaos Engineering\n\n## Network Latency Injection\n\n```python\n# Using toxiproxy for network chaos\nimport requests\nfrom typing import Literal\n\nclass ToxiproxyClient:\n    def __init__(self, host: str = \"localhost:8474\"):\n        self.base_url = f\"http://{host}\"\n\n    def create_proxy(self, name: str, listen: str, upstream: str):\n        \"\"\"Create proxy to inject failures.\"\"\"\n        response = requests.post(f\"{self.base_url}/proxies\", json={\n            \"name\": name,\n            \"listen\": listen,\n            \"upstream\": upstream,\n            \"enabled\": True\n        })\n        return response.json()\n\n    def add_latency(self, proxy: str, latency_ms: int, jitter_ms: int = 0):\n        \"\"\"Add latency toxic.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"latency\",\n                \"type\": \"latency\",\n                \"attributes\": {\n                    \"latency\": latency_ms,\n                    \"jitter\": jitter_ms\n                }\n            }\n        )\n\n    def add_bandwidth_limit(self, proxy: str, rate_kb: int):\n        \"\"\"Limit bandwidth.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"bandwidth\",\n                \"type\": \"bandwidth\",\n                \"attributes\": {\"rate\": rate_kb}\n            }\n        )\n\n    def add_timeout(self, proxy: str, timeout_ms: int):\n        \"\"\"Add connection timeout.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"timeout\",\n                \"type\": \"timeout\",\n                \"attributes\": {\"timeout\": timeout_ms}\n            }\n        )\n\n# Example usage\ntoxiproxy = ToxiproxyClient()\n\n# Create proxy to database\ntoxiproxy.create_proxy(\n    name=\"postgres\",\n    listen=\"0.0.0.0:5433\",\n    upstream=\"postgres:5432\"\n)\n\n# Inject 200ms latency with 50ms jitter\ntoxiproxy.add_latency(\"postgres\", latency_ms=200, jitter_ms=50)\n```\n\n## AWS Zone Failure Simulation\n\n```python\nimport boto3\nfrom datetime import datetime, timedelta\n\nclass AWSChaosSimulator:\n    def __init__(self, region: str):\n        self.ec2 = boto3.client('ec2', region_name=region)\n        self.asg = boto3.client('autoscaling', region_name=region)\n        self.elb = boto3.client('elbv2', region_name=region)\n\n    def simulate_az_failure(\n        self,\n        availability_zone: str,\n        asg_name: str,\n        duration_minutes: int = 10\n    ):\n        \"\"\"\n        Simulate AZ failure by terminating instances in specific AZ.\n        Auto Scaling Group will launch replacements in other AZs.\n        \"\"\"\n        # Find instances in target AZ\n        instances = self.ec2.describe_instances(Filters=[\n            {'Name': 'tag:aws:autoscaling:groupName', 'Values': [asg_name]},\n            {'Name': 'availability-zone', 'Values': [availability_zone]},\n            {'Name': 'instance-state-name', 'Values': ['running']}\n        ])\n\n        instance_ids = [\n            i['InstanceId']\n            for r in instances['Reservations']\n            for i in r['Instances']\n        ]\n\n        if not instance_ids:\n            return {\"status\": \"no_instances\", \"instances\": []}\n\n        # Suspend AZ-specific scaling activities\n        self.asg.suspend_processes(\n            AutoScalingGroupName=asg_name,\n            ScalingProcesses=['AZRebalance']\n        )\n\n        # Terminate instances to simulate AZ failure\n        self.ec2.terminate_instances(InstanceIds=instance_ids)\n\n        return {\n            \"status\": \"simulated\",\n            \"availability_zone\": availability_zone,\n            \"terminated_instances\": instance_ids,\n            \"recovery_time\": datetime.now() + timedelta(minutes=duration_minutes)\n        }\n\n    def drain_az_from_load_balancer(\n        self,\n        target_group_arn: str,\n        availability_zone: str\n    ):\n        \"\"\"Remove AZ from load balancer to simulate zone failure.\"\"\"\n        # Get current target health\n        health = self.elb.describe_target_health(\n            TargetGroupArn=target_group_arn\n        )\n\n        # Find targets in AZ\n        targets_to_deregister = []\n        for target in health['TargetHealthDescriptions']:\n            # Get instance details\n            instance = self.ec2.describe_instances(\n                InstanceIds=[target['Target']['Id']]\n            )\n            if instance['Reservations'][0]['Instances'][0]['Placement']['AvailabilityZone'] == availability_zone:\n                targets_to_deregister.append(target['Target'])\n\n        # Deregister targets\n        if targets_to_deregister:\n            self.elb.deregister_targets(\n                TargetGroupArn=target_group_arn,\n                Targets=targets_to_deregister\n            )\n\n        return {\n            \"deregistered_targets\": len(targets_to_deregister),\n            \"availability_zone\": availability_zone\n        }\n```\n\n## Server Resource Exhaustion\n\n```bash\n#!/bin/bash\n# CPU stress test using stress-ng\n\n# Install stress-ng\nsudo apt-get install -y stress-ng\n\n# Stress CPU - use 80% of available cores for 5 minutes\nstress-ng --cpu $(nproc --all) --cpu-load 80 --timeout 5m\n\n# Memory stress - consume 70% of available memory\nTOTAL_MEM_MB=$(free -m | awk 'NR==2{print $2}')\nSTRESS_MEM_MB=$((TOTAL_MEM_MB * 70 / 100))\nstress-ng --vm 1 --vm-bytes ${STRESS_MEM_MB}M --timeout 5m\n\n# Disk I/O stress - 4 workers doing sequential writes\nstress-ng --hdd 4 --hdd-bytes 1G --timeout 5m\n\n# Network bandwidth saturation\n# Using iperf3 to saturate network\niperf3 -c target-server -t 300 -P 10  # 10 parallel streams for 5 minutes\n```\n\n## Docker Container Chaos with Pumba\n\n```bash\n#!/bin/bash\n# Pumba - chaos testing for Docker\n\n# Kill random container every 30 seconds\npumba --interval 30s kill --signal SIGKILL \"re2:^myapp\"\n\n# Pause container for 15 seconds, then resume\npumba pause --duration 15s myapp-container\n\n# Add network delay to container\npumba netem \\\n  --duration 5m \\\n  --interface eth0 \\\n  delay \\\n    --time 300 \\\n    --jitter 50 \\\n  myapp-container\n\n# Packet loss - drop 20% of packets\npumba netem \\\n  --duration 5m \\\n  loss \\\n    --percent 20 \\\n  myapp-container\n\n# Limit bandwidth to 1Mbps\npumba netem \\\n  --duration 5m \\\n  rate \\\n    --rate 1mbit \\\n  myapp-container\n\n# Stop all containers matching pattern for 2 minutes\npumba stop --duration 2m \"re2:^production-.*\"\n```\n\n## DNS Failure Simulation\n\n```python\n# Using dnsmasq or editing /etc/hosts for DNS chaos\n\nimport subprocess\nimport time\nfrom contextlib import contextmanager\n\nclass DNSChaos:\n    @staticmethod\n    @contextmanager\n    def block_domain(domain: str, duration_seconds: int = 60):\n        \"\"\"Block DNS resolution for domain by pointing to localhost.\"\"\"\n        try:\n            # Add entry to /etc/hosts\n            subprocess.run([\n                'sudo', 'sh', '-c',\n                f'echo \"127.0.0.1 {domain}\" >> /etc/hosts'\n            ], check=True)\n\n            print(f\"Blocked DNS for {domain}\")\n            yield\n\n        finally:\n            # Wait for duration\n            time.sleep(duration_seconds)\n\n            # Remove entry from /etc/hosts\n            subprocess.run([\n                'sudo', 'sed', '-i',\n                f'/127.0.0.1 {domain}/d',\n                '/etc/hosts'\n            ], check=True)\n\n            print(f\"Restored DNS for {domain}\")\n\n    @staticmethod\n    def add_dns_latency(domain: str, latency_ms: int):\n        \"\"\"Add latency to DNS queries using dnsmasq.\"\"\"\n        config = f\"\"\"\n        # Add to /etc/dnsmasq.conf\n        address=/{domain}/127.0.0.1\n        min-cache-ttl=0\n\n        # Restart dnsmasq with delay\n        \"\"\"\n        return config\n\n# Usage\nwith DNSChaos.block_domain('api.external-service.com', duration_seconds=120):\n    # Run tests while DNS is blocked\n    print(\"DNS blocked - testing fallback behavior\")\n```\n\n## Certificate Expiry Simulation\n\n```python\nfrom datetime import datetime, timedelta\nfrom cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom cryptography.hazmat.primitives import hashes, serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\n\ndef create_expired_certificate(\n    common_name: str,\n    expired_days_ago: int = 1\n) -> tuple[bytes, bytes]:\n    \"\"\"\n    Create an expired TLS certificate for chaos testing.\n    Returns (certificate_pem, private_key_pem)\n    \"\"\"\n    # Generate private key\n    private_key = rsa.generate_private_key(\n        public_exponent=65537,\n        key_size=2048\n    )\n\n    # Certificate valid from 365 days ago to `expired_days_ago` ago\n    not_valid_before = datetime.utcnow() - timedelta(days=365)\n    not_valid_after = datetime.utcnow() - timedelta(days=expired_days_ago)\n\n    subject = issuer = x509.Name([\n        x509.NameAttribute(NameOID.COMMON_NAME, common_name)\n    ])\n\n    cert = x509.CertificateBuilder().subject_name(\n        subject\n    ).issuer_name(\n        issuer\n    ).public_key(\n        private_key.public_key()\n    ).serial_number(\n        x509.random_serial_number()\n    ).not_valid_before(\n        not_valid_before\n    ).not_valid_after(\n        not_valid_after\n    ).sign(private_key, hashes.SHA256())\n\n    # Serialize to PEM\n    cert_pem = cert.public_bytes(serialization.Encoding.PEM)\n    key_pem = private_key.private_bytes(\n        encoding=serialization.Encoding.PEM,\n        format=serialization.PrivateFormat.PKCS8,\n        encryption_algorithm=serialization.NoEncryption()\n    )\n\n    return cert_pem, key_pem\n```\n\n## Quick Reference\n\n| Failure Type | Tool | Command/Method |\n|--------------|------|----------------|\n| Network latency | toxiproxy | `add_latency(proxy, ms)` |\n| Packet loss | toxiproxy/pumba | `loss --percent 20` |\n| AZ failure | AWS API | `simulate_az_failure(az, asg)` |\n| CPU stress | stress-ng | `--cpu N --cpu-load 80` |\n| Memory exhaustion | stress-ng | `--vm 1 --vm-bytes XG` |\n| Container kill | pumba | `kill --signal SIGKILL` |\n| DNS failure | /etc/hosts | Block domain resolution |\n| Cert expiry | cryptography | Generate expired cert |\n","skills/chaos-engineer/references/kubernetes-chaos.md":"# Kubernetes Chaos Engineering\n\n## Litmus Chaos - ChaosEngine\n\n```yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: nginx-chaos\n  namespace: default\nspec:\n  # Application information\n  appinfo:\n    appns: 'default'\n    applabel: 'app=nginx'\n    appkind: 'deployment'\n\n  # Chaos service account\n  chaosServiceAccount: litmus-admin\n\n  # Experiments to run\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            # Total chaos duration\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n\n            # Chaos interval (delete pod every X seconds)\n            - name: CHAOS_INTERVAL\n              value: '10'\n\n            # Force delete pods\n            - name: FORCE\n              value: 'true'\n\n            # Number of pods to delete\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n    - name: pod-network-latency\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: NETWORK_LATENCY\n              value: '2000'  # 2 second latency\n            - name: JITTER\n              value: '200'   # 200ms jitter\n            - name: CONTAINER_RUNTIME\n              value: 'containerd'\n\n    - name: pod-cpu-hog\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: CPU_CORES\n              value: '2'\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n  # Monitor application during chaos\n  monitoring: true\n\n  # Job cleanup policy\n  jobCleanUpPolicy: 'delete'\n```\n\n## Chaos Mesh Experiments\n\n```yaml\n# Network partition between services\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: partition-frontend-backend\n  namespace: chaos-testing\nspec:\n  action: partition\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'frontend'\n  direction: to\n  target:\n    mode: all\n    selector:\n      namespaces:\n        - production\n      labelSelectors:\n        'app': 'backend'\n  duration: '5m'\n\n---\n# Pod failure - kill random pods\napiVersion: chaos-mesh.org/v1alpha1\nkind: PodChaos\nmetadata:\n  name: pod-failure\n  namespace: chaos-testing\nspec:\n  action: pod-failure\n  mode: one  # one, all, fixed, fixed-percent, random-max-percent\n  duration: '30s'\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'payment-service'\n  scheduler:\n    cron: '@every 10m'  # Run every 10 minutes\n\n---\n# Network bandwidth limitation\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: bandwidth-limit\nspec:\n  action: bandwidth\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'tier': 'backend'\n  bandwidth:\n    rate: '1mbps'\n    limit: 20000\n    buffer: 10000\n  duration: '5m'\n\n---\n# Disk I/O stress\napiVersion: chaos-mesh.org/v1alpha1\nkind: IOChaos\nmetadata:\n  name: io-latency\nspec:\n  action: latency\n  mode: one\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'database'\n  volumePath: /var/lib/postgresql/data\n  path: /var/lib/postgresql/data/**/*\n  delay: '100ms'\n  percent: 50  # 50% of I/O operations affected\n  duration: '5m'\n\n---\n# DNS chaos - random DNS errors\napiVersion: chaos-mesh.org/v1alpha1\nkind: DNSChaos\nmetadata:\n  name: dns-random-error\nspec:\n  action: random\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'api-gateway'\n  patterns:\n    - external-api.example.com\n    - *.third-party-service.com\n  duration: '3m'\n```\n\n## Node Drain Simulation\n\n```python\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport time\n\nclass K8sNodeChaos:\n    def __init__(self):\n        config.load_kube_config()\n        self.core_v1 = client.CoreV1Api()\n        self.apps_v1 = client.AppsV1Api()\n\n    def cordon_node(self, node_name: str):\n        \"\"\"Mark node as unschedulable.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": True\n            }\n        }\n        try:\n            self.core_v1.patch_node(node_name, body)\n            print(f\"Node {node_name} cordoned\")\n        except ApiException as e:\n            print(f\"Failed to cordon node: {e}\")\n\n    def drain_node(\n        self,\n        node_name: str,\n        grace_period_seconds: int = 30,\n        delete_local_data: bool = True\n    ):\n        \"\"\"\n        Drain node by evicting all pods.\n        Simulates node failure or maintenance.\n        \"\"\"\n        # First, cordon the node\n        self.cordon_node(node_name)\n\n        # Get all pods on the node\n        field_selector = f\"spec.nodeName={node_name}\"\n        pods = self.core_v1.list_pod_for_all_namespaces(\n            field_selector=field_selector\n        )\n\n        # Evict each pod\n        for pod in pods.items:\n            # Skip DaemonSet pods and mirror pods\n            if pod.metadata.owner_references:\n                for owner in pod.metadata.owner_references:\n                    if owner.kind in ['DaemonSet', 'Node']:\n                        continue\n\n            # Create eviction\n            eviction = client.V1Eviction(\n                metadata=client.V1ObjectMeta(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace\n                ),\n                delete_options=client.V1DeleteOptions(\n                    grace_period_seconds=grace_period_seconds\n                )\n            )\n\n            try:\n                self.core_v1.create_namespaced_pod_eviction(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace,\n                    body=eviction\n                )\n                print(f\"Evicted pod {pod.metadata.name}\")\n            except ApiException as e:\n                if e.status == 429:  # Too many requests\n                    print(f\"Pod {pod.metadata.name} protected by PDB\")\n                else:\n                    print(f\"Failed to evict {pod.metadata.name}: {e}\")\n\n        return {\"node\": node_name, \"status\": \"drained\"}\n\n    def uncordon_node(self, node_name: str):\n        \"\"\"Mark node as schedulable again.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": False\n            }\n        }\n        self.core_v1.patch_node(node_name, body)\n        print(f\"Node {node_name} uncordoned\")\n\n    def simulate_node_failure(\n        self,\n        node_name: str,\n        duration_seconds: int = 300\n    ):\n        \"\"\"\n        Simulate complete node failure.\n        Drain node, wait, then restore.\n        \"\"\"\n        print(f\"Simulating failure of node {node_name}\")\n\n        # Drain the node\n        self.drain_node(node_name)\n\n        # Wait for duration\n        print(f\"Node failed for {duration_seconds} seconds\")\n        time.sleep(duration_seconds)\n\n        # Restore node\n        self.uncordon_node(node_name)\n        print(\"Node restored\")\n```\n\n## Pod Autoscaling Chaos\n\n```yaml\n# Test HPA behavior under load\napiVersion: chaos-mesh.org/v1alpha1\nkind: StressChaos\nmetadata:\n  name: stress-hpa-trigger\nspec:\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'web-server'\n  stressors:\n    cpu:\n      workers: 2\n      load: 80  # 80% CPU load\n  duration: '10m'\n\n---\n# Verify HPA scaling response\napiVersion: v1\nkind: Pod\nmetadata:\n  name: chaos-verification\nspec:\n  containers:\n  - name: verifier\n    image: bitnami/kubectl:latest\n    command:\n      - /bin/bash\n      - -c\n      - |\n        # Monitor HPA scaling\n        while true; do\n          echo \"=== HPA Status ===\"\n          kubectl get hpa web-server -o json | \\\n            jq '.status | {current: .currentReplicas, desired: .desiredReplicas, cpu: .currentCPUUtilizationPercentage}'\n\n          echo \"=== Pod Count ===\"\n          kubectl get pods -l app=web-server --no-headers | wc -l\n\n          sleep 10\n        done\n```\n\n## Custom Resource Chaos\n\n```python\n# Python script to test custom CRD resilience\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport random\nimport time\n\ndef chaos_delete_custom_resources(\n    group: str,\n    version: str,\n    plural: str,\n    namespace: str,\n    percentage: int = 30\n):\n    \"\"\"\n    Randomly delete custom resources to test operator resilience.\n\n    Args:\n        group: API group (e.g., 'app.example.com')\n        version: API version (e.g., 'v1')\n        plural: Resource plural name (e.g., 'myapps')\n        namespace: Namespace to target\n        percentage: Percentage of resources to delete (0-100)\n    \"\"\"\n    config.load_kube_config()\n    custom_api = client.CustomObjectsApi()\n\n    try:\n        # List all custom resources\n        resources = custom_api.list_namespaced_custom_object(\n            group=group,\n            version=version,\n            namespace=namespace,\n            plural=plural\n        )\n\n        items = resources.get('items', [])\n        if not items:\n            print(\"No resources found\")\n            return\n\n        # Calculate number to delete\n        count_to_delete = max(1, int(len(items) * percentage / 100))\n\n        # Randomly select resources\n        to_delete = random.sample(items, count_to_delete)\n\n        print(f\"Deleting {count_to_delete} of {len(items)} {plural}\")\n\n        # Delete selected resources\n        for resource in to_delete:\n            name = resource['metadata']['name']\n            try:\n                custom_api.delete_namespaced_custom_object(\n                    group=group,\n                    version=version,\n                    namespace=namespace,\n                    plural=plural,\n                    name=name,\n                    body=client.V1DeleteOptions()\n                )\n                print(f\"Deleted {plural}/{name}\")\n                time.sleep(1)  # Rate limit deletions\n            except ApiException as e:\n                print(f\"Failed to delete {name}: {e}\")\n\n    except ApiException as e:\n        print(f\"Error listing resources: {e}\")\n\n# Example: Delete 30% of MyApp custom resources\nchaos_delete_custom_resources(\n    group='app.example.com',\n    version='v1',\n    plural='myapps',\n    namespace='production',\n    percentage=30\n)\n```\n\n## Quick Reference\n\n| Chaos Type | Tool | YAML/Command |\n|------------|------|--------------|\n| Pod delete | Litmus | `pod-delete` experiment |\n| Network latency | Chaos Mesh | `NetworkChaos` with action: delay |\n| Node drain | kubectl/API | `kubectl drain <node>` |\n| CPU stress | Chaos Mesh | `StressChaos` with cpu stressor |\n| DNS failure | Chaos Mesh | `DNSChaos` random/error action |\n| I/O latency | Chaos Mesh | `IOChaos` with latency action |\n| Network partition | Chaos Mesh | `NetworkChaos` partition |\n| Pod failure | Chaos Mesh | `PodChaos` pod-failure |\n","skills/cli-developer/SKILL.md":"---\nname: cli-developer\ndescription: Use when building CLI tools, implementing argument parsing, or adding interactive prompts. Invoke for parsing flags and subcommands, displaying progress bars and spinners, generating bash/zsh/fish completion scripts, CLI design, shell completions, and cross-platform terminal applications using commander, click, typer, or cobra.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: devops\n  triggers: CLI, command-line, terminal app, argument parsing, shell completion, interactive prompt, progress bar, commander, click, typer, cobra\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: devops-engineer\n---\n\n# CLI Developer\n\n## Core Workflow\n\n1. **Analyze UX** — Identify user workflows, command hierarchy, common tasks. Validate by listing all commands and their expected `--help` output before writing code.\n2. **Design commands** — Plan subcommands, flags, arguments, configuration. Confirm flag naming is consistent and no existing signatures are broken.\n3. **Implement** — Build with the appropriate CLI framework for the language (see Reference Guide below). After wiring up commands, run `<cli> --help` to verify help text renders correctly and `<cli> --version` to confirm version output.\n4. **Polish** — Add completions, help text, error messages, progress indicators. Verify TTY detection for color output and graceful SIGINT handling.\n5. **Test** — Run cross-platform smoke tests; benchmark startup time (target: <50ms).\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Design Patterns | `references/design-patterns.md` | Subcommands, flags, config, architecture |\n| Node.js CLIs | `references/node-cli.md` | commander, yargs, inquirer, chalk |\n| Python CLIs | `references/python-cli.md` | click, typer, argparse, rich |\n| Go CLIs | `references/go-cli.md` | cobra, viper, bubbletea |\n| UX Patterns | `references/ux-patterns.md` | Progress bars, colors, help text |\n\n## Quick-Start Example\n\n### Node.js (commander)\n\n```js\n#!/usr/bin/env node\n// npm install commander\nconst { program } = require('commander');\n\nprogram\n  .name('mytool')\n  .description('Example CLI')\n  .version('1.0.0');\n\nprogram\n  .command('greet <name>')\n  .description('Greet a user')\n  .option('-l, --loud', 'uppercase the greeting')\n  .action((name, opts) => {\n    const msg = `Hello, ${name}!`;\n    console.log(opts.loud ? msg.toUpperCase() : msg);\n  });\n\nprogram.parse();\n```\n\nFor Python (click/typer) and Go (cobra) quick-start examples, see `references/python-cli.md` and `references/go-cli.md`.\n\n## Constraints\n\n### MUST DO\n- Keep startup time under 50ms\n- Provide clear, actionable error messages\n- Support `--help` and `--version` flags\n- Use consistent flag naming conventions\n- Handle SIGINT (Ctrl+C) gracefully\n- Validate user input early\n- Support both interactive and non-interactive modes\n- Test on Windows, macOS, and Linux\n\n### MUST NOT DO\n\n- **Block on synchronous I/O unnecessarily** — use async reads or stream processing instead.\n- **Print to stdout when output will be piped** — write logs/diagnostics to stderr.\n- **Use colors when output is not a TTY** — detect before applying color:\n  ```js\n  // Node.js\n  const useColor = process.stdout.isTTY;\n  ```\n  ```python\n  # Python\n  import sys\n  use_color = sys.stdout.isatty()\n  ```\n  ```go\n  // Go\n  import \"golang.org/x/term\"\n  useColor := term.IsTerminal(int(os.Stdout.Fd()))\n  ```\n- **Break existing command signatures** — treat flag/subcommand renames as breaking changes.\n- **Require interactive input in CI/CD environments** — always provide non-interactive fallbacks via flags or env vars.\n- **Hardcode paths or platform-specific logic** — use `os.homedir()` / `os.UserHomeDir()` / `Path.home()` instead.\n- **Ship without shell completions** — all three frameworks above have built-in completion generation.\n\n## Output Templates\n\nWhen implementing CLI features, provide:\n1. Command structure (main entry point, subcommands)\n2. Configuration handling (files, env vars, flags)\n3. Core implementation with error handling\n4. Shell completion scripts if applicable\n5. Brief explanation of UX decisions\n\n## Knowledge Reference\n\nCLI frameworks (commander, yargs, oclif, click, typer, argparse, cobra, viper), terminal UI (chalk, inquirer, rich, bubbletea), testing (snapshot testing, E2E), distribution (npm, pip, homebrew, releases), performance optimization\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/devops/cli-developer/)\n","skills/cli-developer/references/design-patterns.md":"# CLI Design Patterns\n\n## Command Hierarchy\n\n```\nmycli                           # Root command\n├── init [options]              # Simple command\n├── config\n│   ├── get <key>              # Nested subcommand\n│   ├── set <key> <value>\n│   └── list\n├── deploy [environment]        # Command with args\n│   ├── --dry-run              # Flag\n│   ├── --force\n│   └── --config <file>        # Option with value\n└── plugins\n    ├── install <name>\n    ├── list\n    └── remove <name>\n```\n\n## Flag Conventions\n\n```bash\n# Boolean flags (presence = true)\nmycli deploy --force --dry-run\n\n# Short + long forms\nmycli -v --verbose\nmycli -c config.yml --config config.yml\n\n# Required vs optional\nmycli deploy <env>              # Positional (required)\nmycli deploy --env production   # Flag (optional)\n\n# Multiple values\nmycli install pkg1 pkg2 pkg3    # Variadic args\nmycli --exclude node_modules --exclude .git\n```\n\n## Configuration Layers\n\nPriority order (highest to lowest):\n\n1. **Command-line flags** - Explicit user intent\n2. **Environment variables** - Runtime context\n3. **Config files (project)** - `.myclirc`, `mycli.config.js`\n4. **Config files (user)** - `~/.myclirc`, `~/.config/mycli/config.yml`\n5. **Config files (system)** - `/etc/mycli/config.yml`\n6. **Defaults** - Hard-coded sensible defaults\n\n```javascript\n// Example config resolution\nconst config = {\n  ...systemDefaults,\n  ...loadSystemConfig(),\n  ...loadUserConfig(),\n  ...loadProjectConfig(),\n  ...loadEnvVars(),\n  ...parseCliFlags(),\n};\n```\n\n## Exit Codes\n\nStandard POSIX exit codes:\n\n```javascript\nconst EXIT_CODES = {\n  SUCCESS: 0,\n  GENERAL_ERROR: 1,\n  MISUSE: 2,              // Invalid arguments\n  PERMISSION_DENIED: 77,\n  NOT_FOUND: 127,\n  SIGINT: 130,            // Ctrl+C\n};\n```\n\n## Plugin Architecture\n\n```\nmycli/\n├── core/                      # Core functionality\n├── plugins/\n│   ├── aws/                  # Plugin: AWS integration\n│   │   ├── package.json\n│   │   └── index.js\n│   └── github/               # Plugin: GitHub integration\n│       ├── package.json\n│       └── index.js\n└── plugin-loader.js          # Discovery & loading\n```\n\nPlugin discovery:\n1. Check `~/.mycli/plugins/`\n2. Check `node_modules/mycli-plugin-*`\n3. Check `MYCLI_PLUGIN_PATH` env var\n\n## Error Handling Patterns\n\n```javascript\n// Good: Actionable error messages\nError: Config file not found at /path/to/config.yml\n\nTried locations:\n  • ./mycli.config.yml\n  • ~/.myclirc\n  • /etc/mycli/config.yml\n\nRun 'mycli init' to create a config file, or use --config to specify location.\n\n// Bad: Unhelpful errors\nError: ENOENT\n```\n\n## Interactive vs Non-Interactive\n\n```javascript\n// Detect if running in CI/CD\nconst isCI = process.env.CI === 'true' || !process.stdout.isTTY;\n\nif (isCI) {\n  // Non-interactive: fail fast with clear errors\n  if (!options.environment) {\n    throw new Error('--environment required in non-interactive mode');\n  }\n} else {\n  // Interactive: prompt user\n  const environment = await prompt({\n    type: 'select',\n    message: 'Select environment:',\n    choices: ['development', 'staging', 'production'],\n  });\n}\n```\n\n## State Management\n\n```\n~/.mycli/\n├── config.yml           # User configuration\n├── cache/               # Cached data\n│   ├── plugins.json\n│   └── api-responses/\n├── credentials.json     # Sensitive data (600 perms)\n└── state.json          # Session state\n```\n\n## Performance Patterns\n\n```javascript\n// Lazy loading: Don't load unused dependencies\nif (command === 'deploy') {\n  const deploy = require('./commands/deploy'); // Load on demand\n  await deploy.run();\n}\n\n// Caching: Avoid repeated API calls\nconst cache = new Cache('~/.mycli/cache', { ttl: 3600 });\nlet plugins = await cache.get('plugins');\nif (!plugins) {\n  plugins = await fetchPlugins();\n  await cache.set('plugins', plugins);\n}\n\n// Async operations: Don't block unnecessarily\nawait Promise.all([\n  validateConfig(),\n  checkForUpdates(),\n  loadPlugins(),\n]);\n```\n\n## Versioning & Updates\n\n```javascript\n// Check for updates (non-blocking)\ncheckForUpdates().then(update => {\n  if (update.available) {\n    console.log(`Update available: ${update.version}`);\n    console.log(`Run: npm install -g mycli@latest`);\n  }\n}).catch(() => {\n  // Silently fail - don't interrupt user workflow\n});\n\n// Version compatibility\nconst MIN_NODE_VERSION = '18.0.0';\nif (!semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`)) {\n  console.error(`mycli requires Node.js ${MIN_NODE_VERSION} or higher`);\n  process.exit(1);\n}\n```\n\n## Help Text Design\n\n```\nUSAGE\n  mycli deploy [environment] [options]\n\nARGUMENTS\n  environment  Target environment (development|staging|production)\n\nOPTIONS\n  -c, --config <file>  Path to config file\n  -f, --force          Skip confirmation prompts\n  -d, --dry-run        Preview changes without executing\n  -v, --verbose        Show detailed output\n\nEXAMPLES\n  # Deploy to production\n  mycli deploy production\n\n  # Preview staging deployment\n  mycli deploy staging --dry-run\n\n  # Use custom config\n  mycli deploy --config ./custom.yml\n\nLearn more: https://docs.mycli.dev/deploy\n```\n","skills/cli-developer/references/go-cli.md":"# Go CLI Development\n\n## Cobra (Recommended)\n\nPowerful CLI framework used by kubectl, hugo, docker.\n\n```go\n// cmd/root.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"os\"\n    \"github.com/spf13/cobra\"\n    \"github.com/spf13/viper\"\n)\n\nvar (\n    cfgFile string\n    verbose bool\n)\n\nvar rootCmd = &cobra.Command{\n    Use:   \"mycli\",\n    Short: \"My awesome CLI tool\",\n    Long: `A longer description of your CLI application`,\n    Version: \"1.0.0\",\n}\n\nfunc Execute() {\n    if err := rootCmd.Execute(); err != nil {\n        fmt.Fprintln(os.Stderr, err)\n        os.Exit(1)\n    }\n}\n\nfunc init() {\n    cobra.OnInitialize(initConfig)\n\n    rootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file\")\n    rootCmd.PersistentFlags().BoolVarP(&verbose, \"verbose\", \"v\", false, \"verbose output\")\n\n    viper.BindPFlag(\"verbose\", rootCmd.PersistentFlags().Lookup(\"verbose\"))\n}\n\nfunc initConfig() {\n    if cfgFile != \"\" {\n        viper.SetConfigFile(cfgFile)\n    } else {\n        home, err := os.UserHomeDir()\n        cobra.CheckErr(err)\n\n        viper.AddConfigPath(home)\n        viper.AddConfigPath(\".\")\n        viper.SetConfigType(\"yaml\")\n        viper.SetConfigName(\".mycli\")\n    }\n\n    viper.AutomaticEnv()\n\n    if err := viper.ReadInConfig(); err == nil {\n        fmt.Fprintln(os.Stderr, \"Using config file:\", viper.ConfigFileUsed())\n    }\n}\n\n// cmd/init.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/cobra\"\n)\n\nvar (\n    template string\n    force    bool\n)\n\nvar initCmd = &cobra.Command{\n    Use:   \"init [name]\",\n    Short: \"Initialize a new project\",\n    Args:  cobra.ExactArgs(1),\n    RunE: func(cmd *cobra.Command, args []string) error {\n        name := args[0]\n        return initProject(name, template, force)\n    },\n}\n\nfunc init() {\n    rootCmd.AddCommand(initCmd)\n\n    initCmd.Flags().StringVarP(&template, \"template\", \"t\", \"default\", \"Project template\")\n    initCmd.Flags().BoolVarP(&force, \"force\", \"f\", false, \"Overwrite existing\")\n}\n\nfunc initProject(name, template string, force bool) error {\n    fmt.Printf(\"Creating %s from %s\\n\", name, template)\n    return nil\n}\n\n// cmd/deploy.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/cobra\"\n)\n\nvar (\n    dryRun bool\n)\n\nvar deployCmd = &cobra.Command{\n    Use:   \"deploy [environment]\",\n    Short: \"Deploy to environment\",\n    Args:  cobra.ExactArgs(1),\n    ValidArgs: []string{\"dev\", \"staging\", \"prod\"},\n    RunE: func(cmd *cobra.Command, args []string) error {\n        env := args[0]\n        return deploy(env, dryRun)\n    },\n}\n\nfunc init() {\n    rootCmd.AddCommand(deployCmd)\n    deployCmd.Flags().BoolVar(&dryRun, \"dry-run\", false, \"Preview only\")\n}\n\nfunc deploy(env string, dryRun bool) error {\n    if dryRun {\n        fmt.Printf(\"Would deploy to: %s\\n\", env)\n    } else {\n        fmt.Printf(\"Deploying to %s...\\n\", env)\n    }\n    return nil\n}\n\n// main.go\npackage main\n\nimport \"mycli/cmd\"\n\nfunc main() {\n    cmd.Execute()\n}\n```\n\n## Viper (Configuration)\n\nConfiguration management with multiple sources.\n\n```go\npackage config\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/viper\"\n)\n\ntype Config struct {\n    Environment string `mapstructure:\"environment\"`\n    Timeout     int    `mapstructure:\"timeout\"`\n    Verbose     bool   `mapstructure:\"verbose\"`\n    API         APIConfig `mapstructure:\"api\"`\n}\n\ntype APIConfig struct {\n    Endpoint string `mapstructure:\"endpoint\"`\n    Token    string `mapstructure:\"token\"`\n}\n\nfunc Load() (*Config, error) {\n    // Set defaults\n    viper.SetDefault(\"environment\", \"development\")\n    viper.SetDefault(\"timeout\", 30)\n    viper.SetDefault(\"verbose\", false)\n\n    // Config file locations\n    viper.SetConfigName(\"config\")\n    viper.SetConfigType(\"yaml\")\n    viper.AddConfigPath(\"/etc/mycli/\")\n    viper.AddConfigPath(\"$HOME/.config/mycli\")\n    viper.AddConfigPath(\".\")\n\n    // Environment variables\n    viper.SetEnvPrefix(\"MYCLI\")\n    viper.AutomaticEnv()\n\n    // Read config\n    if err := viper.ReadInConfig(); err != nil {\n        if _, ok := err.(viper.ConfigFileNotFoundError); !ok {\n            return nil, fmt.Errorf(\"failed to read config: %w\", err)\n        }\n    }\n\n    // Unmarshal into struct\n    var cfg Config\n    if err := viper.Unmarshal(&cfg); err != nil {\n        return nil, fmt.Errorf(\"failed to unmarshal config: %w\", err)\n    }\n\n    return &cfg, nil\n}\n```\n\n## Bubble Tea (Interactive TUI)\n\nModern terminal UI framework for interactive CLIs.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n\n    tea \"github.com/charmbracelet/bubbletea\"\n    \"github.com/charmbracelet/lipgloss\"\n)\n\n// Model\ntype model struct {\n    choices  []string\n    cursor   int\n    selected map[int]struct{}\n}\n\nfunc initialModel() model {\n    return model{\n        choices:  []string{\"TypeScript\", \"ESLint\", \"Prettier\", \"Jest\"},\n        selected: make(map[int]struct{}),\n    }\n}\n\n// Init\nfunc (m model) Init() tea.Cmd {\n    return nil\n}\n\n// Update\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n    case tea.KeyMsg:\n        switch msg.String() {\n        case \"ctrl+c\", \"q\":\n            return m, tea.Quit\n\n        case \"up\", \"k\":\n            if m.cursor > 0 {\n                m.cursor--\n            }\n\n        case \"down\", \"j\":\n            if m.cursor < len(m.choices)-1 {\n                m.cursor++\n            }\n\n        case \" \":\n            _, ok := m.selected[m.cursor]\n            if ok {\n                delete(m.selected, m.cursor)\n            } else {\n                m.selected[m.cursor] = struct{}{}\n            }\n\n        case \"enter\":\n            return m, tea.Quit\n        }\n    }\n\n    return m, nil\n}\n\n// View\nfunc (m model) View() string {\n    s := \"Select features:\\n\\n\"\n\n    for i, choice := range m.choices {\n        cursor := \" \"\n        if m.cursor == i {\n            cursor = \">\"\n        }\n\n        checked := \" \"\n        if _, ok := m.selected[i]; ok {\n            checked = \"x\"\n        }\n\n        s += fmt.Sprintf(\"%s [%s] %s\\n\", cursor, checked, choice)\n    }\n\n    s += \"\\nPress space to select, enter to confirm, q to quit.\\n\"\n\n    return s\n}\n\nfunc main() {\n    p := tea.NewProgram(initialModel())\n    if _, err := p.Run(); err != nil {\n        fmt.Printf(\"Error: %v\", err)\n        os.Exit(1)\n    }\n}\n```\n\n## Progress Indicators\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/schollz/progressbar/v3\"\n)\n\nfunc main() {\n    // Simple progress bar\n    bar := progressbar.Default(100, \"Downloading\")\n    for i := 0; i < 100; i++ {\n        bar.Add(1)\n        time.Sleep(40 * time.Millisecond)\n    }\n\n    // Custom progress bar\n    bar = progressbar.NewOptions(100,\n        progressbar.OptionEnableColorCodes(true),\n        progressbar.OptionShowBytes(true),\n        progressbar.OptionSetWidth(15),\n        progressbar.OptionSetDescription(\"[cyan][1/3][reset] Downloading...\"),\n        progressbar.OptionSetTheme(progressbar.Theme{\n            Saucer:        \"[green]=[reset]\",\n            SaucerHead:    \"[green]>[reset]\",\n            SaucerPadding: \" \",\n            BarStart:      \"[\",\n            BarEnd:        \"]\",\n        }),\n    )\n\n    for i := 0; i < 100; i++ {\n        bar.Add(1)\n        time.Sleep(40 * time.Millisecond)\n    }\n}\n```\n\n## Spinner\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/briandowns/spinner\"\n)\n\nfunc main() {\n    s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)\n    s.Suffix = \" Installing dependencies...\"\n    s.Start()\n\n    time.Sleep(4 * time.Second)\n\n    s.UpdateCharSet(spinner.CharSets[9])\n    s.Suffix = \" Processing...\"\n    time.Sleep(2 * time.Second)\n\n    s.Stop()\n    fmt.Println(\"✓ Done!\")\n}\n```\n\n## Colored Output\n\n```go\npackage main\n\nimport (\n    \"github.com/fatih/color\"\n)\n\nfunc main() {\n    // Basic colors\n    color.Blue(\"Info: Starting deployment...\")\n    color.Green(\"Success: Deployment complete!\")\n    color.Yellow(\"Warning: Deprecated flag used\")\n    color.Red(\"Error: Deployment failed\")\n\n    // Custom styles\n    success := color.New(color.FgGreen, color.Bold).PrintlnFunc()\n    error := color.New(color.FgRed, color.Bold).PrintlnFunc()\n\n    success(\"✓ Build successful\")\n    error(\"✗ Build failed\")\n\n    // Printf-style\n    color.Cyan(\"Processing %d files...\\n\", 42)\n\n    // Disable colors for CI\n    if os.Getenv(\"CI\") != \"\" {\n        color.NoColor = true\n    }\n}\n```\n\n## Error Handling\n\n```go\npackage main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"os\"\n    \"syscall\"\n\n    \"github.com/spf13/cobra\"\n)\n\nvar deployCmd = &cobra.Command{\n    Use:   \"deploy\",\n    Short: \"Deploy application\",\n    RunE: func(cmd *cobra.Command, args []string) error {\n        if err := deploy(); err != nil {\n            return handleError(err)\n        }\n        return nil\n    },\n}\n\nfunc handleError(err error) error {\n    var exitCode int\n\n    switch {\n    case errors.Is(err, os.ErrPermission):\n        fmt.Fprintln(os.Stderr, \"Permission denied\")\n        fmt.Fprintln(os.Stderr, \"Try running with sudo or check file permissions\")\n        exitCode = 77\n\n    case errors.Is(err, os.ErrNotExist):\n        fmt.Fprintf(os.Stderr, \"File not found: %v\\n\", err)\n        exitCode = 127\n\n    default:\n        fmt.Fprintf(os.Stderr, \"Deployment failed: %v\\n\", err)\n        if os.Getenv(\"DEBUG\") != \"\" {\n            fmt.Fprintf(os.Stderr, \"%+v\\n\", err)\n        }\n        exitCode = 1\n    }\n\n    os.Exit(exitCode)\n    return nil\n}\n\n// Handle SIGINT (Ctrl+C)\nfunc main() {\n    // Setup signal handling\n    c := make(chan os.Signal, 1)\n    signal.Notify(c, os.Interrupt, syscall.SIGTERM)\n\n    go func() {\n        <-c\n        fmt.Println(\"\\nOperation cancelled\")\n        os.Exit(130)\n    }()\n\n    cmd.Execute()\n}\n```\n\n## Testing\n\n```go\npackage cmd\n\nimport (\n    \"bytes\"\n    \"testing\"\n\n    \"github.com/spf13/cobra\"\n    \"github.com/stretchr/testify/assert\"\n)\n\nfunc TestInitCommand(t *testing.T) {\n    cmd := &cobra.Command{Use: \"test\"}\n    cmd.AddCommand(initCmd)\n\n    b := bytes.NewBufferString(\"\")\n    cmd.SetOut(b)\n    cmd.SetArgs([]string{\"init\", \"my-project\"})\n\n    err := cmd.Execute()\n    assert.NoError(t, err)\n    assert.Contains(t, b.String(), \"Creating my-project\")\n}\n\nfunc TestInitWithTemplate(t *testing.T) {\n    cmd := &cobra.Command{Use: \"test\"}\n    cmd.AddCommand(initCmd)\n\n    b := bytes.NewBufferString(\"\")\n    cmd.SetOut(b)\n    cmd.SetArgs([]string{\"init\", \"my-project\", \"--template\", \"react\"})\n\n    err := cmd.Execute()\n    assert.NoError(t, err)\n    assert.Contains(t, b.String(), \"react\")\n}\n```\n\n## Build & Distribution\n\n```makefile\n# Makefile\nVERSION := $(shell git describe --tags --always --dirty)\nLDFLAGS := -ldflags \"-X main.version=$(VERSION)\"\n\n.PHONY: build\nbuild:\n\tgo build $(LDFLAGS) -o bin/mycli main.go\n\n.PHONY: install\ninstall:\n\tgo install $(LDFLAGS)\n\n.PHONY: test\ntest:\n\tgo test -v ./...\n\n.PHONY: release\nrelease:\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-linux-amd64\n\tGOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-darwin-amd64\n\tGOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/mycli-darwin-arm64\n\tGOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-windows-amd64.exe\n```\n","skills/cli-developer/references/node-cli.md":"# Node.js CLI Development\n\n## Commander.js (Recommended)\n\nModern, elegant CLI framework with TypeScript support.\n\n```javascript\n#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { version } from './package.json';\n\nconst program = new Command();\n\nprogram\n  .name('mycli')\n  .description('My awesome CLI tool')\n  .version(version);\n\n// Simple command\nprogram\n  .command('init')\n  .description('Initialize a new project')\n  .option('-t, --template <type>', 'Project template', 'default')\n  .option('-f, --force', 'Overwrite existing files')\n  .action(async (options) => {\n    console.log(`Initializing with template: ${options.template}`);\n  });\n\n// Command with arguments\nprogram\n  .command('deploy <environment>')\n  .description('Deploy to environment')\n  .option('--dry-run', 'Preview without executing')\n  .action(async (environment, options) => {\n    if (options.dryRun) {\n      console.log(`Would deploy to: ${environment}`);\n    } else {\n      await deploy(environment);\n    }\n  });\n\n// Nested subcommands\nconst config = program.command('config').description('Manage configuration');\n\nconfig\n  .command('get <key>')\n  .description('Get config value')\n  .action((key) => console.log(getConfig(key)));\n\nconfig\n  .command('set <key> <value>')\n  .description('Set config value')\n  .action((key, value) => setConfig(key, value));\n\nprogram.parse();\n```\n\n## Yargs (Alternative)\n\nPowerful argument parsing with middleware support.\n\n```javascript\n#!/usr/bin/env node\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\n\nyargs(hideBin(process.argv))\n  .command(\n    'deploy <env>',\n    'Deploy to environment',\n    (yargs) => {\n      return yargs\n        .positional('env', {\n          describe: 'Environment name',\n          choices: ['dev', 'staging', 'prod'],\n        })\n        .option('force', {\n          alias: 'f',\n          type: 'boolean',\n          description: 'Force deployment',\n        });\n    },\n    async (argv) => {\n      await deploy(argv.env, { force: argv.force });\n    }\n  )\n  .middleware([(argv) => {\n    // Validate before all commands\n    if (!isConfigValid()) {\n      throw new Error('Invalid config');\n    }\n  }])\n  .demandCommand()\n  .help()\n  .parse();\n```\n\n## Interactive Prompts (Inquirer)\n\nBeautiful interactive prompts for user input.\n\n```javascript\nimport inquirer from 'inquirer';\n\n// Text input\nconst { name } = await inquirer.prompt([\n  {\n    type: 'input',\n    name: 'name',\n    message: 'Project name:',\n    default: 'my-project',\n    validate: (input) => input.length > 0 || 'Name required',\n  },\n]);\n\n// Select from list\nconst { environment } = await inquirer.prompt([\n  {\n    type: 'list',\n    name: 'environment',\n    message: 'Select environment:',\n    choices: ['development', 'staging', 'production'],\n    default: 'development',\n  },\n]);\n\n// Checkbox (multi-select)\nconst { features } = await inquirer.prompt([\n  {\n    type: 'checkbox',\n    name: 'features',\n    message: 'Select features:',\n    choices: [\n      { name: 'TypeScript', checked: true },\n      { name: 'ESLint', checked: true },\n      { name: 'Prettier', checked: true },\n      { name: 'Jest', checked: false },\n    ],\n  },\n]);\n\n// Confirmation\nconst { confirmed } = await inquirer.prompt([\n  {\n    type: 'confirm',\n    name: 'confirmed',\n    message: 'Deploy to production?',\n    default: false,\n  },\n]);\n\n// Password\nconst { password } = await inquirer.prompt([\n  {\n    type: 'password',\n    name: 'password',\n    message: 'Enter password:',\n    mask: '*',\n  },\n]);\n```\n\n## Terminal Output (Chalk)\n\nColorful terminal output with proper TTY detection.\n\n```javascript\nimport chalk from 'chalk';\n\n// Basic colors\nconsole.log(chalk.blue('Info: ') + 'Starting deployment...');\nconsole.log(chalk.green('Success: ') + 'Deployment complete');\nconsole.log(chalk.yellow('Warning: ') + 'Deprecated flag used');\nconsole.log(chalk.red('Error: ') + 'Deployment failed');\n\n// Styles\nconsole.log(chalk.bold.underline('Important'));\nconsole.log(chalk.dim('Less important'));\n\n// Templates\nconst success = chalk.green.bold;\nconst error = chalk.red.bold;\nconsole.log(success('✓') + ' Build successful');\nconsole.log(error('✗') + ' Build failed');\n\n// Disable colors for CI\nconst log = {\n  info: (msg) => console.log(chalk.blue('ℹ'), msg),\n  success: (msg) => console.log(chalk.green('✔'), msg),\n  warn: (msg) => console.log(chalk.yellow('⚠'), msg),\n  error: (msg) => console.log(chalk.red('✖'), msg),\n};\n\n// Auto-detects TTY and CI environments\n```\n\n## Progress Indicators (Ora)\n\nElegant terminal spinners and progress indicators.\n\n```javascript\nimport ora from 'ora';\n\n// Simple spinner\nconst spinner = ora('Loading...').start();\nawait doWork();\nspinner.succeed('Done!');\n\n// Update text\nconst spinner = ora('Starting...').start();\nspinner.text = 'Processing...';\nawait process();\nspinner.text = 'Finalizing...';\nawait finalize();\nspinner.succeed('Complete!');\n\n// Different states\nspinner.start('Installing dependencies...');\n// ... work\nspinner.succeed('Dependencies installed');\n// or\nspinner.fail('Installation failed');\n// or\nspinner.warn('Some packages skipped');\n// or\nspinner.info('Using cached packages');\n\n// Multiple spinners\nconst spinners = {\n  api: ora('Deploying API...').start(),\n  web: ora('Deploying web app...').start(),\n  db: ora('Running migrations...').start(),\n};\n\nawait Promise.all([\n  deployApi().then(() => spinners.api.succeed()),\n  deployWeb().then(() => spinners.web.succeed()),\n  runMigrations().then(() => spinners.db.succeed()),\n]);\n```\n\n## Progress Bars (cli-progress)\n\n```javascript\nimport cliProgress from 'cli-progress';\n\n// Single progress bar\nconst bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\nbar.start(100, 0);\n\nfor (let i = 0; i <= 100; i++) {\n  await processItem(i);\n  bar.update(i);\n}\n\nbar.stop();\n\n// Multi-progress\nconst multibar = new cliProgress.MultiBar({\n  clearOnComplete: false,\n  hideCursor: true,\n});\n\nconst bar1 = multibar.create(100, 0, { task: 'API' });\nconst bar2 = multibar.create(100, 0, { task: 'Web' });\n\nawait Promise.all([\n  processApi(bar1),\n  processWeb(bar2),\n]);\n\nmultibar.stop();\n```\n\n## File System Helpers\n\n```javascript\nimport fs from 'fs-extra';\nimport { globby } from 'globby';\nimport path from 'path';\n\n// Copy with template\nawait fs.copy('templates/app', targetDir, {\n  filter: (src) => !src.includes('node_modules'),\n});\n\n// Read/write JSON\nconst config = await fs.readJson('config.json');\nawait fs.writeJson('output.json', data, { spaces: 2 });\n\n// Ensure directory exists\nawait fs.ensureDir('dist/assets');\n\n// Find files\nconst files = await globby(['src/**/*.ts', '!src/**/*.test.ts']);\n```\n\n## Error Handling\n\n```javascript\nimport { Command } from 'commander';\n\nprogram\n  .command('deploy')\n  .action(async () => {\n    try {\n      await deploy();\n    } catch (error) {\n      if (error.code === 'EACCES') {\n        console.error(chalk.red('Permission denied'));\n        console.error('Try running with sudo or check file permissions');\n        process.exit(77);\n      } else if (error.code === 'ENOENT') {\n        console.error(chalk.red('File not found:'), error.path);\n        process.exit(127);\n      } else {\n        console.error(chalk.red('Deployment failed:'), error.message);\n        if (process.env.DEBUG) {\n          console.error(error.stack);\n        }\n        process.exit(1);\n      }\n    }\n  });\n\n// Handle SIGINT (Ctrl+C)\nprocess.on('SIGINT', () => {\n  console.log('\\nOperation cancelled');\n  process.exit(130);\n});\n```\n\n## Package.json Setup\n\n```json\n{\n  \"name\": \"mycli\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"bin\": {\n    \"mycli\": \"./bin/cli.js\"\n  },\n  \"files\": [\n    \"bin/\",\n    \"lib/\",\n    \"templates/\"\n  ],\n  \"engines\": {\n    \"node\": \">=18.0.0\"\n  },\n  \"dependencies\": {\n    \"commander\": \"^11.0.0\",\n    \"inquirer\": \"^9.0.0\",\n    \"chalk\": \"^5.0.0\",\n    \"ora\": \"^7.0.0\"\n  }\n}\n```\n\n## Testing CLIs\n\n```javascript\nimport { execaCommand } from 'execa';\nimport { describe, it, expect } from 'vitest';\n\ndescribe('mycli', () => {\n  it('shows version', async () => {\n    const { stdout } = await execaCommand('node bin/cli.js --version');\n    expect(stdout).toMatch(/\\d+\\.\\d+\\.\\d+/);\n  });\n\n  it('shows help', async () => {\n    const { stdout } = await execaCommand('node bin/cli.js --help');\n    expect(stdout).toContain('Usage:');\n  });\n\n  it('handles invalid command', async () => {\n    await expect(\n      execaCommand('node bin/cli.js invalid')\n    ).rejects.toThrow();\n  });\n});\n```\n","skills/cli-developer/references/python-cli.md":"# Python CLI Development\n\n## Typer (Recommended - Modern)\n\nFastAPI-style CLI framework with automatic help generation.\n\n```python\n#!/usr/bin/env python3\nimport typer\nfrom typing import Optional\nfrom enum import Enum\n\napp = typer.Typer()\n\nclass Environment(str, Enum):\n    dev = \"development\"\n    staging = \"staging\"\n    prod = \"production\"\n\n@app.command()\ndef init(\n    name: str = typer.Argument(..., help=\"Project name\"),\n    template: str = typer.Option(\"default\", help=\"Project template\"),\n    force: bool = typer.Option(False, \"--force\", \"-f\", help=\"Overwrite existing\"),\n):\n    \"\"\"Initialize a new project\"\"\"\n    typer.echo(f\"Creating {name} from {template}\")\n    if force:\n        typer.echo(\"Force mode enabled\")\n\n@app.command()\ndef deploy(\n    environment: Environment = typer.Argument(..., help=\"Target environment\"),\n    dry_run: bool = typer.Option(False, \"--dry-run\", help=\"Preview only\"),\n    config: Optional[typer.FileText] = typer.Option(None, help=\"Config file\"),\n):\n    \"\"\"Deploy to environment\"\"\"\n    if dry_run:\n        typer.echo(f\"Would deploy to: {environment.value}\")\n    else:\n        typer.echo(f\"Deploying to {environment.value}...\")\n\n# Nested commands\nconfig_app = typer.Typer()\napp.add_typer(config_app, name=\"config\", help=\"Manage configuration\")\n\n@config_app.command(\"get\")\ndef config_get(key: str):\n    \"\"\"Get config value\"\"\"\n    typer.echo(f\"Value: {get_config(key)}\")\n\n@config_app.command(\"set\")\ndef config_set(key: str, value: str):\n    \"\"\"Set config value\"\"\"\n    set_config(key, value)\n    typer.echo(f\"Set {key} = {value}\")\n\nif __name__ == \"__main__\":\n    app()\n```\n\n## Click (Widely Used)\n\nPowerful, composable CLI framework.\n\n```python\nimport click\n\n@click.group()\n@click.version_option()\ndef cli():\n    \"\"\"My awesome CLI tool\"\"\"\n    pass\n\n@cli.command()\n@click.argument('name')\n@click.option('--template', default='default', help='Project template')\n@click.option('--force', '-f', is_flag=True, help='Overwrite existing')\ndef init(name, template, force):\n    \"\"\"Initialize a new project\"\"\"\n    click.echo(f\"Creating {name} from {template}\")\n\n@cli.command()\n@click.argument('environment', type=click.Choice(['dev', 'staging', 'prod']))\n@click.option('--dry-run', is_flag=True, help='Preview only')\n@click.option('--config', type=click.File('r'), help='Config file')\ndef deploy(environment, dry_run, config):\n    \"\"\"Deploy to environment\"\"\"\n    if dry_run:\n        click.secho(f\"Would deploy to: {environment}\", fg='yellow')\n    else:\n        click.secho(f\"Deploying to {environment}...\", fg='green')\n\n# Nested groups\n@cli.group()\ndef config():\n    \"\"\"Manage configuration\"\"\"\n    pass\n\n@config.command('get')\n@click.argument('key')\ndef config_get(key):\n    \"\"\"Get config value\"\"\"\n    click.echo(get_config(key))\n\n@config.command('set')\n@click.argument('key')\n@click.argument('value')\ndef config_set(key, value):\n    \"\"\"Set config value\"\"\"\n    set_config(key, value)\n\nif __name__ == '__main__':\n    cli()\n```\n\n## Rich Terminal Output\n\nBeautiful terminal formatting and progress indicators.\n\n```python\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.progress import Progress, SpinnerColumn, TextColumn\nfrom rich.panel import Panel\nfrom rich.syntax import Syntax\nfrom rich import print as rprint\n\nconsole = Console()\n\n# Styled output\nconsole.print(\"[bold blue]Info:[/] Starting deployment...\")\nconsole.print(\"[bold green]Success:[/] Deployment complete!\")\nconsole.print(\"[bold yellow]Warning:[/] Deprecated flag used\")\nconsole.print(\"[bold red]Error:[/] Deployment failed\")\n\n# Tables\ntable = Table(title=\"Deployments\")\ntable.add_column(\"Environment\", style=\"cyan\")\ntable.add_column(\"Status\", style=\"magenta\")\ntable.add_column(\"Time\", style=\"green\")\n\ntable.add_row(\"Production\", \"✓ Success\", \"2m 34s\")\ntable.add_row(\"Staging\", \"✗ Failed\", \"1m 12s\")\nconsole.print(table)\n\n# Panels\nconsole.print(Panel.fit(\n    \"Deploy to production?\",\n    title=\"Confirmation\",\n    border_style=\"red\"\n))\n\n# Syntax highlighting\ncode = '''\ndef deploy(env: str):\n    print(f\"Deploying to {env}\")\n'''\nconsole.print(Syntax(code, \"python\", theme=\"monokai\"))\n\n# Progress bars\nwith Progress() as progress:\n    task = progress.add_task(\"[cyan]Deploying...\", total=100)\n    for i in range(100):\n        do_work()\n        progress.update(task, advance=1)\n\n# Spinners\nwith Progress(\n    SpinnerColumn(),\n    TextColumn(\"[progress.description]{task.description}\"),\n) as progress:\n    task = progress.add_task(\"Installing dependencies...\")\n    install_dependencies()\n```\n\n## Interactive Prompts (questionary)\n\n```python\nimport questionary\n\n# Text input\nname = questionary.text(\n    \"Project name:\",\n    default=\"my-project\",\n    validate=lambda x: len(x) > 0 or \"Name required\"\n).ask()\n\n# Select from list\nenvironment = questionary.select(\n    \"Select environment:\",\n    choices=[\"development\", \"staging\", \"production\"],\n    default=\"development\"\n).ask()\n\n# Checkbox (multi-select)\nfeatures = questionary.checkbox(\n    \"Select features:\",\n    choices=[\n        questionary.Choice(\"TypeScript\", checked=True),\n        questionary.Choice(\"ESLint\", checked=True),\n        questionary.Choice(\"Prettier\", checked=True),\n        questionary.Choice(\"Jest\", checked=False),\n    ]\n).ask()\n\n# Confirmation\nconfirmed = questionary.confirm(\n    \"Deploy to production?\",\n    default=False\n).ask()\n\nif confirmed:\n    deploy()\n\n# Password\npassword = questionary.password(\"Enter password:\").ask()\n```\n\n## Argparse (Standard Library)\n\nBuilt-in argument parsing (verbose but no dependencies).\n\n```python\nimport argparse\nimport sys\n\ndef main():\n    parser = argparse.ArgumentParser(\n        prog='mycli',\n        description='My awesome CLI tool',\n    )\n    parser.add_argument('--version', action='version', version='1.0.0')\n\n    subparsers = parser.add_subparsers(dest='command', required=True)\n\n    # Init command\n    init_parser = subparsers.add_parser('init', help='Initialize project')\n    init_parser.add_argument('name', help='Project name')\n    init_parser.add_argument('--template', default='default', help='Template')\n    init_parser.add_argument('-f', '--force', action='store_true')\n\n    # Deploy command\n    deploy_parser = subparsers.add_parser('deploy', help='Deploy')\n    deploy_parser.add_argument(\n        'environment',\n        choices=['dev', 'staging', 'prod'],\n        help='Target environment'\n    )\n    deploy_parser.add_argument('--dry-run', action='store_true')\n    deploy_parser.add_argument('--config', type=argparse.FileType('r'))\n\n    args = parser.parse_args()\n\n    if args.command == 'init':\n        init(args.name, args.template, args.force)\n    elif args.command == 'deploy':\n        deploy(args.environment, args.dry_run, args.config)\n\nif __name__ == '__main__':\n    main()\n```\n\n## Error Handling\n\n```python\nimport typer\nimport sys\nfrom pathlib import Path\n\napp = typer.Typer()\n\n@app.command()\ndef deploy():\n    try:\n        perform_deploy()\n    except PermissionError as e:\n        typer.secho(\"Permission denied\", fg=typer.colors.RED, err=True)\n        typer.echo(\"Try running with sudo or check file permissions\")\n        raise typer.Exit(code=77)\n    except FileNotFoundError as e:\n        typer.secho(f\"File not found: {e.filename}\", fg=typer.colors.RED, err=True)\n        raise typer.Exit(code=127)\n    except Exception as e:\n        typer.secho(f\"Deployment failed: {e}\", fg=typer.colors.RED, err=True)\n        if os.getenv('DEBUG'):\n            import traceback\n            traceback.print_exc()\n        raise typer.Exit(code=1)\n\n# Handle KeyboardInterrupt (Ctrl+C)\ndef main():\n    try:\n        app()\n    except KeyboardInterrupt:\n        typer.echo(\"\\nOperation cancelled\")\n        sys.exit(130)\n\nif __name__ == \"__main__\":\n    main()\n```\n\n## Configuration Management\n\n```python\nfrom pathlib import Path\nfrom typing import Any\nimport json\nimport os\n\nclass Config:\n    def __init__(self):\n        self.config_paths = [\n            Path(\"/etc/mycli/config.json\"),          # System\n            Path.home() / \".config\" / \"mycli\" / \"config.json\",  # User\n            Path.cwd() / \"mycli.json\",               # Project\n        ]\n\n    def load(self) -> dict[str, Any]:\n        config = self._defaults()\n\n        # Load from files (lowest to highest priority)\n        for path in self.config_paths:\n            if path.exists():\n                with path.open() as f:\n                    config.update(json.load(f))\n\n        # Override with environment variables\n        for key in config.keys():\n            env_var = f\"MYCLI_{key.upper()}\"\n            if env_var in os.environ:\n                config[key] = os.environ[env_var]\n\n        return config\n\n    def _defaults(self) -> dict[str, Any]:\n        return {\n            \"environment\": \"development\",\n            \"verbose\": False,\n            \"timeout\": 30,\n        }\n```\n\n## Setup.py / pyproject.toml\n\n```toml\n# pyproject.toml\n[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"mycli\"\nversion = \"1.0.0\"\ndescription = \"My awesome CLI tool\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"typer[all]>=0.9.0\",\n    \"rich>=13.0.0\",\n    \"questionary>=2.0.0\",\n]\n\n[project.scripts]\nmycli = \"mycli.cli:main\"\n\n[project.optional-dependencies]\ndev = [\n    \"pytest>=7.0.0\",\n    \"pytest-cov>=4.0.0\",\n]\n```\n\n## Testing CLIs\n\n```python\nfrom typer.testing import CliRunner\nfrom mycli.cli import app\n\nrunner = CliRunner()\n\ndef test_version():\n    result = runner.invoke(app, [\"--version\"])\n    assert result.exit_code == 0\n    assert \"1.0.0\" in result.stdout\n\ndef test_init():\n    result = runner.invoke(app, [\"init\", \"my-project\"])\n    assert result.exit_code == 0\n    assert \"Creating my-project\" in result.stdout\n\ndef test_init_with_template():\n    result = runner.invoke(app, [\"init\", \"my-project\", \"--template\", \"react\"])\n    assert result.exit_code == 0\n    assert \"react\" in result.stdout\n\ndef test_invalid_command():\n    result = runner.invoke(app, [\"invalid\"])\n    assert result.exit_code != 0\n```\n\n## Progress Bars (tqdm)\n\n```python\nfrom tqdm import tqdm\nimport time\n\n# Simple progress bar\nfor i in tqdm(range(100), desc=\"Processing\"):\n    process_item(i)\n\n# Custom format\nwith tqdm(total=100, desc=\"Downloading\", unit=\"MB\") as pbar:\n    for chunk in download_chunks():\n        pbar.update(len(chunk))\n\n# Multiple progress bars\nfrom tqdm import trange\n\nfor epoch in trange(10, desc=\"Epochs\"):\n    for batch in trange(100, desc=\"Batches\", leave=False):\n        train_batch(batch)\n```\n","skills/cli-developer/references/ux-patterns.md":"# CLI UX Patterns\n\n## Progress Indicators\n\n### When to Use What\n\n```\nDeterminate (known total):\n  [████████████░░░░░░░░] 60% (3/5 files)\n  Use: File operations, downloads, batch processing\n\nIndeterminate (unknown duration):\n  ⠋ Loading...\n  Use: API calls, database queries, waiting for external services\n\nMulti-step:\n  ✓ Dependencies installed\n  ⠋ Building application...\n  ⏳ Running tests...\n  Use: Multi-phase operations (build, deploy, etc.)\n```\n\n### Progress Bar Best Practices\n\n```\nGood:\n[████████████░░░░░░░░] 60% | 120/200 MB | 2.4 MB/s | ETA: 33s\n↑ Visual     ↑ Percent  ↑ Progress  ↑ Rate     ↑ Time\n\nComponents:\n- Visual bar (20-40 chars)\n- Percentage (when known)\n- Current/total (with units)\n- Speed/rate (when applicable)\n- ETA (estimated time remaining)\n\nBad:\nProcessing... (no feedback)\n60% (no context)\n[████████████████████████████████████████] (too wide)\n```\n\n### Spinner Styles\n\n```\n⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏   Dots (elegant, low-key)\n⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷        Blocks (bold, attention)\n◐ ◓ ◑ ◒                  Circle (classic)\n▖ ▘ ▝ ▗                  Corners (minimal)\n⠁ ⠂ ⠄ ⡀ ⢀ ⠠ ⠐ ⠈        Line (subtle)\n\nChoose based on:\n- Terminal compatibility (stick to ASCII for Windows)\n- Branding (match your tool's personality)\n- Context (subtle for background, bold for main task)\n```\n\n## Color Usage\n\n### Semantic Colors\n\n```\nRed:     Errors, failures, destructive actions\nYellow:  Warnings, deprecations, non-critical issues\nGreen:   Success, completion, positive feedback\nBlue:    Information, hints, neutral messages\nCyan:    Commands, code, technical details\nMagenta: Highlights, special items\nGray:    Less important, metadata, timestamps\n\nExamples:\n✓ Success: Deployment complete\n✗ Error: File not found\n⚠ Warning: Deprecated flag --old-flag\nℹ Info: Using cache from ~/.mycli/cache\n```\n\n### When to Disable Colors\n\n```javascript\n// Detect non-TTY output (piped to file, etc.)\nconst noColor = !process.stdout.isTTY ||\n                process.env.NO_COLOR ||\n                process.env.CI === 'true';\n\nif (noColor) {\n  // Disable colors\n}\n\n// Support NO_COLOR standard\n// https://no-color.org/\n```\n\n### Color Accessibility\n\n```\n- Don't rely on color alone (use symbols too)\n- Provide high contrast (test with various terminals)\n- Support color blindness (red/green alternatives)\n\nGood:\n✓ Build successful (green)\n✗ Build failed (red)\n↑ Symbols work without color\n\nBad:\nSuccess (only color, no symbol)\nFailed (only color, no symbol)\n```\n\n## Help Text Design\n\n### Command Help Structure\n\n```\nUSAGE\n  mycli <command> [options]\n\nCOMMANDS\n  init         Initialize a new project\n  deploy       Deploy to environment\n  config       Manage configuration\n  plugins      Manage plugins\n\nOPTIONS\n  -h, --help     Show help\n  -v, --version  Show version\n  --config FILE  Config file path\n\nRun 'mycli <command> --help' for more information on a command.\n\nEXAMPLES\n  # Initialize a new project\n  mycli init my-app\n\n  # Deploy to production\n  mycli deploy production --dry-run\n\nLearn more: https://docs.mycli.dev\n```\n\n### Subcommand Help\n\n```\nUSAGE\n  mycli deploy <environment> [options]\n\nARGUMENTS\n  environment    Target environment (required)\n                 Values: development, staging, production\n\nOPTIONS\n  -c, --config <file>    Config file path\n                         Default: ./mycli.config.yml\n\n  -f, --force            Skip confirmation prompts\n                         Use with caution in production\n\n  -d, --dry-run          Preview changes without executing\n                         Shows what would happen\n\n  -v, --verbose          Show detailed output\n                         Includes debug information\n\nEXAMPLES\n  # Deploy to production (with confirmation)\n  mycli deploy production\n\n  # Preview staging deployment\n  mycli deploy staging --dry-run\n\n  # Use custom config\n  mycli deploy production --config ./prod.yml\n\n  # Force deploy without prompts\n  mycli deploy production --force\n\nFor more information, visit https://docs.mycli.dev/deploy\n```\n\n## Error Messages\n\n### Good Error Messages\n\n```\nPattern: [Context] → [Problem] → [Solution]\n\nExample 1: File not found\n✗ Error: Config file not found\n\nSearched locations:\n  • ./mycli.config.yml\n  • ~/.config/mycli/config.yml\n  • /etc/mycli/config.yml\n\nSolutions:\n  • Run 'mycli init' to create a config file\n  • Use --config to specify a different location\n  • Check file permissions\n\nExample 2: Validation error\n✗ Error: Invalid environment 'prod'\n\nExpected one of:\n  • development\n  • staging\n  • production\n\nDid you mean 'production'?\n\nExample 3: Permission error\n✗ Error: Permission denied writing to /etc/mycli/config.yml\n\nThis operation requires elevated permissions.\n\nTry:\n  • Run with sudo: sudo mycli config set key value\n  • Use user config: mycli config set --user key value\n  • Check file permissions: ls -la /etc/mycli/config.yml\n```\n\n### Error Message Guidelines\n\n```\nDO:\n✓ Be specific (\"Port 3000 already in use\" not \"Port unavailable\")\n✓ Show context (\"in file config.yml, line 42\")\n✓ Suggest solutions (\"Try running 'mycli fix'\")\n✓ Use plain language (\"File not found\" not \"ENOENT\")\n\nDON'T:\n✗ Show stack traces to users (save for --debug)\n✗ Use jargon (\"EACCES: permission denied\")\n✗ Leave users stuck (\"Invalid input\" with no explanation)\n✗ Be vague (\"Something went wrong\")\n```\n\n## Interactive Prompts\n\n### Prompt Types\n\n```\nText Input:\n  Project name: my-awesome-app\n  ↑ Clear label\n\nSelect (Single Choice):\n  ? Select environment: (Use arrow keys)\n  ❯ development\n    staging\n    production\n\nCheckbox (Multiple Choice):\n  ? Select features: (Press space to select, enter to confirm)\n  ◉ TypeScript\n  ◯ ESLint\n  ◉ Prettier\n  ◯ Jest\n\nConfirmation:\n  ? Deploy to production? (y/N)\n  ↑ Default is No (safer)\n\nPassword:\n  ? Enter password: ********\n  ↑ Masked input\n```\n\n### Prompt Guidelines\n\n```\nDO:\n✓ Show keyboard hints (\"Use arrow keys\", \"Press space\")\n✓ Provide sensible defaults (pre-select common choices)\n✓ Allow skipping with Ctrl+C\n✓ Validate input immediately\n✓ Show preview/summary before final action\n\nDON'T:\n✗ Require interaction in CI/CD environments\n✗ Ask obvious questions (confirm every action)\n✗ Hide what will happen next\n✗ Make users repeat information\n```\n\n## Output Formatting\n\n### Tables\n\n```\nGood:\n┌─────────────┬──────────┬──────────┐\n│ Environment │ Status   │ Updated  │\n├─────────────┼──────────┼──────────┤\n│ production  │ ✓ Active │ 2h ago   │\n│ staging     │ ✓ Active │ 5m ago   │\n│ development │ ✗ Down   │ 1d ago   │\n└─────────────┴──────────┴──────────┘\n\nMinimal (for scripting):\nEnvironment  Status  Updated\nproduction   Active  2h ago\nstaging      Active  5m ago\ndevelopment  Down    1d ago\n\nJSON (for programmatic use):\n[\n  {\"env\": \"production\", \"status\": \"active\", \"updated\": \"2h ago\"},\n  {\"env\": \"staging\", \"status\": \"active\", \"updated\": \"5m ago\"}\n]\n```\n\n### Lists\n\n```\nBulleted:\nFeatures:\n  • TypeScript support\n  • Hot reload\n  • Auto-formatting\n\nNumbered:\nSteps to deploy:\n  1. Build application\n  2. Run tests\n  3. Deploy to server\n  4. Verify deployment\n\nTree:\nmy-app/\n├── src/\n│   ├── components/\n│   └── utils/\n├── tests/\n└── package.json\n```\n\n## Status Messages\n\n### Real-time Updates\n\n```\nMulti-step process:\n✓ Dependencies installed (2.3s)\n✓ Application built (8.1s)\n⠋ Running tests... (current)\n⏳ Deploying... (pending)\n⏳ Verifying... (pending)\n\nUpdates:\n⠋ Installing dependencies...\n  → npm install\n✓ Dependencies installed (2.3s)\n\n⠋ Building application...\n  → webpack build\n✓ Application built (8.1s)\n  → Output: dist/ (2.4 MB)\n```\n\n### Summary/Completion\n\n```\n✓ Deployment complete!\n\nSummary:\n  Environment:  production\n  Version:      v1.2.3\n  Duration:     2m 34s\n  Deployed:     2023-12-14 10:30:45 UTC\n\nNext steps:\n  • View logs: mycli logs production\n  • Monitor:   mycli status production\n  • Rollback:  mycli rollback production\n\nURL: https://app.example.com\n```\n\n## Debugging & Verbose Mode\n\n```\nNormal mode (default):\n✓ Deployed to production (2m 34s)\n\nVerbose mode (--verbose):\n[10:30:12] Starting deployment...\n[10:30:13] Loading config from ./mycli.config.yml\n[10:30:14] Connecting to production server...\n[10:30:15] Uploading files (124 files, 2.4 MB)...\n[10:30:28] Running post-deploy hooks...\n[10:32:46] ✓ Deployment complete\n\nDebug mode (--debug):\n[DEBUG] Config loaded: {env: 'production', ...}\n[DEBUG] SSH connection established: user@host\n[DEBUG] Executing: rsync -avz ./dist/ user@host:/var/www/\n[DEBUG] Output: sending incremental file list...\n[DEBUG] Exit code: 0\n✓ Deployed to production (2m 34s)\n\nUsage:\n# Normal: concise output\nmycli deploy production\n\n# Verbose: detailed steps\nmycli deploy production --verbose\n\n# Debug: everything including internals\nDEBUG=* mycli deploy production\n```\n\n## Man Page Format\n\n```\nNAME\n    mycli-deploy - Deploy application to environment\n\nSYNOPSIS\n    mycli deploy <environment> [options]\n\nDESCRIPTION\n    Deploy your application to the specified environment.\n    Supports development, staging, and production environments.\n\nOPTIONS\n    -c, --config <file>\n        Path to configuration file\n        Default: ./mycli.config.yml\n\n    -f, --force\n        Skip all confirmation prompts\n        Use with caution in production\n\n    -d, --dry-run\n        Preview deployment without executing\n        Shows what would be deployed\n\nEXAMPLES\n    Deploy to production:\n        mycli deploy production\n\n    Preview staging deployment:\n        mycli deploy staging --dry-run\n\nSEE ALSO\n    mycli-init(1), mycli-config(1), mycli-rollback(1)\n```\n","skills/cloud-architect/SKILL.md":"---\nname: cloud-architect\ndescription: Designs cloud architectures, creates migration plans, generates cost optimization recommendations, and produces disaster recovery strategies across AWS, Azure, and GCP. Use when designing cloud architectures, planning migrations, or optimizing multi-cloud deployments. Invoke for Well-Architected Framework, cost optimization, disaster recovery, landing zones, security architecture, serverless design.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: infrastructure\n  triggers: AWS, Azure, GCP, Google Cloud, cloud migration, cloud architecture, multi-cloud, cloud cost, Well-Architected, landing zone, cloud security, disaster recovery, cloud native, serverless architecture\n  role: architect\n  scope: infrastructure\n  output-format: architecture\n  related-skills: devops-engineer, kubernetes-specialist, terraform-engineer, security-reviewer, microservices-architect, monitoring-expert\n---\n\n# Cloud Architect\n\n## Core Workflow\n\n1. **Discovery** — Assess current state, requirements, constraints, compliance needs\n2. **Design** — Select services, design topology, plan data architecture\n3. **Security** — Implement zero-trust, identity federation, encryption\n4. **Cost Model** — Right-size resources, reserved capacity, auto-scaling\n5. **Migration** — Apply 6Rs framework, define waves, validate connectivity before cutover\n6. **Operate** — Set up monitoring, automation, continuous optimization\n\n### Workflow Validation Checkpoints\n\n**After Design:** Confirm every component has a redundancy strategy and no single points of failure exist in the topology.\n\n**Before Migration cutover:** Validate VPC peering or connectivity is fully established:\n```bash\n# AWS: confirm peering connection is Active before proceeding\naws ec2 describe-vpc-peering-connections \\\n  --filters \"Name=status-code,Values=active\"\n\n# Azure: confirm VNet peering state\naz network vnet peering list \\\n  --resource-group myRG --vnet-name myVNet \\\n  --query \"[].{Name:name,State:peeringState}\"\n```\n\n**After Migration:** Verify application health and routing:\n```bash\n# AWS: check target group health in ALB\naws elbv2 describe-target-health \\\n  --target-group-arn arn:aws:elasticloadbalancing:...\n```\n\n**After DR test:** Confirm RTO/RPO targets were met; document actual recovery times.\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| AWS Services | `references/aws.md` | EC2, S3, Lambda, RDS, Well-Architected Framework |\n| Azure Services | `references/azure.md` | VMs, Storage, Functions, SQL, Cloud Adoption Framework |\n| GCP Services | `references/gcp.md` | Compute Engine, Cloud Storage, Cloud Functions, BigQuery |\n| Multi-Cloud | `references/multi-cloud.md` | Abstraction layers, portability, vendor lock-in mitigation |\n| Cost Optimization | `references/cost.md` | Reserved instances, spot, right-sizing, FinOps practices |\n\n## Constraints\n\n### MUST DO\n- Design for high availability (99.9%+)\n- Implement security by design (zero-trust)\n- Use infrastructure as code (Terraform, CloudFormation)\n- Enable cost allocation tags and monitoring\n- Plan disaster recovery with defined RTO/RPO\n- Implement multi-region for critical workloads\n- Use managed services when possible\n- Document architectural decisions\n\n### MUST NOT DO\n- Store credentials in code or public repos\n- Skip encryption (at rest and in transit)\n- Create single points of failure\n- Ignore cost optimization opportunities\n- Deploy without proper monitoring\n- Use overly complex architectures\n- Ignore compliance requirements\n- Skip disaster recovery testing\n\n## Common Patterns with Examples\n\n### Least-Privilege IAM (Zero-Trust)\n\nRather than broad policies, scope permissions to specific resources and actions:\n\n```bash\n# AWS: create a scoped role for an application\naws iam create-role \\\n  --role-name AppRole \\\n  --assume-role-policy-document file://trust-policy.json\n\naws iam put-role-policy \\\n  --role-name AppRole \\\n  --policy-name AppInlinePolicy \\\n  --policy-document '{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\"],\n      \"Resource\": \"arn:aws:s3:::my-app-bucket/*\"\n    }]\n  }'\n```\n\n```hcl\n# Terraform equivalent\nresource \"aws_iam_role\" \"app_role\" {\n  name               = \"AppRole\"\n  assume_role_policy = data.aws_iam_policy_document.trust.json\n}\n\nresource \"aws_iam_role_policy\" \"app_policy\" {\n  role = aws_iam_role.app_role.id\n  policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [{\n      Effect   = \"Allow\"\n      Action   = [\"s3:GetObject\", \"s3:PutObject\"]\n      Resource = \"${aws_s3_bucket.app.arn}/*\"\n    }]\n  })\n}\n```\n\n### VPC with Public/Private Subnets (Terraform)\n\n```hcl\nresource \"aws_vpc\" \"main\" {\n  cidr_block           = \"10.0.0.0/16\"\n  enable_dns_hostnames = true\n  tags = { Name = \"main\", CostCenter = var.cost_center }\n}\n\nresource \"aws_subnet\" \"private\" {\n  count             = 2\n  vpc_id            = aws_vpc.main.id\n  cidr_block        = cidrsubnet(\"10.0.0.0/16\", 8, count.index)\n  availability_zone = data.aws_availability_zones.available.names[count.index]\n}\n\nresource \"aws_subnet\" \"public\" {\n  count                   = 2\n  vpc_id                  = aws_vpc.main.id\n  cidr_block              = cidrsubnet(\"10.0.0.0/16\", 8, count.index + 10)\n  availability_zone       = data.aws_availability_zones.available.names[count.index]\n  map_public_ip_on_launch = true\n}\n```\n\n### Auto-Scaling Group (Terraform)\n\n```hcl\nresource \"aws_autoscaling_group\" \"app\" {\n  desired_capacity    = 2\n  min_size            = 1\n  max_size            = 10\n  vpc_zone_identifier = aws_subnet.private[*].id\n\n  launch_template {\n    id      = aws_launch_template.app.id\n    version = \"$Latest\"\n  }\n\n  tag {\n    key                 = \"CostCenter\"\n    value               = var.cost_center\n    propagate_at_launch = true\n  }\n}\n\nresource \"aws_autoscaling_policy\" \"cpu_target\" {\n  autoscaling_group_name = aws_autoscaling_group.app.name\n  policy_type            = \"TargetTrackingScaling\"\n  target_tracking_configuration {\n    predefined_metric_specification {\n      predefined_metric_type = \"ASGAverageCPUUtilization\"\n    }\n    target_value = 60.0\n  }\n}\n```\n\n### Cost Analysis CLI\n\n```bash\n# AWS: identify top cost drivers for the last 30 days\naws ce get-cost-and-usage \\\n  --time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \\\n  --granularity MONTHLY \\\n  --metrics \"UnblendedCost\" \\\n  --group-by Type=DIMENSION,Key=SERVICE \\\n  --query 'ResultsByTime[0].Groups[*].{Service:Keys[0],Cost:Metrics.UnblendedCost.Amount}' \\\n  --output table\n\n# Azure: review spend by resource group\naz consumption usage list \\\n  --start-date $(date -d '30 days ago' +%Y-%m-%d) \\\n  --end-date $(date +%Y-%m-%d) \\\n  --query \"[].{ResourceGroup:resourceGroup,Cost:pretaxCost,Currency:currency}\" \\\n  --output table\n```\n\n## Output Templates\n\nWhen designing cloud architecture, provide:\n1. Architecture diagram with services and data flow\n2. Service selection rationale (compute, storage, database, networking)\n3. Security architecture (IAM, network segmentation, encryption)\n4. Cost estimation and optimization strategy\n5. Deployment approach and rollback plan\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/infrastructure/cloud-architect/)\n","skills/cloud-architect/references/aws.md":"# AWS Architecture Reference\n\nComprehensive guide for AWS services, patterns, and Well-Architected Framework implementation.\n\n## Well-Architected Framework\n\n### Six Pillars\n\n1. **Operational Excellence**\n   - Infrastructure as Code (CloudFormation, CDK, Terraform)\n   - Continuous integration/deployment\n   - Observability (CloudWatch, X-Ray)\n   - Runbooks and playbooks\n   - Game days and failure injection\n\n2. **Security**\n   - Identity and Access Management (IAM)\n   - Detective controls (GuardDuty, Security Hub)\n   - Infrastructure protection (VPC, security groups, NACLs)\n   - Data protection (KMS, encryption at rest/transit)\n   - Incident response automation\n\n3. **Reliability**\n   - Multi-AZ deployments\n   - Auto Scaling groups\n   - Route 53 health checks and failover\n   - Backup and restore (AWS Backup)\n   - Chaos engineering (AWS FIS)\n\n4. **Performance Efficiency**\n   - Right-sizing with Compute Optimizer\n   - Caching strategies (CloudFront, ElastiCache)\n   - Database optimization (RDS Performance Insights)\n   - Serverless architectures\n   - Global content delivery\n\n5. **Cost Optimization**\n   - Reserved Instances and Savings Plans\n   - Spot Instances for fault-tolerant workloads\n   - S3 Intelligent-Tiering and lifecycle policies\n   - Right-sizing recommendations\n   - Cost allocation tags and budgets\n\n6. **Sustainability**\n   - Region selection for renewable energy\n   - Serverless to minimize idle resources\n   - Efficient data storage patterns\n   - Resource utilization optimization\n\n## Core Services Architecture\n\n### Compute\n\n**EC2 (Elastic Compute Cloud)**\n- Instance families: General (t3, m5), Compute (c5), Memory (r5), GPU (p3, g4)\n- Auto Scaling: Target tracking, step scaling, scheduled scaling\n- Placement groups: Cluster, partition, spread\n- Best practices: Use latest generation, right-size, enable detailed monitoring\n\n**Lambda**\n- Invocation models: Synchronous, asynchronous, event source mapping\n- Concurrency: Reserved, provisioned, burst limits\n- Layers for shared dependencies\n- Best practices: Keep functions small, use environment variables, set timeouts\n\n**ECS/EKS (Container Services)**\n- ECS: Fargate for serverless, EC2 for control\n- EKS: Managed Kubernetes with AWS integration\n- Service mesh: App Mesh for observability\n- Best practices: Use Fargate for simplicity, EKS for portability\n\n**Elastic Beanstalk**\n- Managed platform for web apps\n- Auto-scaling and load balancing included\n- Support for multiple languages and Docker\n\n### Storage\n\n**S3 (Simple Storage Service)**\n- Storage classes: Standard, IA, One Zone-IA, Glacier, Deep Archive\n- Lifecycle policies for automatic tiering\n- Versioning and MFA delete for protection\n- Cross-region replication for DR\n- Best practices: Enable versioning, use lifecycle policies, block public access\n\n**EBS (Elastic Block Store)**\n- Volume types: gp3 (general), io2 (IOPS), st1 (throughput), sc1 (cold)\n- Snapshots to S3 for backup\n- Encryption by default\n- Best practices: Use gp3 for most workloads, enable encryption\n\n**EFS (Elastic File System)**\n- NFSv4 file system for shared access\n- Performance modes: General purpose, Max I/O\n- Throughput modes: Bursting, provisioned\n- Best practices: Use lifecycle management, enable encryption\n\n**FSx**\n- FSx for Windows File Server (SMB)\n- FSx for Lustre (HPC workloads)\n- FSx for NetApp ONTAP\n- FSx for OpenZFS\n\n### Database\n\n**RDS (Relational Database Service)**\n- Engines: MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, Aurora\n- Multi-AZ for high availability\n- Read replicas for scalability\n- Automated backups and point-in-time recovery\n- Best practices: Use Aurora for performance, enable Multi-AZ, use read replicas\n\n**Aurora**\n- MySQL and PostgreSQL compatible\n- 5x MySQL, 3x PostgreSQL performance\n- Global databases for cross-region DR\n- Serverless v2 for variable workloads\n- Best practices: Use Aurora Serverless for unpredictable workloads\n\n**DynamoDB**\n- NoSQL key-value and document database\n- On-demand or provisioned capacity\n- Global tables for multi-region replication\n- DynamoDB Streams for change data capture\n- Best practices: Use on-demand for unpredictable traffic, implement GSI carefully\n\n**ElastiCache**\n- Redis or Memcached in-memory caching\n- Cluster mode for Redis scalability\n- Best practices: Use for session storage, API caching\n\n### Networking\n\n**VPC (Virtual Private Cloud)**\n- CIDR planning: Avoid overlaps, plan for growth\n- Subnets: Public (IGW), private (NAT), isolated (no internet)\n- Route tables and routing decisions\n- Security groups (stateful) and NACLs (stateless)\n- Best practices: Use /16 for VPC, /24 for subnets, plan IP space\n\n**Route 53**\n- DNS service with health checks\n- Routing policies: Simple, weighted, latency, failover, geolocation\n- Best practices: Use alias records, enable DNSSEC\n\n**CloudFront**\n- Global CDN with edge locations\n- Origin types: S3, ALB, custom origins\n- Lambda@Edge for request/response manipulation\n- Best practices: Enable compression, use field-level encryption\n\n**VPN and Direct Connect**\n- Site-to-Site VPN for encrypted tunnels\n- Direct Connect for dedicated bandwidth\n- Transit Gateway for hub-and-spoke topology\n- Best practices: Use Direct Connect for high bandwidth, Transit Gateway for complex routing\n\n**API Gateway**\n- REST APIs, HTTP APIs, WebSocket APIs\n- Throttling and quotas\n- Integration with Lambda, HTTP endpoints, AWS services\n- Best practices: Use HTTP APIs for lower cost, implement caching\n\n### Security\n\n**IAM (Identity and Access Management)**\n- Principle of least privilege\n- Roles for applications, not access keys\n- MFA for privileged users\n- Service Control Policies (SCPs) for organization-wide controls\n- Best practices: Use roles, enable MFA, rotate credentials\n\n**KMS (Key Management Service)**\n- Customer managed keys (CMKs)\n- Automatic key rotation\n- Envelope encryption pattern\n- Best practices: Enable automatic rotation, use grants for temporary access\n\n**Secrets Manager**\n- Automatic rotation for RDS credentials\n- Versioning and rollback\n- Best practices: Rotate secrets regularly, use VPC endpoints\n\n**Security Hub**\n- Centralized security findings\n- CIS AWS Foundations Benchmark\n- Integration with GuardDuty, Inspector, Macie\n\n**GuardDuty**\n- Threat detection using ML\n- Monitors CloudTrail, VPC Flow Logs, DNS logs\n\n## Architecture Patterns\n\n### High Availability\n\n**Multi-AZ Pattern**\n```\n- Application Load Balancer across 3 AZs\n- Auto Scaling group with instances in each AZ\n- RDS Multi-AZ for database\n- S3 for static assets (11 9's durability)\n```\n\n**Multi-Region Pattern**\n```\n- Route 53 with health checks and failover\n- CloudFront for global distribution\n- Aurora Global Database for <1s RPO\n- S3 Cross-Region Replication\n```\n\n### Serverless Architecture\n\n**API-Driven Pattern**\n```\nAPI Gateway -> Lambda -> DynamoDB\n              |\n              v\n          EventBridge -> Lambda (async processing)\n```\n\n**Event-Driven Pattern**\n```\nS3 Event -> Lambda -> Process -> SNS\n                                  |\n                                  v\n                              Multiple subscribers\n```\n\n### Microservices on AWS\n\n**Container-Based**\n```\nALB -> ECS Fargate (multiple services)\n    |\n    v\nService Discovery (Cloud Map)\n    |\n    v\nRDS/DynamoDB per service\n```\n\n**Service Mesh**\n```\nApp Mesh for traffic management\nX-Ray for distributed tracing\nCloudWatch Container Insights\n```\n\n### Data Lake Architecture\n\n```\nData Sources -> Kinesis Data Streams\n                      |\n                      v\n              Kinesis Firehose\n                      |\n                      v\n                S3 (raw bucket)\n                      |\n                      v\n        Glue ETL or Lambda processing\n                      |\n                      v\n            S3 (processed bucket)\n                      |\n                      v\n          Athena/Redshift Spectrum\n                      |\n                      v\n              QuickSight dashboards\n```\n\n## Migration Strategies (6Rs)\n\n1. **Rehost (Lift-and-Shift)**\n   - AWS Application Migration Service (MGN)\n   - Minimal changes, quick migration\n   - Use for legacy apps with compliance constraints\n\n2. **Replatform (Lift-Tinker-and-Shift)**\n   - Migrate to RDS instead of self-managed databases\n   - Use Elastic Beanstalk instead of custom app servers\n   - Small optimizations during migration\n\n3. **Repurchase (Drop-and-Shop)**\n   - Move to SaaS (e.g., Salesforce, Workday)\n   - Reduce maintenance burden\n\n4. **Refactor/Re-architect**\n   - Modernize to serverless or containers\n   - Highest effort, highest benefit\n   - Use for competitive advantage applications\n\n5. **Retire**\n   - Decommission unused applications\n   - Reduce attack surface and costs\n\n6. **Retain**\n   - Keep on-premises temporarily\n   - Migrate later or keep for regulatory reasons\n\n## Landing Zone Design\n\n**AWS Control Tower**\n- Multi-account strategy (AWS Organizations)\n- Account factory for standardization\n- Guardrails for governance (SCPs)\n- Centralized logging (CloudTrail, Config)\n\n**Account Structure**\n```\nRoot\n├── Security OU\n│   ├── Log Archive Account\n│   └── Security Tooling Account\n├── Infrastructure OU\n│   ├── Network Account (Transit Gateway, VPN)\n│   └── Shared Services Account\n└── Workloads OU\n    ├── Production Account\n    ├── Staging Account\n    └── Development Account\n```\n\n**Network Design**\n```\nTransit Gateway (hub)\n    |\n    ├── Production VPC\n    ├── Staging VPC\n    ├── Development VPC\n    └── On-premises (Direct Connect/VPN)\n```\n\n## Cost Optimization Strategies\n\n**Compute Savings**\n- Compute Savings Plans (up to 66% savings)\n- EC2 Reserved Instances (1-year or 3-year)\n- Spot Instances for batch/fault-tolerant workloads\n- Lambda: Reduce memory if possible, use reserved concurrency\n\n**Storage Savings**\n- S3 Intelligent-Tiering for unpredictable access\n- Lifecycle policies to Glacier/Deep Archive\n- EBS gp3 instead of gp2 (20% cheaper, better performance)\n- Delete unused snapshots and volumes\n\n**Database Savings**\n- Aurora Serverless v2 for variable workloads\n- RDS Reserved Instances\n- DynamoDB on-demand for unpredictable workloads\n- Read replicas in same region to reduce cross-AZ data transfer\n\n**Monitoring and Alerting**\n- AWS Cost Explorer for analysis\n- AWS Budgets for alerts\n- Cost Anomaly Detection\n- Trusted Advisor for recommendations\n\n## Disaster Recovery\n\n**RPO and RTO Targets**\n- Backup and Restore: Hours RPO/RTO (lowest cost)\n- Pilot Light: Minutes RPO, hours RTO\n- Warm Standby: Seconds RPO, minutes RTO\n- Multi-Site Active/Active: Near-zero RPO/RTO (highest cost)\n\n**Implementation**\n- AWS Backup for centralized backup management\n- Aurora Global Database for cross-region replication\n- S3 Cross-Region Replication\n- Route 53 health checks and failover routing\n- Regular DR testing with CloudFormation/Terraform\n\n## Monitoring and Observability\n\n**CloudWatch**\n- Metrics: Standard (5 min) and detailed (1 min)\n- Alarms with SNS notifications\n- Logs Insights for log analysis\n- Dashboards for visualization\n\n**X-Ray**\n- Distributed tracing for microservices\n- Service map visualization\n- Trace annotations and metadata\n\n**AWS Config**\n- Resource inventory and change tracking\n- Compliance rules evaluation\n- Relationship tracking between resources\n","skills/cloud-architect/references/azure.md":"# Azure Architecture Reference\n\nComprehensive guide for Azure services, patterns, and Cloud Adoption Framework implementation.\n\n## Cloud Adoption Framework\n\n### Framework Phases\n\n1. **Strategy**\n   - Define business justification\n   - Expected business outcomes\n   - Business case development\n   - First project prioritization\n\n2. **Plan**\n   - Digital estate assessment\n   - Initial organization alignment\n   - Skills readiness plan\n   - Cloud adoption plan\n\n3. **Ready**\n   - Azure landing zone setup\n   - Azure setup guide\n   - Migration readiness\n   - Best practices validation\n\n4. **Adopt (Migrate + Innovate)**\n   - Migration: Assess, migrate, optimize\n   - Innovate: Build cloud-native solutions\n   - Best practices and patterns\n\n5. **Govern**\n   - Methodology for governance\n   - Governance benchmark\n   - Initial governance foundation\n   - Mature governance evolution\n\n6. **Manage**\n   - Business commitments\n   - Operations baseline\n   - Platform and workload specialization\n\n## Azure Well-Architected Framework\n\n### Five Pillars\n\n1. **Cost Optimization**\n   - Azure Cost Management and Billing\n   - Reserved instances and Savings Plans\n   - Azure Hybrid Benefit\n   - Auto-scaling and right-sizing\n\n2. **Operational Excellence**\n   - Infrastructure as Code (ARM, Bicep, Terraform)\n   - Azure DevOps and GitHub Actions\n   - Azure Monitor and Application Insights\n   - Deployment slots and blue-green deployments\n\n3. **Performance Efficiency**\n   - Azure CDN and Front Door\n   - Auto-scaling (VMSS, App Service)\n   - Caching (Redis, CDN)\n   - Performance diagnostics\n\n4. **Reliability**\n   - Availability Zones and regions\n   - Azure Site Recovery\n   - Load Balancer and Traffic Manager\n   - Backup and disaster recovery\n\n5. **Security**\n   - Azure AD (Entra ID)\n   - Network Security Groups and Firewalls\n   - Azure Key Vault\n   - Microsoft Defender for Cloud\n\n## Core Services Architecture\n\n### Compute\n\n**Virtual Machines**\n- VM sizes: General (D-series), Compute (F-series), Memory (E-series), GPU (N-series)\n- Availability Sets (99.95% SLA)\n- Availability Zones (99.99% SLA)\n- VM Scale Sets for auto-scaling\n- Best practices: Use managed disks, enable accelerated networking, use proximity placement groups\n\n**App Service**\n- Web Apps, API Apps, Mobile Apps\n- Deployment slots for staging\n- Auto-scaling based on metrics or schedule\n- Supports .NET, Java, Node.js, Python, PHP, Ruby\n- Best practices: Use deployment slots, enable auto-scaling, use App Service Plan efficiently\n\n**Azure Functions**\n- Consumption Plan (serverless)\n- Premium Plan (VNet integration, no cold start)\n- Dedicated Plan (App Service Plan)\n- Durable Functions for orchestration\n- Best practices: Keep functions small, use Premium for production, implement retry policies\n\n**Azure Kubernetes Service (AKS)**\n- Managed Kubernetes control plane\n- Azure CNI or kubenet networking\n- Azure AD integration\n- Virtual nodes (Azure Container Instances)\n- Best practices: Use system node pools, enable autoscaling, implement network policies\n\n**Container Instances**\n- Serverless containers\n- Fast startup without infrastructure management\n- Best for batch jobs and burstable workloads\n\n**Azure Batch**\n- Large-scale parallel and HPC workloads\n- Auto-scaling compute nodes\n- Task scheduling and dependencies\n\n### Storage\n\n**Blob Storage**\n- Storage tiers: Hot, Cool, Archive\n- Access tiers: Premium, Standard\n- Lifecycle management policies\n- Immutable storage for compliance\n- Best practices: Use lifecycle policies, enable soft delete, implement versioning\n\n**Azure Files**\n- SMB and NFS file shares\n- Integration with Azure File Sync\n- Premium tier for high performance\n- Best practices: Use Premium for databases, implement snapshots\n\n**Disk Storage**\n- Managed Disks: Premium SSD, Standard SSD, Standard HDD, Ultra Disk\n- Disk encryption with Azure Disk Encryption\n- Snapshots and incremental backups\n- Best practices: Use Premium SSD for production, enable encryption\n\n**Data Lake Storage Gen2**\n- Hierarchical namespace for big data\n- Built on Blob Storage\n- Integration with Azure Synapse and Databricks\n- Best practices: Enable hierarchical namespace, use lifecycle policies\n\n**Azure NetApp Files**\n- Enterprise-grade NFS and SMB shares\n- High performance and low latency\n- Snapshots and data protection\n\n### Database\n\n**Azure SQL Database**\n- Serverless and provisioned compute\n- Hyperscale for up to 100TB\n- Elastic pools for multiple databases\n- Auto-tuning and intelligent insights\n- Best practices: Use serverless for dev/test, enable geo-replication\n\n**Azure SQL Managed Instance**\n- Near 100% compatibility with SQL Server\n- VNet integration for isolation\n- Native virtual network implementation\n- Best practices: Use for lift-and-shift migrations\n\n**Cosmos DB**\n- Multi-model NoSQL database\n- Global distribution with multi-master\n- Consistency levels: Strong, Bounded staleness, Session, Consistent prefix, Eventual\n- APIs: SQL, MongoDB, Cassandra, Gremlin, Table\n- Best practices: Choose appropriate consistency, partition key design critical\n\n**Azure Database for PostgreSQL/MySQL/MariaDB**\n- Flexible Server (newer) vs Single Server (legacy)\n- High availability with zone redundancy\n- Read replicas for scaling\n- Best practices: Use Flexible Server, enable HA, implement connection pooling\n\n**Azure Cache for Redis**\n- In-memory caching\n- Clustering for scalability\n- Geo-replication for disaster recovery\n- Best practices: Use Premium tier for production, enable persistence\n\n### Networking\n\n**Virtual Network (VNet)**\n- CIDR planning (avoid overlaps)\n- Subnets with Network Security Groups\n- Service endpoints and Private Link\n- VNet peering for connectivity\n- Best practices: Plan IP address space, use NSGs, implement Private Link\n\n**Azure Load Balancer**\n- Layer 4 load balancing\n- Standard SKU (zone-redundant, SLA)\n- Health probes and distribution algorithms\n- Best practices: Use Standard SKU, configure health probes\n\n**Application Gateway**\n- Layer 7 load balancing\n- WAF (Web Application Firewall)\n- URL-based routing and SSL termination\n- Best practices: Enable WAF, use autoscaling\n\n**Azure Front Door**\n- Global load balancing and CDN\n- WAF at edge\n- Anycast for low latency\n- Best practices: Use for global applications, enable caching\n\n**VPN Gateway and ExpressRoute**\n- Site-to-Site VPN for encrypted connectivity\n- ExpressRoute for private, dedicated connection\n- Virtual WAN for global transit network\n- Best practices: Use ExpressRoute for production, implement redundancy\n\n**Azure Firewall**\n- Managed firewall service\n- Application and network rules\n- Threat intelligence\n- Best practices: Use in hub-spoke topology, enable DNS proxy\n\n**Azure Private Link**\n- Private connectivity to Azure services\n- No public internet exposure\n- Available for PaaS services\n- Best practices: Use for all PaaS services in production\n\n### Security and Identity\n\n**Azure Active Directory (Microsoft Entra ID)**\n- Identity and access management\n- Conditional Access policies\n- Multi-factor authentication\n- B2B and B2C scenarios\n- Best practices: Enable MFA, use Conditional Access, implement PIM\n\n**Azure Key Vault**\n- Secrets, keys, and certificates management\n- Hardware Security Module (HSM) backed\n- Soft delete and purge protection\n- Best practices: Enable soft delete, use RBAC, implement Private Link\n\n**Microsoft Defender for Cloud**\n- Security posture management\n- Threat protection for hybrid workloads\n- Regulatory compliance dashboard\n- Just-in-time VM access\n- Best practices: Enable enhanced security, implement recommendations\n\n**Azure Policy**\n- Governance and compliance at scale\n- Built-in and custom policies\n- Deny, audit, append effects\n- Best practices: Assign at management group level, test before enforce\n\n**Azure Sentinel**\n- Cloud-native SIEM and SOAR\n- AI-powered threat detection\n- Integration with Microsoft 365, third-party tools\n- Best practices: Enable data connectors, create custom analytics rules\n\n## Architecture Patterns\n\n### High Availability\n\n**Zone-Redundant Pattern**\n```\nAzure Front Door (global)\n    |\n    v\nApplication Gateway (zone-redundant)\n    |\n    v\nVM Scale Set (across availability zones)\n    |\n    v\nAzure SQL Database (zone-redundant)\n```\n\n**Multi-Region Pattern**\n```\nAzure Traffic Manager (DNS-based routing)\n    |\n    ├── Region 1: App Service + SQL Database (primary)\n    └── Region 2: App Service + SQL Database (geo-replica)\n```\n\n### Hub-Spoke Topology\n\n```\nHub VNet\n├── Azure Firewall\n├── VPN Gateway\n└── Shared Services\n    |\n    ├── Spoke VNet 1 (Production)\n    ├── Spoke VNet 2 (Development)\n    └── Spoke VNet 3 (DMZ)\n```\n\n### Serverless Architecture\n\n**Event-Driven Pattern**\n```\nEvent Grid -> Azure Functions -> Cosmos DB\n                    |\n                    v\n              Service Bus -> Functions (processing)\n```\n\n**API-First Pattern**\n```\nAPI Management\n    |\n    ├── Function App 1 (auth)\n    ├── Function App 2 (business logic)\n    └── Function App 3 (data access)\n```\n\n### Microservices on Azure\n\n**AKS-Based**\n```\nAzure Front Door\n    |\n    v\nApplication Gateway + WAF\n    |\n    v\nAKS (multiple microservices)\n    |\n    ├── Cosmos DB (microservice A)\n    ├── SQL Database (microservice B)\n    └── Service Bus (async communication)\n```\n\n**Container Apps Pattern**\n```\nAzure Container Apps\n├── Dapr for state management\n├── KEDA for event-driven scaling\n└── Azure Monitor for observability\n```\n\n### Data Platform\n\n```\nData Sources\n    |\n    v\nEvent Hubs / IoT Hub\n    |\n    v\nStream Analytics (real-time processing)\n    |\n    v\nData Lake Storage Gen2\n    |\n    v\nAzure Synapse Analytics\n    |\n    v\nPower BI (visualization)\n```\n\n## Landing Zone Design\n\n### Enterprise-Scale Landing Zone\n\n**Management Group Hierarchy**\n```\nTenant Root Group\n├── Platform\n│   ├── Management (monitoring, automation)\n│   ├── Connectivity (hub networks, VPN)\n│   └── Identity (domain controllers)\n└── Landing Zones\n    ├── Corp (internal workloads)\n    └── Online (internet-facing workloads)\n```\n\n**Network Topology**\n```\nHub VNet (Connectivity subscription)\n├── Azure Firewall\n├── VPN Gateway\n├── ExpressRoute Gateway\n└── Bastion\n\nSpoke VNets (Workload subscriptions)\n├── Production VNet\n├── Staging VNet\n└── Development VNet\n```\n\n**Governance**\n- Azure Policy for compliance\n- Management groups for hierarchy\n- RBAC assignments at appropriate scope\n- Resource tags for cost allocation\n- Azure Blueprints for repeatable deployments\n\n## Migration Strategies\n\n### Azure Migrate\n\n1. **Assess**\n   - Discovery with Azure Migrate appliance\n   - Dependency analysis\n   - Performance-based sizing\n   - Cost estimation\n\n2. **Migrate**\n   - Azure Migrate: Server Migration (agentless)\n   - Database Migration Service\n   - App Service Migration Assistant\n   - Data Box for large data transfers\n\n3. **Optimize**\n   - Right-sizing recommendations\n   - Reserved instances\n   - Azure Hybrid Benefit\n\n### Migration Patterns\n\n**Rehost**: Azure Migrate for VMs\n**Replatform**: App Service, Azure SQL Database\n**Refactor**: Container Apps, AKS, Functions\n**Rebuild**: Azure-native services (Cosmos DB, Cognitive Services)\n\n## Cost Optimization\n\n### Compute Savings\n- Azure Reserved Instances (1-year or 3-year, up to 72% savings)\n- Azure Savings Plans for Compute (up to 65% savings)\n- Spot VMs for fault-tolerant workloads (up to 90% savings)\n- Azure Hybrid Benefit (use existing Windows Server/SQL licenses)\n- Auto-shutdown for dev/test VMs\n\n### Storage Savings\n- Blob Storage lifecycle policies (Hot -> Cool -> Archive)\n- Azure Files: Standard tier for general use\n- Managed Disks: Standard SSD instead of Premium if possible\n- Delete unused snapshots and disks\n\n### Database Savings\n- Serverless tier for Azure SQL Database\n- Reserved capacity for Cosmos DB\n- DTU model vs vCore (choose based on workload)\n- Pause Azure Synapse when not in use\n\n### Monitoring\n- Azure Cost Management + Billing\n- Cost alerts and budgets\n- Azure Advisor recommendations\n- Resource tagging for cost allocation\n\n## Disaster Recovery\n\n### Azure Site Recovery\n\n**VM Replication**\n- Azure to Azure replication\n- On-premises to Azure (VMware, Hyper-V, physical)\n- RPO: 30 seconds to a few minutes\n- Automated failover and failback\n\n**Recovery Plans**\n- Multi-tier application recovery\n- Customizable scripts and manual actions\n- Integration with Azure Automation\n\n### Backup Strategies\n\n**Azure Backup**\n- VM backups (application-consistent)\n- SQL Server and SAP HANA in Azure VMs\n- Azure Files backup\n- Cross-region restore\n\n**Database Backup**\n- SQL Database: Automated backups (7-35 days)\n- Cosmos DB: Continuous backup (30 days)\n- Long-term retention policies\n\n### High Availability\n\n**RTO/RPO Targets**\n- Active-Active: Multi-region with Traffic Manager (near-zero)\n- Active-Passive: Geo-replication with failover (minutes)\n- Backup and Restore: Azure Backup (hours)\n\n## Monitoring and Observability\n\n### Azure Monitor\n\n**Components**\n- Metrics: Time-series data (1-minute resolution)\n- Logs: Log Analytics workspace for queries (KQL)\n- Alerts: Metric, log, and activity log alerts\n- Dashboards: Custom visualizations\n\n**Application Insights**\n- APM for web applications\n- Distributed tracing\n- Live Metrics Stream\n- Smart detection and anomaly detection\n- Best practices: Instrument all applications, set up availability tests\n\n### Log Analytics\n\n**KQL Queries**\n```kusto\n// Performance analysis\nPerf\n| where CounterName == \"% Processor Time\"\n| summarize avg(CounterValue) by bin(TimeGenerated, 5m), Computer\n| render timechart\n\n// Failed requests\nrequests\n| where success == false\n| summarize count() by resultCode, bin(timestamp, 1h)\n```\n\n**Workbooks**\n- Interactive reports\n- Parameterized queries\n- Combining metrics and logs\n\n## Identity and Access\n\n### Azure AD Best Practices\n\n- Enable MFA for all users\n- Use Conditional Access policies\n- Implement Privileged Identity Management (PIM)\n- Regular access reviews\n- Break-glass accounts\n\n### RBAC Design\n\n**Built-in Roles**\n- Owner: Full access including RBAC\n- Contributor: Full access except RBAC\n- Reader: Read-only access\n- Custom roles for specific needs\n\n**Scope Hierarchy**\n```\nManagement Group (highest)\n    |\nSubscription\n    |\nResource Group\n    |\nResource (lowest)\n```\n\nBest practices: Assign at highest appropriate scope, use groups not individual users, apply least privilege\n","skills/cloud-architect/references/cost.md":"# Cloud Cost Optimization Reference\n\nComprehensive guide for cloud cost optimization including reserved instances, spot/preemptible, right-sizing, and FinOps practices.\n\n## FinOps Framework\n\n### FinOps Principles\n\n1. **Teams need to collaborate** - Finance, engineering, and business work together\n2. **Everyone takes ownership** - Decentralized cost responsibility\n3. **A centralized team drives FinOps** - Center of excellence for best practices\n4. **Reports should be accessible and timely** - Real-time visibility\n5. **Decisions are driven by business value** - Cost per business outcome\n6. **Take advantage of variable cost model** - Scale up and down as needed\n\n### FinOps Lifecycle\n\n```\n      Inform\n         |\n    +---------+\n    |         |\n    v         v\n Optimize --> Operate\n    ^         |\n    |         |\n    +---------+\n```\n\n**Inform Phase**\n- Visibility into cloud spend\n- Allocation and showback\n- Benchmarking and forecasting\n\n**Optimize Phase**\n- Rate optimization (RIs, savings plans)\n- Usage optimization (right-sizing)\n- Architectural optimization\n\n**Operate Phase**\n- Continuous improvement\n- Automation and governance\n- Anomaly detection\n\n## Compute Cost Optimization\n\n### Reserved Instances / Savings Plans\n\n**AWS Savings Plans**\n\n| Type | Flexibility | Savings |\n|------|-------------|---------|\n| Compute Savings Plans | Any EC2, Fargate, Lambda | Up to 66% |\n| EC2 Instance Savings Plans | Specific instance family, region | Up to 72% |\n| Reserved Instances | Specific instance type, AZ | Up to 72% |\n\n**Commitment Strategy**\n```\nBaseline (always-on): 1-year or 3-year Savings Plans\nVariable (predictable): Scheduled Reserved Instances\nSpiky (unpredictable): On-Demand + Spot\n```\n\n**Azure Reservations**\n```\n# Azure CLI - Purchase reservation\naz reservations reservation-order purchase \\\n  --sku Standard_D2s_v3 \\\n  --term P1Y \\\n  --billing-scope /subscriptions/{subscription-id} \\\n  --quantity 10 \\\n  --applied-scope-type Shared\n```\n\n**GCP Committed Use Discounts**\n- Resource-based: Specific vCPUs and memory\n- Spend-based: Dollar commitment for flexibility\n- 1-year (37% discount) or 3-year (55% discount)\n\n### Spot/Preemptible Instances\n\n**When to Use Spot**\n- Batch processing and analytics\n- CI/CD build agents\n- Stateless web servers (with auto-scaling)\n- Machine learning training\n- Development and testing environments\n\n**AWS Spot Best Practices**\n```yaml\n# EC2 Auto Scaling with Spot\nMixedInstancesPolicy:\n  InstancesDistribution:\n    OnDemandBaseCapacity: 2\n    OnDemandPercentageAboveBaseCapacity: 20\n    SpotAllocationStrategy: capacity-optimized\n  LaunchTemplate:\n    Overrides:\n      - InstanceType: m5.large\n      - InstanceType: m5a.large\n      - InstanceType: m4.large\n      - InstanceType: r5.large\n```\n\n**Spot Interruption Handling**\n```python\n# Check for spot termination notice (AWS)\nimport requests\n\ndef check_spot_termination():\n    try:\n        response = requests.get(\n            \"http://169.254.169.254/latest/meta-data/spot/termination-time\",\n            timeout=2\n        )\n        if response.status_code == 200:\n            # 2-minute warning - gracefully shutdown\n            graceful_shutdown()\n    except requests.exceptions.RequestException:\n        pass  # Not being terminated\n```\n\n**GCP Preemptible/Spot VMs**\n```hcl\n# Terraform - GCP Spot VM\nresource \"google_compute_instance\" \"spot\" {\n  name         = \"spot-instance\"\n  machine_type = \"n2-standard-4\"\n\n  scheduling {\n    preemptible                 = true\n    automatic_restart           = false\n    provisioning_model          = \"SPOT\"\n    instance_termination_action = \"STOP\"\n  }\n}\n```\n\n### Right-Sizing\n\n**Analysis Process**\n1. Collect metrics (CPU, memory, network, disk I/O)\n2. Identify idle or underutilized resources\n3. Recommend appropriate instance size\n4. Implement changes during maintenance windows\n5. Monitor and iterate\n\n**AWS Compute Optimizer**\n```bash\n# Enable Compute Optimizer\naws compute-optimizer update-enrollment-status \\\n  --status Active \\\n  --include-member-accounts\n\n# Get recommendations\naws compute-optimizer get-ec2-instance-recommendations \\\n  --filters name=Finding,values=OVER_PROVISIONED\n```\n\n**Right-Sizing Thresholds**\n| Metric | Underutilized | Optimal | Overutilized |\n|--------|---------------|---------|--------------|\n| CPU | <20% avg | 40-60% avg | >80% avg |\n| Memory | <30% avg | 50-70% avg | >85% avg |\n| Network | <10% capacity | Variable | >80% capacity |\n\n**Azure Advisor Recommendations**\n```bash\n# Get cost recommendations\naz advisor recommendation list \\\n  --category Cost \\\n  --query \"[?impact=='High']\"\n```\n\n## Storage Cost Optimization\n\n### Object Storage Tiering\n\n**AWS S3 Storage Classes**\n```\nS3 Standard\n    |\n    | (30 days)\n    v\nS3 Standard-IA\n    |\n    | (90 days)\n    v\nS3 Glacier Instant Retrieval\n    |\n    | (180 days)\n    v\nS3 Glacier Deep Archive\n```\n\n**Lifecycle Policy Example**\n```json\n{\n  \"Rules\": [\n    {\n      \"ID\": \"OptimizeCosts\",\n      \"Status\": \"Enabled\",\n      \"Filter\": { \"Prefix\": \"logs/\" },\n      \"Transitions\": [\n        { \"Days\": 30, \"StorageClass\": \"STANDARD_IA\" },\n        { \"Days\": 90, \"StorageClass\": \"GLACIER\" },\n        { \"Days\": 365, \"StorageClass\": \"DEEP_ARCHIVE\" }\n      ],\n      \"Expiration\": { \"Days\": 730 }\n    }\n  ]\n}\n```\n\n**S3 Intelligent-Tiering**\n- Automatic tiering based on access patterns\n- No retrieval fees\n- Small monitoring fee per object\n- Best for unpredictable access patterns\n\n### Block Storage Optimization\n\n**EBS Volume Selection**\n| Type | Use Case | $/GB/month |\n|------|----------|------------|\n| gp3 | General purpose | $0.08 |\n| gp2 | Legacy (migrate to gp3) | $0.10 |\n| io2 | High IOPS databases | $0.125+ |\n| st1 | Throughput (big data) | $0.045 |\n| sc1 | Cold archives | $0.015 |\n\n**gp3 Migration (20% savings)**\n```bash\n# Modify EBS volume from gp2 to gp3\naws ec2 modify-volume \\\n  --volume-id vol-12345678 \\\n  --volume-type gp3 \\\n  --iops 3000 \\\n  --throughput 125\n```\n\n### Database Storage\n\n**Aurora Storage Optimization**\n- Pay only for storage used (auto-scaling)\n- No pre-provisioning required\n- 10GB increments up to 128TB\n\n**DynamoDB Capacity Modes**\n| Mode | Best For | Pricing |\n|------|----------|---------|\n| On-Demand | Unpredictable traffic | Pay per request |\n| Provisioned | Steady traffic | Pay per capacity unit |\n| Provisioned + Auto Scaling | Variable but predictable | Lower cost than on-demand |\n\n## Network Cost Optimization\n\n### Data Transfer Costs\n\n**AWS Data Transfer Pricing**\n```\nInbound: Free\nSame AZ: Free\nCross-AZ: $0.01/GB each direction\nSame Region (via public IP): $0.01/GB\nCross-Region: $0.02/GB\nInternet Egress: $0.09/GB (first 10TB)\n```\n\n**Optimization Strategies**\n1. Keep traffic within same AZ when possible\n2. Use VPC endpoints for AWS services\n3. Use CloudFront for cacheable content\n4. Compress data before transfer\n5. Use regional rather than global services\n\n**VPC Endpoints (Avoid NAT Gateway)**\n```hcl\n# Gateway endpoint (free for S3, DynamoDB)\nresource \"aws_vpc_endpoint\" \"s3\" {\n  vpc_id       = aws_vpc.main.id\n  service_name = \"com.amazonaws.us-east-1.s3\"\n}\n\n# Interface endpoint (cheaper than NAT for specific services)\nresource \"aws_vpc_endpoint\" \"ecr\" {\n  vpc_id            = aws_vpc.main.id\n  service_name      = \"com.amazonaws.us-east-1.ecr.api\"\n  vpc_endpoint_type = \"Interface\"\n}\n```\n\n### CDN Optimization\n\n**CloudFront Cost Savings**\n- Lower data transfer rates than direct from origin\n- Cache hit ratio optimization (target >90%)\n- Use Origin Shield to reduce origin load\n- Compress objects (Gzip/Brotli)\n\n```yaml\n# CloudFront cache optimization\nCacheBehaviors:\n  - PathPattern: \"/static/*\"\n    CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6  # CachingOptimized\n    Compress: true\n    TTL:\n      DefaultTTL: 86400\n      MaxTTL: 31536000\n```\n\n## Serverless Cost Optimization\n\n### Lambda Optimization\n\n**Memory/CPU Tuning**\n```python\n# Use AWS Lambda Power Tuning\n# Finds optimal memory for cost vs performance\n\n# Results example:\n# 128MB:  $0.000021 per invocation, 3200ms duration\n# 256MB:  $0.000025 per invocation, 1600ms duration\n# 512MB:  $0.000031 per invocation, 800ms duration\n# 1024MB: $0.000042 per invocation, 450ms duration\n# Optimal: 512MB (best cost-performance balance)\n```\n\n**Cost Reduction Strategies**\n1. Right-size memory allocation\n2. Minimize cold starts (provisioned concurrency for critical paths)\n3. Use ARM64 (Graviton2) - 20% cheaper\n4. Optimize package size for faster cold starts\n5. Use Lambda Layers for shared dependencies\n\n**Graviton2 Migration**\n```yaml\n# SAM template with ARM64\nResources:\n  MyFunction:\n    Type: AWS::Serverless::Function\n    Properties:\n      Runtime: python3.11\n      Architectures:\n        - arm64  # 20% cost savings\n```\n\n### Container Optimization\n\n**Fargate Pricing Optimization**\n```\n# Fargate Spot: Up to 70% discount\n# Use for fault-tolerant workloads\n\nECS Service:\n  CapacityProviderStrategy:\n    - CapacityProvider: FARGATE_SPOT\n      Weight: 4\n    - CapacityProvider: FARGATE\n      Weight: 1\n      Base: 2  # Minimum on-demand tasks\n```\n\n**Right-Size Container Resources**\n```yaml\n# Analyze actual usage with Container Insights\nresources:\n  requests:\n    memory: \"256Mi\"  # Based on p95 usage + 20% buffer\n    cpu: \"100m\"      # Based on p95 usage + 20% buffer\n  limits:\n    memory: \"512Mi\"  # 2x requests for burst\n    cpu: \"500m\"\n```\n\n## Cost Allocation and Tagging\n\n### Tagging Strategy\n\n**Required Tags**\n```yaml\n# Terraform - enforce tags\nvariable \"required_tags\" {\n  default = {\n    environment  = \"prod\"\n    cost-center  = \"engineering\"\n    owner        = \"platform-team\"\n    project      = \"api-gateway\"\n    managed-by   = \"terraform\"\n  }\n}\n\nresource \"aws_instance\" \"example\" {\n  ami           = data.aws_ami.latest.id\n  instance_type = \"t3.medium\"\n  tags          = var.required_tags\n}\n```\n\n**Tag Enforcement**\n```json\n// AWS SCP - Deny untagged resources\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyUntaggedEC2\",\n      \"Effect\": \"Deny\",\n      \"Action\": \"ec2:RunInstances\",\n      \"Resource\": \"arn:aws:ec2:*:*:instance/*\",\n      \"Condition\": {\n        \"Null\": {\n          \"aws:RequestTag/cost-center\": \"true\"\n        }\n      }\n    }\n  ]\n}\n```\n\n### Cost Allocation Reports\n\n**AWS Cost and Usage Report**\n```bash\n# Enable detailed billing reports\naws cur put-report-definition \\\n  --report-definition '{\n    \"ReportName\": \"detailed-cost-report\",\n    \"TimeUnit\": \"HOURLY\",\n    \"Format\": \"Parquet\",\n    \"Compression\": \"Parquet\",\n    \"S3Bucket\": \"my-billing-bucket\",\n    \"S3Region\": \"us-east-1\",\n    \"AdditionalArtifacts\": [\"ATHENA\"]\n  }'\n```\n\n**Athena Queries for Analysis**\n```sql\n-- Cost by service and tag\nSELECT\n  line_item_product_code as service,\n  resource_tags_user_cost_center as cost_center,\n  SUM(line_item_unblended_cost) as cost\nFROM cost_report\nWHERE month = '2024-01'\nGROUP BY 1, 2\nORDER BY 3 DESC;\n\n-- Unused Reserved Instances\nSELECT\n  reservation_reservation_a_r_n,\n  reservation_unused_quantity,\n  reservation_unused_normalized_unit_quantity\nFROM cost_report\nWHERE reservation_unused_quantity > 0;\n```\n\n## Automation and Governance\n\n### Automated Cost Controls\n\n**AWS Budgets with Actions**\n```yaml\n# CloudFormation - Budget with auto-stop\nResources:\n  MonthlyCostBudget:\n    Type: AWS::Budgets::Budget\n    Properties:\n      Budget:\n        BudgetName: monthly-cost-limit\n        BudgetLimit:\n          Amount: 10000\n          Unit: USD\n        TimeUnit: MONTHLY\n        BudgetType: COST\n      NotificationsWithSubscribers:\n        - Notification:\n            NotificationType: ACTUAL\n            ComparisonOperator: GREATER_THAN\n            Threshold: 80\n          Subscribers:\n            - SubscriptionType: EMAIL\n              Address: finance@company.com\n```\n\n**Scheduled Scaling (Dev/Test)**\n```yaml\n# Stop non-prod resources nights/weekends\nResources:\n  ScaleDownSchedule:\n    Type: AWS::AutoScaling::ScheduledAction\n    Properties:\n      AutoScalingGroupName: !Ref DevASG\n      DesiredCapacity: 0\n      Recurrence: \"0 20 * * MON-FRI\"  # 8 PM weekdays\n\n  ScaleUpSchedule:\n    Type: AWS::AutoScaling::ScheduledAction\n    Properties:\n      AutoScalingGroupName: !Ref DevASG\n      DesiredCapacity: 3\n      Recurrence: \"0 8 * * MON-FRI\"   # 8 AM weekdays\n```\n\n### Cost Anomaly Detection\n\n**AWS Cost Anomaly Detection**\n```bash\n# Create anomaly monitor\naws ce create-anomaly-monitor \\\n  --anomaly-monitor '{\n    \"MonitorName\": \"ServiceMonitor\",\n    \"MonitorType\": \"DIMENSIONAL\",\n    \"MonitorDimension\": \"SERVICE\"\n  }'\n\n# Create anomaly subscription\naws ce create-anomaly-subscription \\\n  --anomaly-subscription '{\n    \"SubscriptionName\": \"CostAlerts\",\n    \"MonitorArnList\": [\"arn:aws:ce::123456789:anomalymonitor/abc123\"],\n    \"Subscribers\": [{\"Type\": \"EMAIL\", \"Address\": \"alerts@company.com\"}],\n    \"Threshold\": 100\n  }'\n```\n\n## Cost Metrics and KPIs\n\n### Key Metrics\n\n| Metric | Formula | Target |\n|--------|---------|--------|\n| Unit Cost | Total Cost / Business Metric | Decreasing |\n| Coverage | Reserved Hours / Total Hours | >70% |\n| Utilization | Used Reserved Hours / Purchased | >80% |\n| Waste | Idle Resource Cost / Total Cost | <10% |\n| Forecast Accuracy | Actual / Forecasted | 90-110% |\n\n### Dashboard Example\n\n```sql\n-- Cost efficiency dashboard metrics\nWITH metrics AS (\n  SELECT\n    date_trunc('month', usage_date) as month,\n    SUM(cost) as total_cost,\n    SUM(CASE WHEN reservation_arn IS NOT NULL THEN cost END) as reserved_cost,\n    COUNT(DISTINCT user_id) as active_users\n  FROM cloud_costs\n  GROUP BY 1\n)\nSELECT\n  month,\n  total_cost,\n  reserved_cost / total_cost as reservation_coverage,\n  total_cost / active_users as cost_per_user\nFROM metrics;\n```\n\n## Quick Wins Checklist\n\n**Immediate Savings (This Week)**\n- [ ] Delete unused EBS volumes and snapshots\n- [ ] Terminate stopped EC2 instances not needed\n- [ ] Remove unused Elastic IPs\n- [ ] Delete unused load balancers\n- [ ] Review and delete old AMIs\n\n**Short-Term (This Month)**\n- [ ] Right-size underutilized instances\n- [ ] Migrate gp2 volumes to gp3\n- [ ] Implement S3 lifecycle policies\n- [ ] Enable S3 Intelligent-Tiering\n- [ ] Schedule dev/test environments\n\n**Medium-Term (This Quarter)**\n- [ ] Purchase Savings Plans for baseline\n- [ ] Implement Spot for fault-tolerant workloads\n- [ ] Set up cost allocation tags\n- [ ] Enable Cost Anomaly Detection\n- [ ] Establish FinOps practices\n","skills/cloud-architect/references/gcp.md":"# GCP Architecture Reference\n\nComprehensive guide for Google Cloud Platform services, patterns, and architecture framework.\n\n## Google Cloud Architecture Framework\n\n### Five Pillars\n\n1. **Operational Excellence**\n   - Infrastructure as Code (Deployment Manager, Terraform)\n   - CI/CD with Cloud Build\n   - Monitoring with Cloud Monitoring (Stackdriver)\n   - SRE principles and SLOs\n   - Incident management\n\n2. **Security, Privacy, and Compliance**\n   - Identity and Access Management (Cloud IAM)\n   - VPC Service Controls for data perimeter\n   - Binary Authorization for containers\n   - Data encryption (default at rest and in transit)\n   - Security Command Center\n\n3. **Reliability**\n   - Multi-zone and multi-region deployments\n   - Load balancing and autoscaling\n   - Disaster recovery planning\n   - Chaos engineering practices\n   - SLIs, SLOs, and error budgets\n\n4. **Cost Optimization**\n   - Committed Use Discounts\n   - Sustained Use Discounts (automatic)\n   - Preemptible VMs and Spot VMs\n   - Recommender for right-sizing\n   - Active Assist for optimization\n\n5. **Performance Optimization**\n   - Cloud CDN and Media CDN\n   - Caching strategies (Memorystore)\n   - Database performance tuning\n   - Network optimization (Premium vs Standard tier)\n   - Regional and zonal resource placement\n\n## Core Services Architecture\n\n### Compute\n\n**Compute Engine**\n- Machine types: E2 (cost-optimized), N2 (balanced), C2 (compute-optimized), M2 (memory-optimized)\n- Custom machine types for specific needs\n- Preemptible VMs (up to 80% discount, max 24 hours)\n- Spot VMs (similar to preemptible, better availability)\n- Instance groups: Managed (with autoscaling), unmanaged\n- Best practices: Use latest generation, committed use discounts, Spot for batch jobs\n\n**Cloud Run**\n- Fully managed serverless container platform\n- Auto-scaling to zero\n- Pay per request\n- CPU allocated only during request handling\n- Best practices: Stateless containers, optimize cold starts, use Cloud Run jobs for batch\n\n**Cloud Functions**\n- Event-driven serverless functions\n- 1st gen: HTTP and background functions\n- 2nd gen: Built on Cloud Run, better performance\n- Event sources: Pub/Sub, Cloud Storage, Firestore, HTTP\n- Best practices: Use 2nd gen, minimize cold starts, implement retry logic\n\n**Google Kubernetes Engine (GKE)**\n- Managed Kubernetes with GCP integration\n- Autopilot mode: Fully managed, per-pod pricing\n- Standard mode: More control, node management\n- Workload Identity for secure service access\n- Binary Authorization for deployment policies\n- Best practices: Use Autopilot for simplicity, enable Workload Identity, implement network policies\n\n**App Engine**\n- Fully managed platform (PaaS)\n- Standard environment (sandboxed, auto-scaling)\n- Flexible environment (Docker containers, custom runtimes)\n- Traffic splitting for canary deployments\n- Best practices: Use Standard for web apps, Flexible for custom dependencies\n\n### Storage\n\n**Cloud Storage**\n- Storage classes: Standard, Nearline (30-day), Coldline (90-day), Archive (365-day)\n- Object lifecycle management\n- Object versioning and retention policies\n- Autoclass for automatic tier transitions\n- Requester pays for data transfer\n- Best practices: Use Autoclass, enable versioning, implement lifecycle policies\n\n**Persistent Disk**\n- Types: Standard (HDD), Balanced SSD, SSD, Extreme\n- Zonal and regional persistent disks\n- Snapshots for backup (incremental)\n- Disk resize without downtime\n- Best practices: Use Balanced SSD for most workloads, enable snapshots\n\n**Filestore**\n- Managed NFS file storage\n- Tiers: Basic (1-63.9 TB), Enterprise (1-10 TB, better performance)\n- Backup to Cloud Storage\n- Best practices: Use Enterprise for production, implement backups\n\n**Cloud Storage for Firebase**\n- Object storage for mobile and web apps\n- Client SDKs for direct upload/download\n- Security rules for access control\n\n### Database\n\n**Cloud SQL**\n- Managed MySQL, PostgreSQL, SQL Server\n- High availability configuration (regional)\n- Read replicas for scaling\n- Automated backups and point-in-time recovery\n- Best practices: Enable HA, use read replicas, implement connection pooling with Cloud SQL Proxy\n\n**Cloud Spanner**\n- Globally distributed relational database\n- Horizontal scalability with strong consistency\n- Multi-region for 99.999% availability\n- TrueTime for global consistency\n- Best practices: Design proper schema splits, use commit timestamps, optimize hotspots\n\n**Firestore (Native mode)**\n- NoSQL document database\n- Real-time synchronization\n- Offline support for mobile\n- ACID transactions\n- Best practices: Design document structure carefully, use collection group queries wisely\n\n**Bigtable**\n- NoSQL wide-column database\n- Petabyte-scale with single-digit millisecond latency\n- HBase API compatible\n- Linear scalability by adding nodes\n- Best practices: Design row keys to avoid hotspots, use replication for HA\n\n**Memorystore**\n- Managed Redis and Memcached\n- Standard tier (HA with replica) and Basic tier\n- Best practices: Use Standard tier for production, implement connection pooling\n\n**BigQuery**\n- Serverless data warehouse\n- SQL analytics on petabyte-scale data\n- Column-oriented storage\n- Automatic caching and optimization\n- Best practices: Partition and cluster tables, use approximate functions, control costs with quotas\n\n### Networking\n\n**VPC (Virtual Private Cloud)**\n- Global resource (subnets are regional)\n- Custom or auto mode networks\n- Firewall rules (stateful)\n- VPC peering and Shared VPC\n- Private Google Access for GCP services\n- Best practices: Use custom mode VPC, plan IP ranges, implement firewall rules\n\n**Cloud Load Balancing**\n- Global load balancing (HTTP(S), TCP/SSL Proxy, external TCP/UDP)\n- Regional load balancing (internal HTTP(S), internal TCP/UDP)\n- Anycast IP for global distribution\n- Backend services with health checks\n- Best practices: Use global for multi-region, enable CDN, configure health checks\n\n**Cloud CDN**\n- Global content delivery network\n- Cache invalidation and signed URLs\n- Integration with Cloud Storage and compute\n- Best practices: Enable compression, use cache-control headers\n\n**Cloud Interconnect and VPN**\n- Dedicated Interconnect (10 Gbps or 100 Gbps)\n- Partner Interconnect (50 Mbps to 50 Gbps)\n- Cloud VPN (HA VPN for 99.99% SLA)\n- Best practices: Use HA VPN for redundancy, Dedicated Interconnect for high bandwidth\n\n**Cloud Armor**\n- DDoS protection and WAF\n- Preconfigured and custom rules\n- Adaptive protection (ML-based)\n- Best practices: Enable for internet-facing services, use preconfigured rules\n\n**Private Service Connect**\n- Private connectivity to Google APIs and services\n- Service Directory for service discovery\n- Best practices: Use for all managed services in production\n\n### Serverless and Event-Driven\n\n**Pub/Sub**\n- Global message queue\n- At-least-once delivery\n- Push and pull subscriptions\n- Message ordering and filtering\n- Dead-letter topics\n- Best practices: Use message attributes for filtering, implement idempotent processing\n\n**Eventarc**\n- Event-driven architecture\n- Triggers for Cloud Run, Workflows, GKE\n- Sources: Audit Logs, Pub/Sub, custom events\n- Best practices: Use for decoupled architectures, implement event filtering\n\n**Cloud Scheduler**\n- Fully managed cron service\n- HTTP, Pub/Sub, and App Engine targets\n- Best practices: Use for periodic tasks, implement retry logic\n\n**Workflows**\n- Orchestrate and automate GCP and HTTP services\n- YAML-based workflow definition\n- Built-in error handling and retry\n- Best practices: Use for complex multi-step processes, implement compensating transactions\n\n### Security and Identity\n\n**Cloud IAM**\n- Resource hierarchy: Organization -> Folders -> Projects -> Resources\n- Roles: Primitive (Owner, Editor, Viewer), Predefined, Custom\n- Service accounts for applications\n- Workload Identity for GKE\n- Best practices: Use predefined roles, least privilege, service accounts for apps\n\n**Cloud Key Management (KMS)**\n- Encryption key management\n- Customer-managed encryption keys (CMEK)\n- Hardware Security Module (HSM) backed\n- Automatic key rotation\n- Best practices: Enable automatic rotation, use separate keys per environment\n\n**Secret Manager**\n- Store API keys, passwords, certificates\n- Versioning and access control\n- Automatic rotation integration\n- Best practices: Rotate secrets regularly, use IAM for access control\n\n**Security Command Center**\n- Centralized security and risk management\n- Asset discovery and vulnerability scanning\n- Threat detection and compliance monitoring\n- Best practices: Enable all detectors, review findings regularly\n\n**VPC Service Controls**\n- Create security perimeters around GCP resources\n- Prevent data exfiltration\n- Best practices: Use for sensitive data, implement access levels\n\n### AI and Machine Learning\n\n**Vertex AI**\n- Unified ML platform\n- AutoML for custom models\n- Pre-trained models (Vision, Natural Language, etc.)\n- MLOps with pipelines\n- Best practices: Use AutoML for quick start, implement feature store\n\n**BigQuery ML**\n- Create and execute ML models using SQL\n- Model types: Linear regression, logistic regression, clustering, etc.\n- Integration with Vertex AI\n- Best practices: Use for simple models, leverage BigQuery's scale\n\n## Architecture Patterns\n\n### High Availability\n\n**Multi-Zone Pattern**\n```\nGlobal HTTP(S) Load Balancer\n    |\n    v\nManaged Instance Group (multi-zone)\n    |\n    v\nCloud SQL (regional, HA configuration)\n    |\n    v\nCloud Storage (multi-region)\n```\n\n**Multi-Region Pattern**\n```\nGlobal HTTP(S) Load Balancer\n    |\n    ├── Backend Service Region 1 (Cloud Run)\n    └── Backend Service Region 2 (Cloud Run)\n         |\n         v\n    Cloud Spanner (multi-region)\n```\n\n### Serverless Architecture\n\n**Event-Driven Pattern**\n```\nCloud Storage upload event\n    |\n    v\nPub/Sub topic\n    |\n    v\nCloud Functions (image processing)\n    |\n    v\nFirestore (metadata storage)\n```\n\n**API-First Pattern**\n```\nCloud Endpoints or API Gateway\n    |\n    v\nCloud Run (multiple services)\n    |\n    ├── Cloud SQL (transactional data)\n    └── Firestore (user data)\n```\n\n### Microservices on GKE\n\n**GKE with Service Mesh**\n```\nGlobal Load Balancer\n    |\n    v\nGKE Ingress\n    |\n    v\nAnthos Service Mesh (Istio)\n    |\n    v\nMicroservices (Cloud Spanner, Firestore, Memorystore)\n```\n\n### Data Analytics Platform\n\n```\nData Sources\n    |\n    v\nPub/Sub (streaming)\n    |\n    v\nDataflow (Apache Beam)\n    |\n    v\nBigQuery (data warehouse)\n    |\n    v\nLooker or Data Studio (visualization)\n```\n\n**Batch Processing**\n```\nCloud Storage (raw data)\n    |\n    v\nDataproc (Apache Spark)\n    |\n    v\nBigQuery (analytics)\n```\n\n## Landing Zone Design\n\n### Resource Hierarchy\n\n```\nOrganization\n├── Folders (by environment or team)\n│   ├── Production Folder\n│   │   ├── Project A\n│   │   └── Project B\n│   ├── Staging Folder\n│   └── Development Folder\n└── Shared Services Folder\n    ├── Networking Project (Shared VPC host)\n    ├── Security Project (KMS, Secret Manager)\n    └── Logging Project (centralized logs)\n```\n\n### Network Design\n\n**Shared VPC Pattern**\n```\nHost Project (networking team)\n├── Shared VPC\n│   ├── Subnet Production (region A)\n│   ├── Subnet Staging (region A)\n│   └── Subnet Development (region B)\n\nService Projects (application teams)\n├── Production Project (uses Production subnet)\n├── Staging Project (uses Staging subnet)\n└── Development Project (uses Development subnet)\n```\n\n**Hub-and-Spoke with VPN**\n```\nOn-premises Network\n    |\n    v\nCloud VPN / Interconnect\n    |\n    v\nHub VPC (shared services)\n    |\n    ├── Spoke VPC 1 (production workloads)\n    ├── Spoke VPC 2 (development workloads)\n    └── Spoke VPC 3 (analytics workloads)\n```\n\n### Governance\n\n**Organization Policies**\n- Restrict public IP assignment\n- Enforce uniform bucket-level access\n- Restrict VM external IP\n- Define allowed resource locations\n\n**IAM Strategy**\n- Use Google Groups for role assignments\n- Separate duties (network admin, security admin, etc.)\n- Service accounts per application\n- Workload Identity for GKE workloads\n\n**Logging and Monitoring**\n```\nAll Projects\n    |\n    v\nLog Router\n    |\n    ├── Cloud Logging (default sink)\n    ├── BigQuery (long-term analysis)\n    ├── Cloud Storage (archive)\n    └── Pub/Sub (real-time processing)\n```\n\n## Migration Strategies\n\n### Migrate to Virtual Machines\n\n**Tools**\n- Migrate to Virtual Machines (formerly Migrate for Compute Engine)\n- Supports VMware, AWS, Azure, physical servers\n- Agentless or agent-based migration\n- Waves and test clones\n\n**Process**\n1. Assess: Fit assessment and TCO analysis\n2. Plan: Group VMs, define migration waves\n3. Deploy: Set up infrastructure (VPC, firewall rules)\n4. Migrate: Test migration, cutover, validation\n5. Optimize: Right-sizing, committed use discounts\n\n### Database Migration\n\n**Database Migration Service**\n- Minimal downtime migrations\n- Supports MySQL, PostgreSQL, SQL Server, Oracle\n- Continuous replication for cutover flexibility\n\n**Transfer Appliance**\n- Physical device for large data transfers\n- Up to 1 PB capacity\n- Offline data transfer\n\n## Cost Optimization\n\n### Compute Savings\n\n**Committed Use Discounts**\n- 1-year or 3-year commitments\n- Up to 57% savings for VMs\n- Resource-based or spend-based\n\n**Sustained Use Discounts**\n- Automatic discounts for running VMs >25% of month\n- Up to 30% savings\n- No commitment required\n\n**Preemptible and Spot VMs**\n- Up to 80% discount\n- Can be terminated by GCP\n- Best for batch processing, fault-tolerant workloads\n\n**Recommender**\n- VM rightsizing recommendations\n- Idle resource identification\n- Committed use discount recommendations\n\n### Storage Savings\n\n**Cloud Storage**\n- Autoclass for automatic tier transitions\n- Lifecycle policies (delete or transition)\n- Nearline (30+ days), Coldline (90+ days), Archive (365+ days)\n- Requester pays for data transfer\n\n**Persistent Disk**\n- Delete orphaned disks\n- Use balanced SSD instead of SSD when possible\n- Resize disks to match actual usage\n\n### BigQuery Savings\n\n**On-Demand Pricing**\n- $5 per TB processed\n- Use partitioning and clustering\n- Query cache for free repeated queries\n\n**Flat-Rate Pricing**\n- Predictable costs for heavy users\n- Autoscaling slots available\n- Flex slots for short-term commitments\n\n**Best Practices**\n- Use approximate aggregation functions (APPROX_COUNT_DISTINCT)\n- Avoid SELECT *, specify columns\n- Use materialized views for common queries\n- Set up cost controls with custom quotas\n\n### Monitoring Costs\n\n**Cloud Billing**\n- Budgets and alerts\n- Cost breakdown by project, service, SKU\n- Export to BigQuery for analysis\n- Recommendations from Active Assist\n\n## Disaster Recovery\n\n### Backup Strategies\n\n**VM Backups**\n- Persistent disk snapshots (incremental)\n- Machine images (include metadata and config)\n- Cross-region snapshot copy\n- Snapshot schedules for automation\n\n**Database Backups**\n- Cloud SQL: Automated backups (7-365 days retention)\n- Cloud Spanner: Backups on demand or scheduled\n- Firestore: Automated daily exports\n- Bigtable: Backups to Cloud Storage\n\n### High Availability\n\n**RTO/RPO Matrix**\n\n| Pattern | RPO | RTO | Cost |\n|---------|-----|-----|------|\n| Active-Active Multi-Region | Seconds | Seconds | High |\n| Active-Passive with Replication | Minutes | Minutes | Medium |\n| Warm Standby | Minutes | 10-30 min | Medium |\n| Backup and Restore | Hours | Hours | Low |\n\n**Cloud SQL HA**\n- Regional configuration with synchronous replication\n- Automatic failover\n- 99.95% SLA (vs 99.5% for single zone)\n\n**Cloud Spanner**\n- Multi-region configuration\n- 99.999% availability SLA\n- Synchronous replication across regions\n\n### Disaster Recovery Testing\n\n- Regular DR drills (quarterly recommended)\n- Document runbooks\n- Test restoration procedures\n- Measure actual RTO/RPO vs targets\n\n## Monitoring and Observability\n\n### Cloud Monitoring (formerly Stackdriver)\n\n**Metrics**\n- System metrics (CPU, memory, disk, network)\n- Custom metrics via Cloud Monitoring API\n- Metric scopes for multi-project monitoring\n- Uptime checks for availability\n\n**Dashboards and Charts**\n- Predefined dashboards for GCP services\n- Custom dashboards with filters and grouping\n- SLO monitoring with error budgets\n\n### Cloud Logging\n\n**Log Types**\n- Admin Activity logs (always enabled, no charge)\n- Data Access logs (must be enabled)\n- System Event logs\n- Access Transparency logs (for Google access)\n\n**Log Sinks**\n- Route logs to BigQuery, Cloud Storage, Pub/Sub\n- Aggregated sinks at organization/folder level\n- Exclusion filters to reduce costs\n\n### Cloud Trace\n\n**Distributed Tracing**\n- Automatic instrumentation for App Engine, Cloud Run, GKE\n- Manual instrumentation with client libraries\n- Latency analysis and performance insights\n- Integration with Zipkin\n\n### Cloud Profiler\n\n**Continuous Profiling**\n- CPU and memory profiling\n- Low overhead (< 0.5% CPU)\n- Flame graphs for visualization\n- Supported languages: Java, Go, Python, Node.js\n\n### Error Reporting\n\n**Aggregated Error Tracking**\n- Automatic error grouping\n- Stack trace analysis\n- Integration with Cloud Logging\n- Notifications for new errors\n","skills/cloud-architect/references/multi-cloud.md":"# Multi-Cloud Architecture Reference\n\nComprehensive guide for multi-cloud strategies, abstraction layers, portability patterns, and vendor lock-in mitigation.\n\n## Multi-Cloud Strategy\n\n### When to Use Multi-Cloud\n\n**Valid Use Cases**\n- Regulatory compliance requiring data residency in specific regions\n- Best-of-breed service selection (BigQuery for analytics, AWS for ML)\n- Acquisition integration (different clouds in merged organizations)\n- Disaster recovery with cloud provider as failure domain\n- Negotiating leverage with cloud vendors\n\n**Poor Reasons for Multi-Cloud**\n- \"Avoiding vendor lock-in\" without specific exit scenario\n- Assuming portability is free (it has significant costs)\n- Political decisions without technical justification\n- Spreading workloads arbitrarily across providers\n\n### Multi-Cloud Patterns\n\n**Active-Active**\n```\nUsers -> Global Load Balancer\n              |\n    +---------+---------+\n    |                   |\n  AWS Region        GCP Region\n    |                   |\n    +----> Data Sync <--+\n```\n- Highest complexity and cost\n- Best for global latency optimization\n- Requires robust data synchronization\n\n**Active-Passive (DR)**\n```\nUsers -> Primary (AWS)\n              |\n         [Failover]\n              |\n         Secondary (Azure)\n```\n- Lower complexity than active-active\n- Cloud provider becomes failure domain\n- Cold or warm standby in secondary cloud\n\n**Segmented by Workload**\n```\nAnalytics -> GCP (BigQuery)\nCore App  -> AWS (ECS, RDS)\nOffice    -> Azure (M365 integration)\n```\n- Each workload on best-fit cloud\n- No cross-cloud data synchronization\n- Simplest multi-cloud pattern\n\n## Abstraction Layers\n\n### Infrastructure Abstraction\n\n**Terraform (Recommended)**\n```hcl\n# Provider-agnostic module structure\nmodule \"compute\" {\n  source = \"./modules/compute\"\n\n  provider_type = var.cloud_provider  # aws, azure, gcp\n  instance_type = var.instance_size\n  region        = var.region\n}\n\n# Provider-specific implementations\n# modules/compute/aws.tf\nresource \"aws_instance\" \"main\" {\n  count         = var.provider_type == \"aws\" ? 1 : 0\n  ami           = data.aws_ami.latest.id\n  instance_type = local.aws_instance_map[var.instance_size]\n}\n\n# modules/compute/azure.tf\nresource \"azurerm_virtual_machine\" \"main\" {\n  count    = var.provider_type == \"azure\" ? 1 : 0\n  vm_size  = local.azure_vm_map[var.instance_size]\n}\n```\n\n**Pulumi (Code-First)**\n```typescript\n// Abstract cloud resources with TypeScript\ninterface ComputeConfig {\n  size: \"small\" | \"medium\" | \"large\";\n  region: string;\n}\n\nfunction createCompute(config: ComputeConfig, provider: \"aws\" | \"gcp\") {\n  if (provider === \"aws\") {\n    return new aws.ec2.Instance(\"web\", {\n      instanceType: sizeMap.aws[config.size],\n      // ...\n    });\n  } else {\n    return new gcp.compute.Instance(\"web\", {\n      machineType: sizeMap.gcp[config.size],\n      // ...\n    });\n  }\n}\n```\n\n### Container Orchestration (Kubernetes)\n\n**Portable Kubernetes Deployment**\n```yaml\n# Same manifests work across EKS, AKS, GKE\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: web-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: web\n  template:\n    spec:\n      containers:\n      - name: web\n        image: myregistry/web:v1\n        resources:\n          requests:\n            memory: \"256Mi\"\n            cpu: \"250m\"\n          limits:\n            memory: \"512Mi\"\n            cpu: \"500m\"\n```\n\n**Cloud-Specific Considerations**\n| Feature | EKS | AKS | GKE |\n|---------|-----|-----|-----|\n| Load Balancer | ALB/NLB annotations | Azure LB | GCP LB |\n| Storage Class | gp3, io2 | managed-premium | pd-ssd |\n| IAM Integration | IRSA | Workload Identity | Workload Identity |\n| Ingress | AWS ALB Controller | AGIC | GKE Ingress |\n\n### Application Abstraction\n\n**Database Abstraction**\n```python\n# Use standard protocols (SQL, Redis, S3 API)\nfrom sqlalchemy import create_engine\n\n# Same code works with:\n# - AWS RDS PostgreSQL\n# - Azure Database for PostgreSQL\n# - GCP Cloud SQL PostgreSQL\n# - Self-managed PostgreSQL\n\nDATABASE_URL = os.environ[\"DATABASE_URL\"]\nengine = create_engine(DATABASE_URL)\n```\n\n**Object Storage Abstraction**\n```python\nimport boto3\nfrom botocore.config import Config\n\n# S3-compatible API works with:\n# - AWS S3\n# - GCP Cloud Storage (interoperability mode)\n# - MinIO\n# - Cloudflare R2\n\ns3_client = boto3.client(\n    's3',\n    endpoint_url=os.environ.get(\"S3_ENDPOINT\"),  # Override for non-AWS\n    aws_access_key_id=os.environ[\"ACCESS_KEY\"],\n    aws_secret_access_key=os.environ[\"SECRET_KEY\"],\n)\n```\n\n## Data Synchronization\n\n### Database Replication\n\n**Cross-Cloud PostgreSQL**\n```\nAWS RDS Primary\n      |\n      | Logical Replication\n      v\nGCP Cloud SQL Replica (read-only)\n```\n\nConfiguration:\n```sql\n-- On primary (AWS RDS)\nCREATE PUBLICATION my_publication FOR ALL TABLES;\n\n-- On replica (GCP Cloud SQL)\nCREATE SUBSCRIPTION my_subscription\n  CONNECTION 'host=aws-rds-endpoint dbname=mydb user=repl'\n  PUBLICATION my_publication;\n```\n\n**Conflict Resolution Strategies**\n- Last-write-wins (timestamp-based)\n- Application-level conflict resolution\n- CRDT data structures for eventually consistent data\n- Avoid multi-master for transactional data\n\n### Object Storage Sync\n\n**Rclone for Cross-Cloud Sync**\n```bash\n# Sync S3 to GCS\nrclone sync s3:my-bucket gcs:my-bucket \\\n  --transfers 32 \\\n  --checkers 16 \\\n  --s3-upload-concurrency 8\n\n# Bidirectional sync with conflict handling\nrclone bisync s3:bucket gcs:bucket \\\n  --conflict-resolve newer\n```\n\n**Event-Driven Replication**\n```\nS3 Bucket -> S3 Event -> Lambda -> GCS Upload\n                              |\n                              v\n                       Consistency Check\n```\n\n## Vendor Lock-In Mitigation\n\n### Lock-In Risk Assessment\n\n| Service Type | Lock-In Risk | Mitigation Strategy |\n|--------------|--------------|---------------------|\n| Compute (VMs) | Low | Standard OS images, IaC |\n| Kubernetes | Low | Portable manifests, avoid proprietary add-ons |\n| Object Storage | Low | S3-compatible API, standard formats |\n| Managed Databases | Medium | Standard SQL, logical backups |\n| Serverless Functions | High | Abstraction layers, containers |\n| Proprietary AI/ML | High | Open-source alternatives, ONNX models |\n| Managed Services | High | Evaluate portability before adoption |\n\n### Mitigation Strategies\n\n**1. Use Open Standards**\n- SQL databases over proprietary NoSQL\n- Kubernetes over ECS/Cloud Run\n- S3 API for object storage\n- OpenTelemetry for observability\n- OIDC for authentication\n\n**2. Abstract Proprietary Services**\n```typescript\n// Wrap cloud-specific services\ninterface QueueService {\n  send(message: string): Promise<void>;\n  receive(): Promise<string>;\n}\n\nclass SQSQueue implements QueueService {\n  async send(message: string) {\n    await this.sqsClient.sendMessage({ QueueUrl: this.url, MessageBody: message });\n  }\n}\n\nclass PubSubQueue implements QueueService {\n  async send(message: string) {\n    await this.pubsubClient.topic(this.topic).publish(Buffer.from(message));\n  }\n}\n\n// Factory pattern for cloud selection\nfunction createQueue(provider: string): QueueService {\n  switch (provider) {\n    case \"aws\": return new SQSQueue();\n    case \"gcp\": return new PubSubQueue();\n  }\n}\n```\n\n**3. Maintain Exit Capability**\n- Regular data export testing\n- Document cloud-specific dependencies\n- Keep IaC portable across providers\n- Estimate migration effort annually\n\n**4. Containerize Everything**\n```dockerfile\n# Portable container runs anywhere\nFROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\nEXPOSE 3000\nCMD [\"node\", \"server.js\"]\n```\n\n## Network Connectivity\n\n### Cross-Cloud Networking\n\n**VPN Interconnect**\n```\nAWS VPC                          GCP VPC\n   |                                |\n   +---> AWS VPN Gateway            |\n              |                     |\n              | IPsec tunnel        |\n              |                     |\n              +---> GCP Cloud VPN <-+\n```\n\n**Dedicated Interconnect (Enterprise)**\n```\nOn-Premises Data Center\n         |\n    +----+----+\n    |         |\nAWS Direct  GCP Cloud\nConnect     Interconnect\n    |         |\n    v         v\nAWS VPC    GCP VPC\n    |         |\n    +----+----+\n         |\n    Transit Hub (e.g., Megaport, Equinix)\n```\n\n### Service Mesh Across Clouds\n\n**Istio Multi-Cluster**\n```yaml\n# Primary cluster (AWS EKS)\napiVersion: install.istio.io/v1alpha1\nkind: IstioOperator\nspec:\n  values:\n    global:\n      meshID: multi-cloud-mesh\n      multiCluster:\n        clusterName: eks-primary\n      network: aws-network\n\n# Remote cluster (GCP GKE)\nspec:\n  values:\n    global:\n      meshID: multi-cloud-mesh\n      multiCluster:\n        clusterName: gke-secondary\n      network: gcp-network\n```\n\n## Cost Management\n\n### Cross-Cloud Cost Visibility\n\n**FinOps Tools**\n- CloudHealth by VMware\n- Apptio Cloudability\n- Spot.io (now part of NetApp)\n- Kubecost for Kubernetes\n\n**Unified Tagging Strategy**\n```\nRequired Tags (all clouds):\n- environment: prod/staging/dev\n- cost-center: engineering/marketing/sales\n- owner: team-name\n- project: project-code\n- managed-by: terraform/manual\n```\n\n### Cost Comparison Framework\n\n```\n| Workload Type | AWS | Azure | GCP | Decision |\n|---------------|-----|-------|-----|----------|\n| General Compute | EC2 m5 | D-series | n2-standard | Compare $/vCPU/hour |\n| GPU Training | p4d | NC-series | A2 | GCP often cheaper |\n| Object Storage | S3 | Blob | GCS | Similar, check egress |\n| Analytics | Redshift | Synapse | BigQuery | BigQuery for ad-hoc |\n| Kubernetes | EKS | AKS | GKE | GKE Autopilot simplest |\n```\n\n## Observability\n\n### Unified Monitoring Stack\n\n**OpenTelemetry Collector**\n```yaml\n# Collect from all clouds, export to single backend\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n      http:\n\nprocessors:\n  batch:\n  attributes:\n    actions:\n      - key: cloud.provider\n        action: upsert\n        value: ${CLOUD_PROVIDER}\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n  jaeger:\n    endpoint: jaeger:14250\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch, attributes]\n      exporters: [jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch, attributes]\n      exporters: [prometheus]\n```\n\n**Grafana for Unified Dashboards**\n- AWS CloudWatch data source\n- Azure Monitor data source\n- GCP Cloud Monitoring data source\n- Single pane of glass across all clouds\n\n## Security Considerations\n\n### Identity Federation\n\n**Cross-Cloud Identity**\n```\nCorporate IdP (Okta/Azure AD)\n         |\n    SAML/OIDC\n         |\n    +----+----+----+\n    |    |    |    |\n  AWS  Azure  GCP  K8s\n  IAM   AD   IAM  RBAC\n```\n\n### Secrets Management\n\n**HashiCorp Vault (Cloud-Agnostic)**\n```hcl\n# Single secrets management across clouds\nresource \"vault_aws_secret_backend_role\" \"aws_role\" {\n  backend = vault_aws_secret_backend.aws.path\n  name    = \"app-role\"\n  credential_type = \"iam_user\"\n}\n\nresource \"vault_gcp_secret_roleset\" \"gcp_role\" {\n  backend     = vault_gcp_secret_backend.gcp.path\n  roleset     = \"app-role\"\n  project     = var.gcp_project\n  token_scopes = [\"https://www.googleapis.com/auth/cloud-platform\"]\n}\n```\n\n### Network Security\n\n**Zero-Trust Across Clouds**\n- mTLS between all services (service mesh)\n- No implicit trust based on network location\n- Identity-based access control\n- Encrypted transit between clouds (VPN/interconnect)\n","skills/code-documenter/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-documenter%2FSKILL.md -->","skills/code-documenter/references/api-docs-fastapi-django.md":"# API Documentation: FastAPI & Django\n\n## FastAPI (Auto-generates from types)\n\nFastAPI automatically generates OpenAPI documentation from type hints and docstrings.\n\n### Endpoint Documentation\n\n```python\nfrom fastapi import FastAPI, HTTPException, status\nfrom pydantic import BaseModel, Field\n\nclass UserCreate(BaseModel):\n    \"\"\"User creation request body.\"\"\"\n\n    name: str = Field(..., min_length=1, max_length=100, example=\"John Doe\")\n    email: str = Field(..., example=\"john@example.com\")\n\nclass UserResponse(BaseModel):\n    \"\"\"User response with generated ID.\"\"\"\n\n    id: int = Field(..., example=1)\n    name: str\n    email: str\n\n@app.post(\n    \"/users\",\n    response_model=UserResponse,\n    status_code=status.HTTP_201_CREATED,\n    summary=\"Create a new user\",\n    tags=[\"Users\"],\n)\nasync def create_user(user: UserCreate) -> UserResponse:\n    \"\"\"Create a new user account.\n\n    Args:\n        user: User creation data including name and email.\n\n    Returns:\n        Created user with generated ID.\n\n    Raises:\n        HTTPException: 400 if email already exists.\n    \"\"\"\n```\n\n### Router with Tags\n\n```python\nfrom fastapi import APIRouter\n\nrouter = APIRouter(\n    prefix=\"/users\",\n    tags=[\"Users\"],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n@router.get(\n    \"/{user_id}\",\n    response_model=UserResponse,\n    summary=\"Get user by ID\",\n)\nasync def get_user(user_id: int) -> UserResponse:\n    \"\"\"Retrieve a user by their unique identifier.\"\"\"\n```\n\n## Django REST Framework (drf-spectacular)\n\n### ViewSet Documentation\n\n```python\nfrom rest_framework import viewsets, status\nfrom rest_framework.decorators import action\nfrom drf_spectacular.utils import extend_schema, OpenApiParameter\n\nclass UserViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    ViewSet for managing user accounts.\n\n    list: Get all users with pagination.\n    create: Create a new user account.\n    retrieve: Get a specific user by ID.\n    update: Update all user fields.\n    partial_update: Update specific user fields.\n    destroy: Delete a user account.\n    \"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n    @extend_schema(\n        summary=\"Get current user\",\n        description=\"Returns the authenticated user's profile\",\n        responses={200: UserSerializer},\n    )\n    @action(detail=False, methods=[\"get\"])\n    def me(self, request):\n        \"\"\"Get the authenticated user's profile.\"\"\"\n        serializer = self.get_serializer(request.user)\n        return Response(serializer.data)\n```\n\n### Serializer Documentation\n\n```python\nfrom rest_framework import serializers\n\nclass UserSerializer(serializers.ModelSerializer):\n    \"\"\"Serializer for user model with validation.\"\"\"\n\n    class Meta:\n        model = User\n        fields = [\"id\", \"name\", \"email\", \"created_at\"]\n        read_only_fields = [\"id\", \"created_at\"]\n\n    name = serializers.CharField(\n        help_text=\"User's display name\",\n        max_length=100,\n    )\n    email = serializers.EmailField(\n        help_text=\"User's email address (unique)\",\n    )\n```\n\n### Custom Schema\n\n```python\nfrom drf_spectacular.utils import extend_schema, OpenApiExample\n\n@extend_schema(\n    request=UserCreateSerializer,\n    responses={\n        201: UserSerializer,\n        400: OpenApiTypes.OBJECT,\n    },\n    examples=[\n        OpenApiExample(\n            \"Valid request\",\n            value={\"name\": \"John\", \"email\": \"john@example.com\"},\n        ),\n    ],\n)\ndef create(self, request):\n    \"\"\"Create a new user.\"\"\"\n```\n\n## Quick Reference\n\n| Framework | Documentation Source | Output |\n|-----------|---------------------|--------|\n| FastAPI | Type hints + docstrings | Auto Swagger UI |\n| DRF | Serializers + drf-spectacular | Auto Swagger UI |\n\n| FastAPI Decorator | Purpose |\n|-------------------|---------|\n| `summary` | Short endpoint description |\n| `description` | Detailed description |\n| `tags` | Group endpoints |\n| `response_model` | Response schema |\n| `responses` | Additional response codes |\n\n| DRF Decorator | Purpose |\n|---------------|---------|\n| `@extend_schema` | Customize schema |\n| `OpenApiParameter` | Query/path params |\n| `OpenApiExample` | Request examples |\n","skills/code-documenter/references/api-docs-nestjs-express.md":"# API Documentation: NestJS & Express\n\n## NestJS (@nestjs/swagger)\n\nNestJS requires explicit decorators for OpenAPI documentation.\n\n### Controller Documentation\n\n```typescript\nimport { Controller, Post, Body, Get, Param } from '@nestjs/common';\nimport {\n  ApiTags,\n  ApiOperation,\n  ApiResponse,\n  ApiParam,\n  ApiBearerAuth,\n} from '@nestjs/swagger';\n\n@ApiTags('Users')\n@ApiBearerAuth()\n@Controller('users')\nexport class UsersController {\n  @Post()\n  @ApiOperation({ summary: 'Create a new user' })\n  @ApiResponse({\n    status: 201,\n    description: 'User created successfully',\n    type: UserDto,\n  })\n  @ApiResponse({\n    status: 400,\n    description: 'Invalid input data',\n  })\n  async create(@Body() dto: CreateUserDto): Promise<UserDto> {\n    return this.usersService.create(dto);\n  }\n\n  @Get(':id')\n  @ApiOperation({ summary: 'Get user by ID' })\n  @ApiParam({\n    name: 'id',\n    description: 'User unique identifier',\n    example: '123',\n  })\n  @ApiResponse({ status: 200, type: UserDto })\n  @ApiResponse({ status: 404, description: 'User not found' })\n  async findOne(@Param('id') id: string): Promise<UserDto> {\n    return this.usersService.findOne(id);\n  }\n}\n```\n\n### DTO Documentation\n\n```typescript\nimport { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsEmail, IsString, MinLength } from 'class-validator';\n\nexport class CreateUserDto {\n  @ApiProperty({\n    description: \"User's display name\",\n    example: 'John Doe',\n    minLength: 1,\n    maxLength: 100,\n  })\n  @IsString()\n  @MinLength(1)\n  name: string;\n\n  @ApiProperty({\n    description: \"User's email address\",\n    example: 'john@example.com',\n  })\n  @IsEmail()\n  email: string;\n\n  @ApiPropertyOptional({\n    description: 'Profile picture URL',\n    example: 'https://example.com/avatar.jpg',\n  })\n  avatarUrl?: string;\n}\n```\n\n## Express (swagger-jsdoc)\n\nExpress uses JSDoc comments with swagger annotations.\n\n### Setup\n\n```javascript\nconst swaggerJsdoc = require('swagger-jsdoc');\nconst swaggerUi = require('swagger-ui-express');\n\nconst options = {\n  definition: {\n    openapi: '3.0.0',\n    info: {\n      title: 'API Documentation',\n      version: '1.0.0',\n    },\n  },\n  apis: ['./routes/*.js'],\n};\n\nconst specs = swaggerJsdoc(options);\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));\n```\n\n### Route Documentation\n\n```javascript\n/**\n * @swagger\n * /users:\n *   post:\n *     summary: Create a new user\n *     tags: [Users]\n *     requestBody:\n *       required: true\n *       content:\n *         application/json:\n *           schema:\n *             $ref: '#/components/schemas/CreateUser'\n *     responses:\n *       201:\n *         description: User created successfully\n *         content:\n *           application/json:\n *             schema:\n *               $ref: '#/components/schemas/User'\n *       400:\n *         description: Invalid input\n */\nrouter.post('/users', createUser);\n\n/**\n * @swagger\n * /users/{id}:\n *   get:\n *     summary: Get user by ID\n *     tags: [Users]\n *     parameters:\n *       - in: path\n *         name: id\n *         required: true\n *         schema:\n *           type: string\n *         description: User ID\n *     responses:\n *       200:\n *         description: User found\n *         content:\n *           application/json:\n *             schema:\n *               $ref: '#/components/schemas/User'\n *       404:\n *         description: User not found\n */\nrouter.get('/users/:id', getUser);\n```\n\n### Schema Documentation\n\n```javascript\n/**\n * @swagger\n * components:\n *   schemas:\n *     CreateUser:\n *       type: object\n *       required:\n *         - name\n *         - email\n *       properties:\n *         name:\n *           type: string\n *           description: User's display name\n *           example: John Doe\n *         email:\n *           type: string\n *           format: email\n *           description: User's email address\n *           example: john@example.com\n *     User:\n *       allOf:\n *         - $ref: '#/components/schemas/CreateUser'\n *         - type: object\n *           properties:\n *             id:\n *               type: string\n *               description: Unique identifier\n *             createdAt:\n *               type: string\n *               format: date-time\n */\n```\n\n## Quick Reference\n\n| NestJS Decorator | Purpose |\n|------------------|---------|\n| `@ApiTags()` | Group endpoints |\n| `@ApiOperation()` | Endpoint summary |\n| `@ApiResponse()` | Response documentation |\n| `@ApiParam()` | Path parameter |\n| `@ApiQuery()` | Query parameter |\n| `@ApiBody()` | Request body |\n| `@ApiBearerAuth()` | Auth requirement |\n| `@ApiProperty()` | DTO property |\n\n| Express swagger-jsdoc | Purpose |\n|-----------------------|---------|\n| `@swagger` | Start swagger block |\n| `tags` | Group endpoints |\n| `summary` | Short description |\n| `parameters` | Path/query params |\n| `requestBody` | Request body schema |\n| `responses` | Response schemas |\n| `$ref` | Reference schema |\n","skills/code-documenter/references/coverage-reports.md":"# Coverage Reports\n\n## Documentation Coverage Report Template\n\n```markdown\n# Documentation Report: {project_name}\n\n## Summary\n- **Files analyzed**: 45\n- **Functions documented**: 120/150 (80%)\n- **Classes documented**: 25/25 (100%)\n- **API endpoints documented**: 30/30 (100%)\n\n## Coverage Before/After\n- Before: 45%\n- After: 92%\n\n## Files Modified\n\n| File | Functions Added | Notes |\n|------|-----------------|-------|\n| src/services/user.ts | 8 | All public methods |\n| src/services/auth.ts | 5 | Added examples |\n| src/controllers/users.ts | 6 | Added @Api decorators |\n| src/dto/user.dto.ts | 4 | Added @ApiProperty |\n\n## API Documentation\n\n- **Framework**: NestJS\n- **Strategy**: @nestjs/swagger decorators\n- **Swagger UI**: /api/docs\n- **OpenAPI spec**: /api-json\n\n## Documentation Style\n\n- **Python**: Google style docstrings\n- **TypeScript**: JSDoc with @param, @returns\n- **API**: OpenAPI 3.0 via decorators\n\n## Next Steps\n\n### Recommendations\n1. Run `npm run docs:lint` to validate JSDoc\n2. Add `eslint-plugin-jsdoc` to enforce documentation\n3. Consider adding examples for complex functions\n4. Set up documentation CI checks\n\n### Missing Documentation\n| File | Missing | Priority |\n|------|---------|----------|\n| src/utils/crypto.ts | 3 functions | High |\n| src/helpers/date.ts | 2 functions | Medium |\n\n### CI Integration\n```yaml\n# Add to CI pipeline\n- name: Check documentation\n  run: npm run docs:check\n\n- name: Generate API docs\n  run: npm run docs:generate\n```\n```\n\n## Checklist During Documentation\n\n```markdown\n## Documentation Checklist\n\n### Before Starting\n- [ ] Confirmed format preference (Google/JSDoc/etc.)\n- [ ] Identified files to exclude (tests, generated)\n- [ ] Detected framework for API docs\n\n### Functions/Methods\n- [ ] All public functions documented\n- [ ] Parameters described with types\n- [ ] Return values documented\n- [ ] Exceptions/errors documented\n- [ ] Examples added for complex functions\n\n### Classes\n- [ ] Class purpose described\n- [ ] Constructor parameters documented\n- [ ] Public methods documented\n- [ ] Important attributes explained\n\n### API Endpoints\n- [ ] All endpoints have summaries\n- [ ] Request bodies documented\n- [ ] Response schemas defined\n- [ ] Error responses documented\n- [ ] Authentication requirements noted\n\n### Final Checks\n- [ ] Ran documentation linter\n- [ ] Verified Swagger UI renders correctly\n- [ ] No inaccurate documentation\n- [ ] Coverage report generated\n```\n\n## Framework-Specific Linting\n\n```bash\n# JavaScript/TypeScript - ESLint\nnpm install eslint-plugin-jsdoc --save-dev\n# Add to .eslintrc: \"plugins\": [\"jsdoc\"]\n\n# Python - pydocstyle\npip install pydocstyle\npydocstyle --convention=google src/\n\n# Python - interrogate (coverage)\npip install interrogate\ninterrogate -v src/\n```\n\n## Quick Reference\n\n| Metric | Good | Acceptable | Poor |\n|--------|------|------------|------|\n| Function coverage | >90% | 70-90% | <70% |\n| Class coverage | 100% | >90% | <90% |\n| API endpoint coverage | 100% | 100% | <100% |\n| Example coverage | >50% | 30-50% | <30% |\n","skills/code-documenter/references/documentation-systems.md":"# Documentation Systems & Infrastructure\n\n## Static Site Generators\n\n### Docusaurus (Meta)\n\n```bash\n# Setup\nnpx create-docusaurus@latest docs classic\ncd docs && npm start\n\n# Structure\ndocs/\n├── docs/           # Documentation pages\n├── blog/           # Blog posts\n├── src/\n│   └── pages/      # Custom pages\n└── docusaurus.config.js\n```\n\n**docusaurus.config.js:**\n```javascript\nmodule.exports = {\n  title: 'My API',\n  tagline: 'Build amazing things',\n  url: 'https://docs.example.com',\n  baseUrl: '/',\n\n  themeConfig: {\n    navbar: {\n      items: [\n        {to: '/docs/intro', label: 'Docs', position: 'left'},\n        {to: '/api', label: 'API', position: 'left'},\n      ],\n    },\n\n    // Algolia search\n    algolia: {\n      apiKey: '<algolia-search-api-key>',\n      indexName: 'your_index',\n      contextualSearch: true,\n    },\n\n    prism: {\n      theme: lightCodeTheme,\n      darkTheme: darkCodeTheme,\n      additionalLanguages: ['python', 'rust'],\n    },\n  },\n};\n```\n\n### MkDocs (Python)\n\n```yaml\n# mkdocs.yml\nsite_name: My API Documentation\ntheme:\n  name: material\n  features:\n    - navigation.tabs\n    - navigation.sections\n    - toc.integrate\n    - search.suggest\n    - search.highlight\n  palette:\n    - scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n\nplugins:\n  - search\n  - mkdocstrings:\n      handlers:\n        python:\n          options:\n            show_source: true\n  - git-revision-date-localized\n\nmarkdown_extensions:\n  - pymdownx.highlight\n  - pymdownx.superfences\n  - admonition\n  - codehilite\n\nnav:\n  - Home: index.md\n  - Getting Started: getting-started.md\n  - API Reference: api/\n```\n\n### VitePress (Vue)\n\n```typescript\n// .vitepress/config.ts\nexport default defineConfig({\n  title: 'API Docs',\n  description: 'Developer documentation',\n\n  themeConfig: {\n    nav: [\n      { text: 'Guide', link: '/guide/' },\n      { text: 'API', link: '/api/' },\n    ],\n\n    sidebar: {\n      '/guide/': [\n        {\n          text: 'Introduction',\n          items: [\n            { text: 'Getting Started', link: '/guide/getting-started' },\n            { text: 'Configuration', link: '/guide/config' },\n          ],\n        },\n      ],\n    },\n\n    search: {\n      provider: 'local',\n    },\n\n    editLink: {\n      pattern: 'https://github.com/user/repo/edit/main/docs/:path',\n    },\n  },\n});\n```\n\n## Multi-Version Documentation\n\n### Version Switcher\n\n```javascript\n// Docusaurus versions\n{\n  versions: {\n    current: {\n      label: '2.0 (Next)',\n      path: 'next',\n    },\n  },\n  onlyIncludeVersions: ['current', '1.5', '1.4'],\n}\n```\n\n### Migration Guides\n\n```markdown\n# Migration Guide: v1 to v2\n\n## Breaking Changes\n\n### Authentication\n**v1:**\n```python\nclient.authenticate(api_key)\n```\n\n**v2:**\n```python\nclient = Client(api_key=api_key)  # Pass in constructor\n```\n\n### Renamed Methods\n| v1 | v2 | Notes |\n|----|----|----- |\n| `get_user()` | `fetch_user()` | Async now |\n| `delete_user()` | `remove_user()` | Returns Promise |\n\n## Deprecation Timeline\n- v1.x: Supported until Dec 2025\n- v2.0: Released Jan 2025\n- v2.1: Current (June 2025)\n```\n\n## Search Implementation\n\n### Algolia DocSearch\n\n```html\n<!-- Add to theme -->\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@docsearch/css@3\" />\n\n<script src=\"https://cdn.jsdelivr.net/npm/@docsearch/js@3\"></script>\n<script>\n  docsearch({\n    appId: '<algolia-app-id>',\n    apiKey: '<algolia-search-api-key>',\n    indexName: 'your_index',\n    container: '#docsearch',\n  });\n</script>\n```\n\n### Local Search (Lunr.js)\n\n```javascript\nconst idx = lunr(function() {\n  this.ref('id');\n  this.field('title', { boost: 10 });\n  this.field('content');\n\n  documents.forEach(doc => this.add(doc));\n});\n\n// Search\nconst results = idx.search('authentication');\n```\n\n## Documentation Testing\n\n### Link Checking\n\n```bash\n# linkcheck (Python)\npip install linkchecker\nlinkchecker http://localhost:3000/docs\n\n# broken-link-checker (Node)\nnpm install -g broken-link-checker\nblc http://localhost:3000 -ro\n```\n\n### Code Example Testing\n\n```python\n# doctest for Python examples\n\"\"\"\n>>> add(2, 3)\n5\n>>> add(-1, 1)\n0\n\"\"\"\n\n# Run tests\npython -m doctest -v docs/*.md\n```\n\n```javascript\n// Jest for TypeScript examples\n// Extract code blocks and test\nimport { runExamples } from './test-docs';\n\ntest('API examples work', async () => {\n  const examples = extractExamples('./docs/api.md');\n  await expect(runExamples(examples)).resolves.toBeTruthy();\n});\n```\n\n## Performance Optimization\n\n### Build Optimization\n\n```javascript\n// Webpack/Vite config\nexport default {\n  build: {\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          'vendor': ['react', 'react-dom'],\n        },\n      },\n    },\n  },\n\n  optimizeDeps: {\n    include: ['prismjs'],\n  },\n};\n```\n\n### CDN & Caching\n\n```nginx\n# nginx.conf\nlocation /docs {\n  expires 1y;\n  add_header Cache-Control \"public, immutable\";\n}\n\nlocation ~* \\.(html)$ {\n  expires 1h;\n  add_header Cache-Control \"public, must-revalidate\";\n}\n```\n\n## Analytics Integration\n\n### Google Analytics\n\n```javascript\n// Docusaurus\ngtag: {\n  trackingID: 'G-XXXXXXXXXX',\n  anonymizeIP: true,\n},\n```\n\n### Custom Analytics\n\n```javascript\n// Track search queries\nfunction trackSearch(query, results) {\n  analytics.track('docs_search', {\n    query,\n    resultCount: results.length,\n    timestamp: new Date(),\n  });\n}\n```\n\n## Quick Reference\n\n| Tool | Best For | Tech Stack |\n|------|----------|-----------|\n| Docusaurus | React projects, versioning | React, MDX |\n| MkDocs | Python projects, simple setup | Python, Jinja2 |\n| VitePress | Vue projects, fast builds | Vue, Vite |\n| Nextra | Next.js integration | React, Next.js |\n| Mintlify | Modern UI, AI search | React |\n\n| Search Solution | Cost | Features |\n|----------------|------|----------|\n| Algolia DocSearch | Free (OSS) | Fast, typo-tolerant |\n| Local (Lunr.js) | Free | Offline, no server |\n| Typesense | Free (self-host) | Privacy-focused |\n| Meilisearch | Free (self-host) | Fast, relevance |\n","skills/code-documenter/references/interactive-api-docs.md":"# Interactive API Documentation\n\n## OpenAPI 3.1 Advanced Features\n\n### Reusable Components\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: Users API\n  version: 2.0.0\n\ncomponents:\n  # Reusable schemas\n  schemas:\n    User:\n      type: object\n      required: [id, email]\n      properties:\n        id:\n          type: string\n          format: uuid\n          example: \"123e4567-e89b-12d3-a456-426614174000\"\n        email:\n          type: string\n          format: email\n          example: \"user@example.com\"\n\n    Error:\n      type: object\n      properties:\n        code:\n          type: string\n        message:\n          type: string\n        details:\n          type: object\n\n    PaginatedResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items: {}\n        total:\n          type: integer\n        page:\n          type: integer\n\n  # Reusable parameters\n  parameters:\n    PageParam:\n      name: page\n      in: query\n      schema:\n        type: integer\n        default: 1\n        minimum: 1\n\n    LimitParam:\n      name: limit\n      in: query\n      schema:\n        type: integer\n        default: 20\n        minimum: 1\n        maximum: 100\n\n  # Security schemes\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\n    ApiKeyAuth:\n      type: apiKey\n      in: header\n      name: X-API-Key\n\n    OAuth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://api.example.com/oauth/authorize\n          tokenUrl: https://api.example.com/oauth/token\n          scopes:\n            read:users: Read user data\n            write:users: Modify user data\n\n  # Reusable responses\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    Unauthorized:\n      description: Authentication required\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\npaths:\n  /users:\n    get:\n      summary: List users\n      parameters:\n        - $ref: '#/components/parameters/PageParam'\n        - $ref: '#/components/parameters/LimitParam'\n      security:\n        - BearerAuth: []\n      responses:\n        '200':\n          description: Success\n          content:\n            application/json:\n              schema:\n                allOf:\n                  - $ref: '#/components/schemas/PaginatedResponse'\n                  - type: object\n                    properties:\n                      data:\n                        type: array\n                        items:\n                          $ref: '#/components/schemas/User'\n```\n\n## Interactive Documentation Portals\n\n### Swagger UI Customization\n\n```javascript\n// Custom Swagger UI\nconst swaggerUi = require('swagger-ui-express');\nconst swaggerDocument = require('./openapi.json');\n\nconst options = {\n  customCss: '.swagger-ui .topbar { display: none }',\n  customSiteTitle: \"API Docs\",\n  customfavIcon: \"/favicon.ico\",\n  swaggerOptions: {\n    persistAuthorization: true,\n    displayRequestDuration: true,\n    filter: true,\n    tryItOutEnabled: true,\n    requestInterceptor: (req) => {\n      req.headers['X-Custom-Header'] = 'value';\n      return req;\n    },\n  },\n};\n\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));\n```\n\n### Redoc (Modern Alternative)\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <title>API Documentation</title>\n  <link href=\"https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700\" rel=\"stylesheet\">\n</head>\n<body>\n  <redoc spec-url='./openapi.yaml'\n    hide-download-button\n    required-props-first\n    native-scrollbars\n    theme='{\n      \"colors\": {\n        \"primary\": {\n          \"main\": \"#4285F4\"\n        }\n      },\n      \"typography\": {\n        \"fontSize\": \"16px\",\n        \"fontFamily\": \"Roboto, sans-serif\"\n      }\n    }'>\n  </redoc>\n  <script src=\"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js\"></script>\n</body>\n</html>\n```\n\n### Stoplight Elements\n\n```javascript\nimport { API } from '@stoplight/elements';\nimport '@stoplight/elements/styles.min.css';\n\nfunction App() {\n  return (\n    <API\n      apiDescriptionUrl=\"./openapi.yaml\"\n      router=\"hash\"\n      layout=\"sidebar\"\n      tryItCredentialsPolicy=\"include\"\n    />\n  );\n}\n```\n\n## Multi-Protocol Documentation\n\n### GraphQL Schema Documentation\n\n```graphql\n\"\"\"\nUser account in the system\n\"\"\"\ntype User {\n  \"\"\"\n  Unique user identifier\n  \"\"\"\n  id: ID!\n\n  \"\"\"\n  User's email address (unique)\n  @example \"user@example.com\"\n  \"\"\"\n  email: String!\n\n  \"\"\"\n  Display name\n  @example \"John Doe\"\n  \"\"\"\n  name: String!\n\n  \"\"\"\n  User's posts (paginated)\n  \"\"\"\n  posts(\n    \"\"\"Number of items per page (max 100)\"\"\"\n    limit: Int = 20\n    \"\"\"Page offset\"\"\"\n    offset: Int = 0\n  ): PostConnection!\n}\n\ntype Query {\n  \"\"\"\n  Fetch a user by ID\n  \"\"\"\n  user(\n    \"\"\"User's unique identifier\"\"\"\n    id: ID!\n  ): User\n\n  \"\"\"\n  Search users by name or email\n  \"\"\"\n  searchUsers(\n    \"\"\"Search query\"\"\"\n    query: String!\n    \"\"\"Maximum results to return\"\"\"\n    limit: Int = 10\n  ): [User!]!\n}\n\ntype Mutation {\n  \"\"\"\n  Create a new user account\n  \"\"\"\n  createUser(\n    \"\"\"User creation input\"\"\"\n    input: CreateUserInput!\n  ): CreateUserPayload!\n}\n\n\"\"\"\nInput for creating a user\n\"\"\"\ninput CreateUserInput {\n  \"\"\"User's email address\"\"\"\n  email: String!\n  \"\"\"Display name\"\"\"\n  name: String!\n}\n```\n\n**GraphQL Playground:**\n```javascript\nconst { ApolloServer } = require('apollo-server');\n\nconst server = new ApolloServer({\n  typeDefs,\n  resolvers,\n  introspection: true,  // Enable in dev\n  playground: {\n    settings: {\n      'editor.theme': 'dark',\n      'editor.fontSize': 14,\n    },\n  },\n});\n```\n\n### WebSocket Protocol Documentation\n\n```yaml\n# AsyncAPI 2.0\nasyncapi: 2.5.0\ninfo:\n  title: Chat WebSocket API\n  version: 1.0.0\n  description: Real-time chat messaging\n\nchannels:\n  chat/{roomId}:\n    parameters:\n      roomId:\n        description: Chat room identifier\n        schema:\n          type: string\n\n    subscribe:\n      summary: Receive messages\n      message:\n        oneOf:\n          - $ref: '#/components/messages/ChatMessage'\n          - $ref: '#/components/messages/UserJoined'\n\n    publish:\n      summary: Send a message\n      message:\n        $ref: '#/components/messages/ChatMessage'\n\ncomponents:\n  messages:\n    ChatMessage:\n      name: message\n      payload:\n        type: object\n        properties:\n          userId:\n            type: string\n          content:\n            type: string\n          timestamp:\n            type: string\n            format: date-time\n\n    UserJoined:\n      name: userJoined\n      payload:\n        type: object\n        properties:\n          userId:\n            type: string\n          username:\n            type: string\n```\n\n### gRPC Documentation\n\n```protobuf\nsyntax = \"proto3\";\n\npackage users.v1;\n\n// User service manages user accounts\nservice UserService {\n  // Get a user by ID\n  // Returns: User object or NOT_FOUND error\n  rpc GetUser(GetUserRequest) returns (User) {}\n\n  // List all users with pagination\n  // Returns: Paginated list of users\n  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {}\n\n  // Create a new user\n  // Returns: Created user or ALREADY_EXISTS error\n  rpc CreateUser(CreateUserRequest) returns (User) {}\n\n  // Stream user updates in real-time\n  // Returns: Stream of user update events\n  rpc WatchUsers(WatchUsersRequest) returns (stream UserEvent) {}\n}\n\n// User account\nmessage User {\n  // Unique identifier\n  string id = 1;\n\n  // Email address (unique, required)\n  string email = 2;\n\n  // Display name\n  string name = 3;\n\n  // Account creation timestamp\n  google.protobuf.Timestamp created_at = 4;\n}\n```\n\n## SDK Documentation Strategies\n\n### Multi-Language Examples\n\n```markdown\n# Create User\n\n## Python\n```python\nfrom myapi import Client\n\nclient = Client(api_key=\"<api-key>\")\nuser = client.users.create(\n    name=\"John Doe\",\n    email=\"john@example.com\"\n)\nprint(user.id)\n```\n\n## TypeScript\n```typescript\nimport { Client } from '@myapi/sdk';\n\nconst client = new Client({ apiKey: '<api-key>' });\nconst user = await client.users.create({\n  name: 'John Doe',\n  email: 'john@example.com',\n});\nconsole.log(user.id);\n```\n\n## Go\n```go\nimport \"github.com/myapi/sdk-go\"\n\nclient := sdk.NewClient(\"<api-key>\")\nuser, err := client.Users.Create(ctx, &sdk.CreateUserInput{\n    Name:  \"John Doe\",\n    Email: \"john@example.com\",\n})\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Println(user.ID)\n```\n\n## Ruby\n```ruby\nrequire 'myapi'\n\nclient = MyAPI::Client.new(api_key: '<api-key>')\nuser = client.users.create(\n  name: 'John Doe',\n  email: 'john@example.com'\n)\nputs user.id\n```\n```\n\n### SDK Reference Template\n\n```markdown\n# Users SDK\n\n## Installation\n```bash\nnpm install @myapi/sdk\n```\n\n## Configuration\n```typescript\nimport { Client } from '@myapi/sdk';\n\nconst client = new Client({\n  apiKey: process.env.API_KEY,\n  baseUrl: 'https://api.example.com',  // Optional\n  timeout: 30000,  // Optional, default 30s\n});\n```\n\n## Methods\n\n### `client.users.create(data)`\nCreate a new user.\n\n**Parameters:**\n- `data.name` (string, required) - User's display name\n- `data.email` (string, required) - User's email address\n\n**Returns:** Promise<User>\n\n**Throws:**\n- `ValidationError` - Invalid input data\n- `ConflictError` - Email already exists\n- `AuthenticationError` - Invalid API key\n\n**Example:**\n```typescript\nconst user = await client.users.create({\n  name: 'John Doe',\n  email: 'john@example.com',\n});\n```\n\n## Error Handling\n```typescript\nimport { ValidationError, ConflictError } from '@myapi/sdk';\n\ntry {\n  await client.users.create(data);\n} catch (error) {\n  if (error instanceof ValidationError) {\n    console.error('Invalid data:', error.fields);\n  } else if (error instanceof ConflictError) {\n    console.error('User already exists');\n  }\n}\n```\n```\n\n## Quick Reference\n\n| Tool | Protocol | Features |\n|------|----------|----------|\n| Swagger UI | REST | Try-it-out, auth |\n| Redoc | REST | Clean, responsive |\n| Stoplight | REST | Modern, mock server |\n| GraphQL Playground | GraphQL | Explorer, history |\n| AsyncAPI Studio | WebSocket | Visual editor |\n| grpcui | gRPC | Interactive console |\n","skills/code-documenter/references/python-docstrings.md":"# Python Docstrings\n\n## Google Style (Recommended)\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"Calculate total cost including tax.\n\n    Args:\n        items: List of items to calculate total for.\n        tax_rate: Tax rate as decimal (e.g., 0.08 for 8%).\n\n    Returns:\n        Total cost including tax.\n\n    Raises:\n        ValueError: If tax_rate is negative or items is empty.\n\n    Example:\n        >>> calculate_total([Item(10), Item(20)], 0.1)\n        33.0\n    \"\"\"\n```\n\n## NumPy Style\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"\n    Calculate total cost including tax.\n\n    Parameters\n    ----------\n    items : list[Item]\n        List of items to calculate total for.\n    tax_rate : float, optional\n        Tax rate as decimal (e.g., 0.08 for 8%). Default is 0.0.\n\n    Returns\n    -------\n    float\n        Total cost including tax.\n\n    Raises\n    ------\n    ValueError\n        If tax_rate is negative or items is empty.\n\n    Examples\n    --------\n    >>> calculate_total([Item(10), Item(20)], 0.1)\n    33.0\n    \"\"\"\n```\n\n## Sphinx Style\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"Calculate total cost including tax.\n\n    :param items: List of items to calculate total for.\n    :type items: list[Item]\n    :param tax_rate: Tax rate as decimal (e.g., 0.08 for 8%).\n    :type tax_rate: float\n    :returns: Total cost including tax.\n    :rtype: float\n    :raises ValueError: If tax_rate is negative or items is empty.\n\n    .. code-block:: python\n\n        >>> calculate_total([Item(10), Item(20)], 0.1)\n        33.0\n    \"\"\"\n```\n\n## Class Documentation\n\n```python\nclass UserService:\n    \"\"\"Service for managing user operations.\n\n    This service handles CRUD operations for users and\n    integrates with the authentication system.\n\n    Attributes:\n        db: Database session for queries.\n        cache: Redis client for caching.\n\n    Example:\n        >>> service = UserService(db, cache)\n        >>> user = await service.create_user(data)\n    \"\"\"\n\n    def __init__(self, db: AsyncSession, cache: Redis) -> None:\n        \"\"\"Initialize UserService.\n\n        Args:\n            db: Database session for queries.\n            cache: Redis client for caching.\n        \"\"\"\n```\n\n## Quick Reference\n\n| Style | Args Format | Returns Format |\n|-------|-------------|----------------|\n| Google | `Args:` block | `Returns:` block |\n| NumPy | `Parameters` section | `Returns` section |\n| Sphinx | `:param name:` | `:returns:` |\n\n## Sections Available\n\n| Section | Google | NumPy | Sphinx |\n|---------|--------|-------|--------|\n| Parameters | `Args:` | `Parameters` | `:param:` |\n| Returns | `Returns:` | `Returns` | `:returns:` |\n| Raises | `Raises:` | `Raises` | `:raises:` |\n| Examples | `Example:` | `Examples` | `.. code-block::` |\n| Notes | `Note:` | `Notes` | `.. note::` |\n| Attributes | `Attributes:` | `Attributes` | `:ivar:` |\n","skills/code-documenter/references/typescript-jsdoc.md":"# TypeScript JSDoc\n\n## Function Documentation\n\n```typescript\n/**\n * Calculate total cost including tax.\n *\n * @param items - List of items to calculate total for\n * @param taxRate - Tax rate as decimal (e.g., 0.08 for 8%)\n * @returns Total cost including tax\n * @throws {Error} If taxRate is negative or items is empty\n *\n * @example\n * ```typescript\n * const total = calculateTotal(items, 0.08);\n * console.log(total); // 108.00\n * ```\n */\nfunction calculateTotal(items: Item[], taxRate = 0): number {\n```\n\n## Class Documentation\n\n```typescript\n/**\n * Service for managing user operations.\n *\n * Handles CRUD operations and integrates with authentication system.\n *\n * @example\n * ```typescript\n * const service = new UserService(db, cache);\n * const user = await service.create(userData);\n * ```\n */\nclass UserService {\n  /**\n   * Create a new UserService instance.\n   *\n   * @param db - Database connection\n   * @param cache - Redis cache client\n   */\n  constructor(\n    private readonly db: Database,\n    private readonly cache: Cache,\n  ) {}\n}\n```\n\n## Interface Documentation\n\n```typescript\n/**\n * User data transfer object.\n *\n * @interface UserDto\n */\ninterface UserDto {\n  /** Unique user identifier */\n  id: string;\n\n  /** User's email address (unique) */\n  email: string;\n\n  /** User's display name */\n  name: string;\n\n  /** Account creation timestamp */\n  createdAt: Date;\n}\n```\n\n## Generic Types\n\n```typescript\n/**\n * Paginated response wrapper.\n *\n * @template T - Type of items in the data array\n */\ninterface PaginatedResponse<T> {\n  /** Array of items for current page */\n  data: T[];\n\n  /** Total number of items across all pages */\n  total: number;\n\n  /** Current page number (1-indexed) */\n  page: number;\n\n  /** Number of items per page */\n  limit: number;\n}\n```\n\n## Async Functions\n\n```typescript\n/**\n * Fetch user by ID from database.\n *\n * @param id - User's unique identifier\n * @returns Promise resolving to user data or null if not found\n * @throws {DatabaseError} If connection fails\n *\n * @async\n */\nasync function findUserById(id: string): Promise<User | null> {\n```\n\n## Quick Reference\n\n| Tag | Purpose | Example |\n|-----|---------|---------|\n| `@param` | Parameter description | `@param name - User's name` |\n| `@returns` | Return value | `@returns User object` |\n| `@throws` | Exception thrown | `@throws {Error} If invalid` |\n| `@example` | Usage example | Code block |\n| `@see` | Reference link | `@see UserService` |\n| `@deprecated` | Mark deprecated | `@deprecated Use v2 instead` |\n| `@template` | Generic type param | `@template T - Item type` |\n| `@async` | Async function | Mark async |\n| `@private` | Private member | Internal use |\n| `@readonly` | Read-only property | Cannot modify |\n\n## Common Patterns\n\n```typescript\n// Optional parameters\n/** @param [options] - Optional configuration */\n\n// Default values\n/** @param [limit=10] - Items per page (default: 10) */\n\n// Multiple types\n/** @param input - Input value (string or number) */\n\n// Callback parameters\n/**\n * @callback FilterFn\n * @param item - Item to filter\n * @returns Whether item passes filter\n */\n```\n","skills/code-documenter/references/user-guides-tutorials.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-documenter%2Freferences%2Fuser-guides-tutorials.md -->","skills/code-reviewer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2FSKILL.md -->","skills/code-reviewer/references/common-issues.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Fcommon-issues.md -->","skills/code-reviewer/references/feedback-examples.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Ffeedback-examples.md -->","skills/code-reviewer/references/receiving-feedback.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freceiving-feedback.md -->","skills/code-reviewer/references/report-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freport-template.md -->","skills/code-reviewer/references/review-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freview-checklist.md -->","skills/code-reviewer/references/spec-compliance-review.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Fspec-compliance-review.md -->","skills/cpp-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2FSKILL.md -->","skills/cpp-pro/references/build-tooling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fbuild-tooling.md -->","skills/cpp-pro/references/concurrency.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fconcurrency.md -->","skills/cpp-pro/references/memory-performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fmemory-performance.md -->","skills/cpp-pro/references/modern-cpp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fmodern-cpp.md -->","skills/cpp-pro/references/templates.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Ftemplates.md -->","skills/csharp-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2FSKILL.md -->","skills/csharp-developer/references/aspnet-core.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Faspnet-core.md -->","skills/csharp-developer/references/blazor.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fblazor.md -->","skills/csharp-developer/references/entity-framework.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fentity-framework.md -->","skills/csharp-developer/references/modern-csharp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fmodern-csharp.md -->","skills/csharp-developer/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fperformance.md -->","skills/database-optimizer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2FSKILL.md -->","skills/database-optimizer/references/index-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Findex-strategies.md -->","skills/database-optimizer/references/monitoring-analysis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fmonitoring-analysis.md -->","skills/database-optimizer/references/mysql-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fmysql-tuning.md -->","skills/database-optimizer/references/postgresql-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fpostgresql-tuning.md -->","skills/database-optimizer/references/query-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fquery-optimization.md -->","skills/debugging-wizard/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2FSKILL.md -->","skills/debugging-wizard/references/common-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fcommon-patterns.md -->","skills/debugging-wizard/references/debugging-tools.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fdebugging-tools.md -->","skills/debugging-wizard/references/quick-fixes.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fquick-fixes.md -->","skills/debugging-wizard/references/strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fstrategies.md -->","skills/debugging-wizard/references/systematic-debugging.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fsystematic-debugging.md -->","skills/devops-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2FSKILL.md -->","skills/devops-engineer/references/deployment-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fdeployment-strategies.md -->","skills/devops-engineer/references/docker-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fdocker-patterns.md -->","skills/devops-engineer/references/github-actions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fgithub-actions.md -->","skills/devops-engineer/references/gitlab-ci.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fgitlab-ci.md -->","skills/devops-engineer/references/incident-response.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fincident-response.md -->","skills/devops-engineer/references/kubernetes.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fkubernetes.md -->","skills/devops-engineer/references/platform-engineering.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fplatform-engineering.md -->","skills/devops-engineer/references/release-automation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Frelease-automation.md -->","skills/devops-engineer/references/terraform-iac.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fterraform-iac.md -->","skills/django-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2FSKILL.md -->","skills/django-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fauthentication.md -->","skills/django-expert/references/drf-serializers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fdrf-serializers.md -->","skills/django-expert/references/models-orm.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fmodels-orm.md -->","skills/django-expert/references/testing-django.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Ftesting-django.md -->","skills/django-expert/references/viewsets-views.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fviewsets-views.md -->","skills/django-storages-s3/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2FSKILL.md -->","skills/django-storages-s3/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fconfiguration.md -->","skills/django-storages-s3/references/custom-backends.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fcustom-backends.md -->","skills/django-storages-s3/references/presigned-urls.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fpresigned-urls.md -->","skills/django-storages-s3/references/testing-storages.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Ftesting-storages.md -->","skills/dotnet-core-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2FSKILL.md -->","skills/dotnet-core-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fauthentication.md -->","skills/dotnet-core-expert/references/clean-architecture.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fclean-architecture.md -->","skills/dotnet-core-expert/references/cloud-native.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fcloud-native.md -->","skills/dotnet-core-expert/references/entity-framework.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fentity-framework.md -->","skills/dotnet-core-expert/references/minimal-apis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fminimal-apis.md -->","skills/embedded-systems/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2FSKILL.md -->","skills/embedded-systems/references/communication-protocols.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fcommunication-protocols.md -->","skills/embedded-systems/references/memory-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fmemory-optimization.md -->","skills/embedded-systems/references/microcontroller-programming.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fmicrocontroller-programming.md -->","skills/embedded-systems/references/power-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fpower-optimization.md -->","skills/embedded-systems/references/rtos-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Frtos-patterns.md -->","skills/fastapi-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2FSKILL.md -->","skills/fastapi-expert/references/async-sqlalchemy.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fasync-sqlalchemy.md -->","skills/fastapi-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fauthentication.md -->","skills/fastapi-expert/references/endpoints-routing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fendpoints-routing.md -->","skills/fastapi-expert/references/migration-from-django.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fmigration-from-django.md -->","skills/fastapi-expert/references/pydantic-v2.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fpydantic-v2.md -->","skills/fastapi-expert/references/testing-async.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Ftesting-async.md -->","skills/feature-forge/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2FSKILL.md -->","skills/feature-forge/references/acceptance-criteria.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Facceptance-criteria.md -->","skills/feature-forge/references/ears-syntax.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fears-syntax.md -->","skills/feature-forge/references/interview-questions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Finterview-questions.md -->","skills/feature-forge/references/pre-discovery-subagents.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fpre-discovery-subagents.md -->","skills/feature-forge/references/specification-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fspecification-template.md -->","skills/fine-tuning-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2FSKILL.md -->","skills/fine-tuning-expert/references/dataset-preparation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fdataset-preparation.md -->","skills/fine-tuning-expert/references/deployment-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fdeployment-optimization.md -->","skills/fine-tuning-expert/references/evaluation-metrics.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fevaluation-metrics.md -->","skills/fine-tuning-expert/references/hyperparameter-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fhyperparameter-tuning.md -->","skills/fine-tuning-expert/references/lora-peft.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Flora-peft.md -->","skills/flutter-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2FSKILL.md -->","skills/flutter-expert/references/bloc-state.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fbloc-state.md -->","skills/flutter-expert/references/gorouter-navigation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fgorouter-navigation.md -->","skills/flutter-expert/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fperformance.md -->","skills/flutter-expert/references/project-structure.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fproject-structure.md -->","skills/flutter-expert/references/riverpod-state.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Friverpod-state.md -->","skills/flutter-expert/references/widget-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fwidget-patterns.md -->","skills/fullstack-guardian/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2FSKILL.md -->","skills/fullstack-guardian/references/api-design-standards.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fapi-design-standards.md -->","skills/fullstack-guardian/references/architecture-decisions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Farchitecture-decisions.md -->","skills/fullstack-guardian/references/backend-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fbackend-patterns.md -->","skills/fullstack-guardian/references/common-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fcommon-patterns.md -->","skills/fullstack-guardian/references/deliverables-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fdeliverables-checklist.md -->","skills/fullstack-guardian/references/design-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fdesign-template.md -->","skills/fullstack-guardian/references/error-handling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Ferror-handling.md -->","skills/fullstack-guardian/references/frontend-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Ffrontend-patterns.md -->","skills/fullstack-guardian/references/integration-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fintegration-patterns.md -->","skills/fullstack-guardian/references/security-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fsecurity-checklist.md -->","skills/game-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2FSKILL.md -->","skills/game-developer/references/ecs-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fecs-patterns.md -->","skills/game-developer/references/multiplayer-networking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fmultiplayer-networking.md -->","skills/game-developer/references/performance-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fperformance-optimization.md -->","skills/game-developer/references/unity-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Funity-patterns.md -->","skills/game-developer/references/unreal-cpp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Funreal-cpp.md -->","skills/golang-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2FSKILL.md -->","skills/golang-pro/references/concurrency.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fconcurrency.md -->","skills/golang-pro/references/generics.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fgenerics.md -->","skills/golang-pro/references/interfaces.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Finterfaces.md -->","skills/golang-pro/references/project-structure.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fproject-structure.md -->","skills/golang-pro/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Ftesting.md -->","skills/graphql-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2FSKILL.md -->","skills/graphql-architect/references/federation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Ffederation.md -->","skills/graphql-architect/references/migration-from-rest.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fmigration-from-rest.md -->","skills/graphql-architect/references/resolvers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fresolvers.md -->","skills/graphql-architect/references/schema-design.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fschema-design.md -->","skills/graphql-architect/references/security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fsecurity.md -->","skills/graphql-architect/references/subscriptions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fsubscriptions.md -->","skills/java-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2FSKILL.md -->","skills/java-architect/references/jpa-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fjpa-optimization.md -->","skills/java-architect/references/reactive-webflux.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Freactive-webflux.md -->","skills/java-architect/references/spring-boot-setup.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fspring-boot-setup.md -->","skills/java-architect/references/spring-security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fspring-security.md -->","skills/java-architect/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Ftesting-patterns.md -->","skills/javascript-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2FSKILL.md -->","skills/javascript-pro/references/async-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fasync-patterns.md -->","skills/javascript-pro/references/browser-apis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fbrowser-apis.md -->","skills/javascript-pro/references/modern-syntax.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fmodern-syntax.md -->","skills/javascript-pro/references/modules.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fmodules.md -->","skills/javascript-pro/references/node-essentials.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fnode-essentials.md -->","skills/kotlin-specialist/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2FSKILL.md -->","skills/kotlin-specialist/references/android-compose.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fandroid-compose.md -->","skills/kotlin-specialist/references/coroutines-flow.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fcoroutines-flow.md -->","skills/kotlin-specialist/references/dsl-idioms.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fdsl-idioms.md -->","skills/kotlin-specialist/references/ktor-server.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fktor-server.md -->","skills/kotlin-specialist/references/multiplatform-kmp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fmultiplatform-kmp.md -->","skills/kubernetes-specialist/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2FSKILL.md -->","skills/kubernetes-specialist/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fconfiguration.md -->","skills/kubernetes-specialist/references/cost-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fcost-optimization.md -->","skills/kubernetes-specialist/references/custom-operators.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fcustom-operators.md -->","skills/kubernetes-specialist/references/gitops.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fgitops.md -->","skills/kubernetes-specialist/references/helm-charts.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fhelm-charts.md -->","skills/kubernetes-specialist/references/multi-cluster.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fmulti-cluster.md -->","skills/kubernetes-specialist/references/networking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fnetworking.md -->","skills/kubernetes-specialist/references/service-mesh.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fservice-mesh.md -->","skills/kubernetes-specialist/references/storage.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fstorage.md -->","skills/kubernetes-specialist/references/troubleshooting.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Ftroubleshooting.md -->","skills/kubernetes-specialist/references/workloads.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fworkloads.md -->","skills/laravel-specialist/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2FSKILL.md -->","skills/laravel-specialist/references/eloquent.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Feloquent.md -->","skills/laravel-specialist/references/livewire.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Flivewire.md -->","skills/laravel-specialist/references/queues.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Fqueues.md -->","skills/laravel-specialist/references/routing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Frouting.md -->","skills/laravel-specialist/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Ftesting.md -->","skills/legacy-modernizer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2FSKILL.md -->","skills/legacy-modernizer/references/legacy-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Flegacy-testing.md -->","skills/legacy-modernizer/references/migration-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fmigration-strategies.md -->","skills/legacy-modernizer/references/refactoring-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Frefactoring-patterns.md -->","skills/legacy-modernizer/references/strangler-fig-pattern.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fstrangler-fig-pattern.md -->","skills/legacy-modernizer/references/system-assessment.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fsystem-assessment.md -->","skills/mcp-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2FSKILL.md -->","skills/mcp-developer/references/protocol.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fprotocol.md -->","skills/mcp-developer/references/python-sdk.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fpython-sdk.md -->","skills/mcp-developer/references/resources.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fresources.md -->","skills/mcp-developer/references/tools.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Ftools.md -->","skills/mcp-developer/references/typescript-sdk.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Ftypescript-sdk.md -->","skills/microservices-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2FSKILL.md -->","skills/microservices-architect/references/communication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fcommunication.md -->","skills/microservices-architect/references/data.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fdata.md -->","skills/microservices-architect/references/decomposition.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fdecomposition.md -->","skills/microservices-architect/references/observability.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fobservability.md -->","skills/microservices-architect/references/patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fpatterns.md -->","skills/ml-pipeline/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2FSKILL.md -->","skills/ml-pipeline/references/experiment-tracking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fexperiment-tracking.md -->","skills/ml-pipeline/references/feature-engineering.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Ffeature-engineering.md -->","skills/ml-pipeline/references/model-validation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fmodel-validation.md -->","skills/ml-pipeline/references/pipeline-orchestration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fpipeline-orchestration.md -->","skills/ml-pipeline/references/training-pipelines.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Ftraining-pipelines.md -->","skills/monitoring-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2FSKILL.md -->","skills/monitoring-expert/references/alerting-rules.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Falerting-rules.md -->","skills/monitoring-expert/references/application-profiling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fapplication-profiling.md -->","skills/monitoring-expert/references/capacity-planning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fcapacity-planning.md -->","skills/monitoring-expert/references/dashboards.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fdashboards.md -->","skills/monitoring-expert/references/opentelemetry.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fopentelemetry.md -->","skills/monitoring-expert/references/performance-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fperformance-testing.md -->","skills/monitoring-expert/references/prometheus-metrics.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fprometheus-metrics.md -->","skills/monitoring-expert/references/structured-logging.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fstructured-logging.md -->","skills/nestjs-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2FSKILL.md -->","skills/nestjs-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fauthentication.md -->","skills/nestjs-expert/references/controllers-routing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fcontrollers-routing.md -->","skills/nestjs-expert/references/dtos-validation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fdtos-validation.md -->","skills/nestjs-expert/references/migration-from-express.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fmigration-from-express.md -->","skills/nestjs-expert/references/services-di.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fservices-di.md -->","skills/nestjs-expert/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Ftesting-patterns.md -->","skills/nextjs-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2FSKILL.md -->","skills/nextjs-developer/references/app-router.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fapp-router.md -->","skills/nextjs-developer/references/data-fetching.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fdata-fetching.md -->","skills/nextjs-developer/references/deployment.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fdeployment.md -->","skills/nextjs-developer/references/server-actions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fserver-actions.md -->","skills/nextjs-developer/references/server-components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fserver-components.md -->","skills/pandas-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2FSKILL.md -->","skills/pandas-pro/references/aggregation-groupby.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Faggregation-groupby.md -->","skills/pandas-pro/references/data-cleaning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fdata-cleaning.md -->","skills/pandas-pro/references/dataframe-operations.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fdataframe-operations.md -->","skills/pandas-pro/references/merging-joining.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fmerging-joining.md -->","skills/pandas-pro/references/performance-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fperformance-optimization.md -->","skills/php-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2FSKILL.md -->","skills/php-pro/references/async-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fasync-patterns.md -->","skills/php-pro/references/laravel-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Flaravel-patterns.md -->","skills/php-pro/references/modern-php-features.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fmodern-php-features.md -->","skills/php-pro/references/symfony-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fsymfony-patterns.md -->","skills/php-pro/references/testing-quality.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Ftesting-quality.md -->","skills/playwright-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2FSKILL.md -->","skills/playwright-expert/references/api-mocking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fapi-mocking.md -->","skills/playwright-expert/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fconfiguration.md -->","skills/playwright-expert/references/debugging-flaky.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fdebugging-flaky.md -->","skills/playwright-expert/references/page-object-model.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fpage-object-model.md -->","skills/playwright-expert/references/selectors-locators.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fselectors-locators.md -->","skills/postgres-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2FSKILL.md -->","skills/postgres-pro/references/extensions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fextensions.md -->","skills/postgres-pro/references/jsonb.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fjsonb.md -->","skills/postgres-pro/references/maintenance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fmaintenance.md -->","skills/postgres-pro/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fperformance.md -->","skills/postgres-pro/references/replication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Freplication.md -->","skills/prompt-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2FSKILL.md -->","skills/prompt-engineer/references/context-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fcontext-management.md -->","skills/prompt-engineer/references/evaluation-frameworks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fevaluation-frameworks.md -->","skills/prompt-engineer/references/prompt-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fprompt-optimization.md -->","skills/prompt-engineer/references/prompt-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fprompt-patterns.md -->","skills/prompt-engineer/references/structured-outputs.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fstructured-outputs.md -->","skills/prompt-engineer/references/system-prompts.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fsystem-prompts.md -->","skills/python-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2FSKILL.md -->","skills/python-pro/references/async-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fasync-patterns.md -->","skills/python-pro/references/packaging.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fpackaging.md -->","skills/python-pro/references/standard-library.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fstandard-library.md -->","skills/python-pro/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Ftesting.md -->","skills/python-pro/references/type-system.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Ftype-system.md -->","skills/rag-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2FSKILL.md -->","skills/rag-architect/references/chunking-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fchunking-strategies.md -->","skills/rag-architect/references/embedding-models.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fembedding-models.md -->","skills/rag-architect/references/rag-evaluation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Frag-evaluation.md -->","skills/rag-architect/references/retrieval-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fretrieval-optimization.md -->","skills/rag-architect/references/vector-databases.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fvector-databases.md -->","skills/rails-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2FSKILL.md -->","skills/rails-expert/references/active-record.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Factive-record.md -->","skills/rails-expert/references/api-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fapi-development.md -->","skills/rails-expert/references/background-jobs.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fbackground-jobs.md -->","skills/rails-expert/references/hotwire-turbo.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fhotwire-turbo.md -->","skills/rails-expert/references/rspec-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Frspec-testing.md -->","skills/react-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2FSKILL.md -->","skills/react-expert/references/hooks-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fhooks-patterns.md -->","skills/react-expert/references/migration-class-to-modern.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fmigration-class-to-modern.md -->","skills/react-expert/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fperformance.md -->","skills/react-expert/references/react-19-features.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Freact-19-features.md -->","skills/react-expert/references/server-components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fserver-components.md -->","skills/react-expert/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fstate-management.md -->","skills/react-expert/references/testing-react.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Ftesting-react.md -->","skills/react-native-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2FSKILL.md -->","skills/react-native-expert/references/expo-router.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fexpo-router.md -->","skills/react-native-expert/references/list-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Flist-optimization.md -->","skills/react-native-expert/references/platform-handling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fplatform-handling.md -->","skills/react-native-expert/references/project-structure.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fproject-structure.md -->","skills/react-native-expert/references/storage-hooks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fstorage-hooks.md -->","skills/rust-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2FSKILL.md -->","skills/rust-engineer/references/async.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Fasync.md -->","skills/rust-engineer/references/error-handling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ferror-handling.md -->","skills/rust-engineer/references/ownership.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Fownership.md -->","skills/rust-engineer/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ftesting.md -->","skills/rust-engineer/references/traits.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ftraits.md -->","skills/salesforce-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2FSKILL.md -->","skills/salesforce-developer/references/apex-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fapex-development.md -->","skills/salesforce-developer/references/deployment-devops.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fdeployment-devops.md -->","skills/salesforce-developer/references/integration-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fintegration-patterns.md -->","skills/salesforce-developer/references/lightning-web-components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Flightning-web-components.md -->","skills/salesforce-developer/references/soql-sosl.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fsoql-sosl.md -->","skills/secure-code-guardian/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2FSKILL.md -->","skills/secure-code-guardian/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fauthentication.md -->","skills/secure-code-guardian/references/input-validation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Finput-validation.md -->","skills/secure-code-guardian/references/owasp-prevention.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fowasp-prevention.md -->","skills/secure-code-guardian/references/security-headers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fsecurity-headers.md -->","skills/secure-code-guardian/references/xss-csrf.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fxss-csrf.md -->","skills/security-reviewer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2FSKILL.md -->","skills/security-reviewer/references/infrastructure-security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Finfrastructure-security.md -->","skills/security-reviewer/references/penetration-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fpenetration-testing.md -->","skills/security-reviewer/references/report-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Freport-template.md -->","skills/security-reviewer/references/sast-tools.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fsast-tools.md -->","skills/security-reviewer/references/secret-scanning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fsecret-scanning.md -->","skills/security-reviewer/references/vulnerability-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fvulnerability-patterns.md -->","skills/shopify-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2FSKILL.md -->","skills/shopify-expert/references/app-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fapp-development.md -->","skills/shopify-expert/references/checkout-customization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fcheckout-customization.md -->","skills/shopify-expert/references/liquid-templating.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fliquid-templating.md -->","skills/shopify-expert/references/performance-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fperformance-optimization.md -->","skills/shopify-expert/references/storefront-api.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fstorefront-api.md -->","skills/spark-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2FSKILL.md -->","skills/spark-engineer/references/partitioning-caching.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fpartitioning-caching.md -->","skills/spark-engineer/references/performance-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fperformance-tuning.md -->","skills/spark-engineer/references/rdd-operations.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Frdd-operations.md -->","skills/spark-engineer/references/spark-sql-dataframes.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fspark-sql-dataframes.md -->","skills/spark-engineer/references/streaming-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fstreaming-patterns.md -->","skills/spec-miner/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2FSKILL.md -->","skills/spec-miner/references/analysis-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fanalysis-checklist.md -->","skills/spec-miner/references/analysis-process.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fanalysis-process.md -->","skills/spec-miner/references/ears-format.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fears-format.md -->","skills/spec-miner/references/specification-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fspecification-template.md -->","skills/spring-boot-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2FSKILL.md -->","skills/spring-boot-engineer/references/cloud.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fcloud.md -->","skills/spring-boot-engineer/references/data.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fdata.md -->","skills/spring-boot-engineer/references/security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fsecurity.md -->","skills/spring-boot-engineer/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Ftesting.md -->","skills/spring-boot-engineer/references/web.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fweb.md -->","skills/sql-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2FSKILL.md -->","skills/sql-pro/references/database-design.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fdatabase-design.md -->","skills/sql-pro/references/dialect-differences.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fdialect-differences.md -->","skills/sql-pro/references/optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Foptimization.md -->","skills/sql-pro/references/query-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fquery-patterns.md -->","skills/sql-pro/references/window-functions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fwindow-functions.md -->","skills/sre-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2FSKILL.md -->","skills/sre-engineer/references/automation-toil.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fautomation-toil.md -->","skills/sre-engineer/references/error-budget-policy.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Ferror-budget-policy.md -->","skills/sre-engineer/references/incident-chaos.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fincident-chaos.md -->","skills/sre-engineer/references/monitoring-alerting.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fmonitoring-alerting.md -->","skills/sre-engineer/references/slo-sli-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fslo-sli-management.md -->","skills/swift-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2FSKILL.md -->","skills/swift-expert/references/async-concurrency.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fasync-concurrency.md -->","skills/swift-expert/references/memory-performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fmemory-performance.md -->","skills/swift-expert/references/protocol-oriented.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fprotocol-oriented.md -->","skills/swift-expert/references/swiftui-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fswiftui-patterns.md -->","skills/swift-expert/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Ftesting-patterns.md -->","skills/terraform-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2FSKILL.md -->","skills/terraform-engineer/references/best-practices.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fbest-practices.md -->","skills/terraform-engineer/references/module-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fmodule-patterns.md -->","skills/terraform-engineer/references/providers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fproviders.md -->","skills/terraform-engineer/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fstate-management.md -->","skills/terraform-engineer/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Ftesting.md -->","skills/test-master/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2FSKILL.md -->","skills/test-master/references/automation-frameworks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fautomation-frameworks.md -->","skills/test-master/references/e2e-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fe2e-testing.md -->","skills/test-master/references/integration-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fintegration-testing.md -->","skills/test-master/references/performance-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fperformance-testing.md -->","skills/test-master/references/qa-methodology.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fqa-methodology.md -->","skills/test-master/references/security-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fsecurity-testing.md -->","skills/test-master/references/tdd-iron-laws.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftdd-iron-laws.md -->","skills/test-master/references/test-reports.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftest-reports.md -->","skills/test-master/references/testing-anti-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftesting-anti-patterns.md -->","skills/test-master/references/unit-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Funit-testing.md -->","skills/the-fool/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2FSKILL.md -->","skills/the-fool/references/dialectic-synthesis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fdialectic-synthesis.md -->","skills/the-fool/references/evidence-audit.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fevidence-audit.md -->","skills/the-fool/references/mode-selection-guide.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fmode-selection-guide.md -->","skills/the-fool/references/pre-mortem-analysis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fpre-mortem-analysis.md -->","skills/the-fool/references/red-team-adversarial.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fred-team-adversarial.md -->","skills/the-fool/references/socratic-questioning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fsocratic-questioning.md -->","skills/typescript-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2FSKILL.md -->","skills/typescript-pro/references/advanced-types.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fadvanced-types.md -->","skills/typescript-pro/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fconfiguration.md -->","skills/typescript-pro/references/patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fpatterns.md -->","skills/typescript-pro/references/type-guards.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Ftype-guards.md -->","skills/typescript-pro/references/utility-types.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Futility-types.md -->","skills/vue-expert-js/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2FSKILL.md -->","skills/vue-expert-js/references/component-architecture.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fcomponent-architecture.md -->","skills/vue-expert-js/references/composables-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fcomposables-patterns.md -->","skills/vue-expert-js/references/jsdoc-typing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fjsdoc-typing.md -->","skills/vue-expert-js/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fstate-management.md -->","skills/vue-expert-js/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Ftesting-patterns.md -->","skills/vue-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2FSKILL.md -->","skills/vue-expert/references/build-tooling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fbuild-tooling.md -->","skills/vue-expert/references/components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fcomponents.md -->","skills/vue-expert/references/composition-api.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fcomposition-api.md -->","skills/vue-expert/references/mobile-hybrid.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fmobile-hybrid.md -->","skills/vue-expert/references/nuxt.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fnuxt.md -->","skills/vue-expert/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fstate-management.md -->","skills/vue-expert/references/typescript.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Ftypescript.md -->","skills/websocket-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2FSKILL.md -->","skills/websocket-engineer/references/alternatives.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Falternatives.md -->","skills/websocket-engineer/references/patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fpatterns.md -->","skills/websocket-engineer/references/protocol.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fprotocol.md -->","skills/websocket-engineer/references/scaling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fscaling.md -->","skills/websocket-engineer/references/security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fsecurity.md -->","skills/wordpress-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2FSKILL.md -->","skills/wordpress-pro/references/gutenberg-blocks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fgutenberg-blocks.md -->","skills/wordpress-pro/references/hooks-filters.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fhooks-filters.md -->","skills/wordpress-pro/references/performance-security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fperformance-security.md -->","skills/wordpress-pro/references/plugin-architecture.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fplugin-architecture.md -->","skills/wordpress-pro/references/theme-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Ftheme-development.md -->"},"files":{".claude-plugin/marketplace.json":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=.claude-plugin%2Fmarketplace.json -->",".claude-plugin/plugin.json":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=.claude-plugin%2Fplugin.json -->","CLAUDE.md":"# Claude Skills Project Configuration\n\n> This file governs Claude's behavior when working on the claude-skills repository.\n\n---\n\n## Skill Authorship Standards\n\nSkills follow the [Agent Skills specification](https://agentskills.io/specification). This section covers project-specific conventions that go beyond the base spec.\n\n### The Description Trap\n\n**Critical:** Never put process steps or workflow sequences in descriptions. When descriptions contain step-by-step instructions, agents follow the brief description instead of reading the full skill content. This defeats the purpose of detailed skills.\n\nBrief capability statements (what it does) and trigger conditions (when to use it) are both appropriate. Process steps (how it works) are not.\n\n**BAD - Process steps in description:**\n```yaml\ndescription: Use for debugging. First investigate root cause, then analyze\npatterns, test hypotheses, and implement fixes with tests.\n```\n\n**GOOD - Capability + trigger:**\n```yaml\ndescription: Diagnoses bugs through root cause analysis and pattern matching.\nUse when encountering errors or unexpected behavior requiring investigation.\n```\n\n**Format:** `[Brief capability statement]. Use when [triggering conditions].`\n\nDescriptions tell WHAT the skill does and WHEN to use it. The SKILL.md body tells HOW.\n\n---\n\n### Frontmatter Requirements\n\nPer the [Agent Skills specification](https://agentskills.io/specification), only `name` and `description` are top-level required fields. Custom fields go under `metadata`.\n\n```yaml\n---\nname: skill-name-with-hyphens\ndescription: [Brief capability statement]. Use when [triggering conditions] - max 1024 chars\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.0.0\"\n  domain: frontend\n  triggers: keyword1, keyword2, keyword3\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: fullstack-guardian, test-master, devops-engineer\n---\n```\n\n**Top-level fields (spec-defined):**\n- `name`: Letters, numbers, and hyphens only (no parentheses or special characters)\n- `description`: Maximum 1024 characters. Capability statement + trigger conditions. No process steps.\n- `license`: Always `MIT` for this project\n- `allowed-tools`: Space-delimited tool list (only on skills that restrict tools)\n\n**Metadata fields (project-specific):**\n- `author`: GitHub profile URL of the skill author\n- `company`: Optional. URL of the author's company (`https://synergetic.solutions` on skills authored by @jeffallan); omit for contributed skills unless the contributor supplies one\n- `version`: Semantic version string (quoted, e.g., `\"1.0.0\"`)\n- `domain`: Category from the domain list below\n- `triggers`: Comma-separated searchable keywords\n- `role`: `specialist` | `expert` | `architect` | `engineer`\n- `scope`: `implementation` | `review` | `design` | `system-design` | `testing` | `analysis` | `infrastructure` | `optimization` | `architecture`\n- `output-format`: `code` | `document` | `report` | `architecture` | `specification` | `schema` | `manifests` | `analysis` | `analysis-and-code`\n- `related-skills`: Comma-separated skill directory names (e.g., `fullstack-guardian, test-master`). Must resolve to existing skill directories.\n\n**Domain values:**\n`language` · `backend` · `frontend` · `infrastructure` · `api-architecture` · `quality` · `devops` · `security` · `data-ml` · `platform` · `specialized` · `workflow`\n\n---\n\n### Reference File Standards\n\nReference files follow the [Agent Skills specification](https://agentskills.io/specification). No specific headers are required.\n\n**Guidelines:**\n- 100-600 lines per reference file\n- Keep files focused on a single topic\n- Complete, working code examples with TypeScript types\n- Cross-reference related skills where relevant\n- Include \"when to use\" and \"when not to use\" guidance\n- Practical patterns over theoretical explanations\n\n### Framework Idiom Principle\n\nReference files for framework-specific skills must reflect the idiomatic best practices of that framework, not generic patterns applied uniformly across all skills. If a framework provides a built-in mechanism (e.g., global error handling, middleware, dependency injection), reference examples should use it rather than duplicating that behavior manually. Each framework's conventions for error handling, architecture, and code organization take precedence over cross-project consistency.\n\n---\n\n### Documentation Backlink\n\nEvery `SKILL.md` MUST end with a single canonical Documentation link pointing back to the docs site:\n\n```\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- `{domain}` is `metadata.domain` from the frontmatter and must be one of the `domains` in `pyproject.toml` (`[tool.skill-linter]`); the docs site build fails on any other value.\n- `{skill-name}` is the skill's directory name.\n- Format is exactly markdown link syntax (`[text](url)`), not a bare URL. Strict CommonMark renderers do not auto-link bare URLs, and the line's purpose is to render as a real `<a href>` on aggregators (skills.sh, etc.) for SEO backlinks.\n\n**Why two surfaces, two behaviors:**\n\n- Aggregators consume raw `SKILL.md` from GitHub and render the line as a hyperlink. This is the SEO point.\n- The docs site itself would render this line as a self-link, which is redundant. The site's content sync (`site/src/components/site-sync/`, configured by `site/scripts/sync-content.mjs`) strips the line, and the credit line above it, at build time so the docs site, public markdown mirrors, `llms.txt`, and `llms-full.txt` never show it.\n\n**When adding or renaming a skill:** update both the directory name (which becomes `{skill-name}`) and the `metadata.domain` consistently with this URL formula, otherwise the backlink will 404.\n\n### Maintainer Credit\n\nSkills authored by @jeffallan (those with `metadata.company: https://synergetic.solutions`) carry one more line, directly above the Documentation backlink, separated by a blank line. It matches the docs site footer credit:\n\n```\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- The Documentation backlink stays the last line\n- Contributed skills do not get this line unless their `metadata.company` is set to the same URL\n- `syncSkillPages` strips it at build time alongside the Documentation backlink, so the docs site, markdown mirrors, and `llms` files never show it (the site footer shows the same credit once per page)\n\n---\n\n### Progressive Disclosure Architecture\n\n**Tier 1 - SKILL.md (~80-100 lines)**\n- Role definition and expertise level\n- When-to-use guidance (triggers)\n- Core workflow (5 steps)\n- Constraints (MUST DO / MUST NOT DO)\n- Routing table to references\n\n**Tier 2 - Reference Files (100-600 lines each)**\n- Deep technical content\n- Complete code examples\n- Edge cases and anti-patterns\n- Loaded only when context requires\n\n**Goal:** 50% token reduction through selective loading.\n\n---\n\n## Project Workflow\n\n### When Creating New Skills\n\n1. Check existing skills for overlap\n2. Write SKILL.md with capability + trigger description (no process steps)\n3. Create reference files for deep content (100+ lines)\n4. Add routing table linking topics to references\n5. Append the canonical Documentation backlink as the last line (see Documentation Backlink above)\n6. Test skill triggers with realistic prompts\n7. Add the skill to SKILLS_GUIDE.md in its category\n\n### When Modifying Skills\n\n1. Read the full current skill before editing\n2. Maintain capability + trigger description format (no process steps)\n3. Preserve progressive disclosure structure\n4. Update related cross-references\n5. Verify routing table accuracy\n\n### When Accepting Contributions\n\nEvery external contribution (issue or PR) gets the `gratitude` label, whatever its outcome.\n\nFor each PR:\n\n1. Discuss fit with project goals before any merge decision (scope, overlap with existing skills, promotional content), then verify the content\n2. Squash-merge with subject `type(scope): title (#N)`\n   - Conflicted fork PRs: squash locally (`git merge --squash`), add `Co-authored-by:` with the contributor's commit email, then close the PR with a comment pointing at the commit\n3. Record it in a separate commit, `docs(changelog): record #N <summary> under Unreleased`, adding an entry under `[Unreleased]` → Added/Changed/Fixed that ends with `(#N)` (append `closing #ISSUE` when it resolves one)\n4. If skill or reference counts changed, run `uvx skill-linter@0.2.0 stamp` in the same push, otherwise `stamp --check` fails CI on main\n5. Post-merge corrections go in their own commit with their own `### Changed` entry\n\nAt release time, credit contributors (including issue reporters) in a `### Contributors` section of the version entry:\n\n```markdown\n### Contributors\n- @handle: What they contributed (#N)\n```\n\n---\n\n## Shared Code from the Handbook\n\nThe site code under `site/src/components/site-*/`, the shared CI workflows and `.github/workflows/handbook-update.yml` come from the private handbook through Copier (`.copier-answers.yml`). Change them in the handbook, not here.\n\nThe versions of the shared site packages come from the handbook too (`personal-sites/package.json`): `astro`, `@astrojs/starlight`, `@jdevalk/astro-seo-graph`, `astro-og-canvas`, `starlight-links-validator`, the `@fontsource` fonts, `js-yaml`, `smol-toml`, `sharp`, `prettier` and `prettier-plugin-astro`. Don't bump them in `site/package.json`. Bump them in the handbook. **They reach this repo only with a handbook release**, as the update PR that `handbook-update.yml` opens, carrying the new `site/package.json` and lockfile.\n\nThe same goes for the actions in the workflows the handbook delivers (`handbook-update.yml`, `docs-site.yml`, `docs-deploy.yml`). Dependabot here skips those files (`exclude-paths`), and the handbook bumps them.\n\n## Release Checklist\n\nWhen releasing a new version, follow these steps.\n\n### 1. Update Version and Counts\n\nVersion and counts are managed through `version.json`:\n\n```json\n{\n  \"version\": \"0.4.2\",\n  \"skillCount\": 65,\n  \"workflowCount\": 9,\n  \"referenceFileCount\": 355\n}\n```\n\n**To release a new version:**\n\n1. Update the `version` field in `version.json`\n2. Stamp the counts and version:\n\n```bash\nuvx skill-linter@0.2.0 stamp\n```\n\nThis computes the counts from the filesystem (skills, per-domain skills, references, workflows), writes them to `version.json`, and stamps the version and counts into the files listed under `[[tool.skill-linter.stamp.files]]` in `pyproject.toml` (README.md, QUICKSTART.md, plugin.json, marketplace.json, the docs site and others).\n\n`uvx skill-linter@0.2.0 stamp --check` reports stale files without writing (CI runs it).\n\n### 2. Update CHANGELOG.md\n\nAdd new version entry at the top following Keep a Changelog format:\n\n```markdown\n## [X.Y.Z] - YYYY-MM-DD\n\n### Added\n- New features, skills, commands\n\n### Changed\n- Modified functionality, updated skills\n\n### Fixed\n- Bug fixes\n```\n\nAdd version comparison link at bottom:\n```markdown\n[X.Y.Z]: https://github.com/jeffallan/claude-skills/compare/vPREVIOUS...vX.Y.Z\n```\n\n### 3. Update Documentation for New/Modified Content\n\n**For new skills:**\n- Add to `SKILLS_GUIDE.md` in appropriate category\n- Add to decision trees if applicable\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For new commands:**\n- Add to `docs/WORKFLOW_COMMANDS.md`\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For modified skills/commands:**\n- Update any cross-references\n- Update SKILLS_GUIDE.md if triggers changed\n\n### 4. Generate Social Preview (conditional)\n\nThe social preview embeds skill, workflow, and reference counts but **not** the version. Only regenerate when at least one of `skillCount`, `workflowCount`, or `referenceFileCount` in `version.json` changed in this release — patch releases that only fix docs or a single skill normally do not need it.\n\nWhen regeneration is needed:\n\n```bash\nnpx -y -p puppeteer node ./assets/capture-screenshot.js\n```\n\nThis creates `assets/social-preview.png` from `assets/social-preview.html`.\n\n### 5. Validate\n\n**Critical:** Run validation before release to prevent broken skills from being published.\n\n```bash\nmake validate\n```\n\nThis runs [skill-linter](https://pypi.org/project/skill-linter/), configured in `pyproject.toml` (`[tool.skill-linter]`), and is the same check CI runs:\n\n- `skill-linter release` checks the shipped plugin tree (manifest, commands map, marketplace entry), then runs every other rule: frontmatter, names and descriptions, sections and references, Markdown syntax (unclosed fences, table separators, comments and column counts), commands, the changelog and stamped counts\n- `skill-linter stamp --check` fails when a stamped count or version is stale\n\n**Options:**\n```bash\nuvx skill-linter@0.2.0 check --skill react-expert   # One skill's rules only\nuvx skill-linter@0.2.0 check --only markdown        # One rule group\nuvx skill-linter@0.2.0 check --format json          # JSON output\nuvx skill-linter@0.2.0 rules                        # Every rule, its group and severity\n```\n\n**Exit codes:** 0 = no errors (warnings OK), 1 = errors found\n\n### 6. Final Verification\n\nAfter running validation, manually verify:\n\n```bash\n# Check no old version references remain (except historical changelog)\ngrep -r \"OLD_VERSION\" --include=\"*.md\" --include=\"*.json\" --include=\"*.html\"\n```\n\n### 7. Plugin Branch\n\nThe plugin ships from the `plugin` branch, not from `main`. Pushing the `vX.Y.Z` tag runs `.github/workflows/publish-plugin.yml`, which rebuilds that branch from `scripts/build-plugin-dist.sh` (only `.claude-plugin/plugin.json`, `skills/`, `commands/`, `references/`, `README.md`, `LICENSE`). Both the marketplace entry and the Claude plugin directory read from it.\n\n- Never edit the `plugin` branch by hand; the next publish overwrites it\n- After the tag push, confirm the workflow succeeded and the branch's latest commit names the new tag\n- A file the plugin needs at runtime must live under one of the shipped paths, or it will be missing from installs\n\n---\n\n## Attribution\n\nBehavioral patterns and process discipline adapted from:\n- **[obra/superpowers](https://github.com/obra/superpowers)** by Jesse Vincent (@obra)\n- License: MIT\n\nResearch documented in: `research/superpowers.md`\n","skills/angular-architect/SKILL.md":"---\nname: angular-architect\ndescription: Generates Angular 17+ standalone components, configures advanced routing with lazy loading and guards, implements NgRx state management, applies RxJS patterns, and optimizes bundle performance. Use when building Angular 17+ applications with standalone components or signals, setting up NgRx stores, establishing RxJS reactive patterns, performance tuning, or writing Angular tests for enterprise apps.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: frontend\n  triggers: Angular, Angular 17, standalone components, signals, RxJS, NgRx, Angular performance, Angular routing, Angular testing\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: typescript-pro, test-master\n---\n\n# Angular Architect\n\nSenior Angular architect specializing in Angular 17+ with standalone components, signals, and enterprise-grade application development.\n\n## Core Workflow\n\n1. **Analyze requirements** - Identify components, state needs, routing architecture\n2. **Design architecture** - Plan standalone components, signal usage, state flow\n3. **Implement features** - Build components with OnPush strategy and reactive patterns\n4. **Manage state** - Setup NgRx store, effects, selectors as needed; verify store hydration and action flow with Redux DevTools before proceeding\n5. **Optimize** - Apply performance best practices and bundle optimization; run `ng build --configuration production` to verify bundle size and flag regressions\n6. **Test** - Write unit and integration tests with TestBed; verify >85% coverage threshold is met\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Components | `references/components.md` | Standalone components, signals, input/output |\n| RxJS | `references/rxjs.md` | Observables, operators, subjects, error handling |\n| NgRx | `references/ngrx.md` | Store, effects, selectors, entity adapter |\n| Routing | `references/routing.md` | Router config, guards, lazy loading, resolvers |\n| Testing | `references/testing.md` | TestBed, component tests, service tests |\n\n## Key Patterns\n\n### Standalone Component with OnPush and Signals\n\n```typescript\nimport { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n  selector: 'app-user-card',\n  standalone: true,\n  imports: [CommonModule],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <div class=\"user-card\">\n      <h2>{{ fullName() }}</h2>\n      <button (click)=\"onSelect()\">Select</button>\n    </div>\n  `,\n})\nexport class UserCardComponent {\n  firstName = input.required<string>();\n  lastName = input.required<string>();\n  selected = output<string>();\n\n  fullName = computed(() => `${this.firstName()} ${this.lastName()}`);\n\n  onSelect(): void {\n    this.selected.emit(this.fullName());\n  }\n}\n```\n\n### RxJS Subscription Management with `takeUntilDestroyed`\n\n```typescript\nimport { Component, OnInit, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { UserService } from './user.service';\n\n@Component({ selector: 'app-users', standalone: true, template: `...` })\nexport class UsersComponent implements OnInit {\n  private userService = inject(UserService);\n  // DestroyRef is captured at construction time for use in ngOnInit\n  private destroyRef = inject(DestroyRef);\n\n  ngOnInit(): void {\n    this.userService.getUsers()\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe({\n        next: (users) => { /* handle */ },\n        error: (err) => console.error('Failed to load users', err),\n      });\n  }\n}\n```\n\n### NgRx Action / Reducer / Selector\n\n```typescript\n// actions\nexport const loadUsers = createAction('[Users] Load Users');\nexport const loadUsersSuccess = createAction('[Users] Load Users Success', props<{ users: User[] }>());\nexport const loadUsersFailure = createAction('[Users] Load Users Failure', props<{ error: string }>());\n\n// reducer\nexport interface UsersState { users: User[]; loading: boolean; error: string | null; }\nconst initialState: UsersState = { users: [], loading: false, error: null };\n\nexport const usersReducer = createReducer(\n  initialState,\n  on(loadUsers, (state) => ({ ...state, loading: true, error: null })),\n  on(loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),\n  on(loadUsersFailure, (state, { error }) => ({ ...state, error, loading: false })),\n);\n\n// selectors\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\nexport const selectAllUsers = createSelector(selectUsersState, (s) => s.users);\nexport const selectUsersLoading = createSelector(selectUsersState, (s) => s.loading);\n```\n\n## Constraints\n\n### MUST DO\n- Use standalone components (Angular 17+ default)\n- Use signals for reactive state where appropriate\n- Use OnPush change detection strategy\n- Use strict TypeScript configuration\n- Implement proper error handling in RxJS streams\n- Use `trackBy` functions in `*ngFor` loops\n- Write tests with >85% coverage\n- Follow Angular style guide\n\n### MUST NOT DO\n- Use NgModule-based components (except when required for compatibility)\n- Forget to unsubscribe from observables (use `takeUntilDestroyed` or `async` pipe)\n- Use async operations without proper error handling\n- Skip accessibility attributes\n- Expose sensitive data in client-side code\n- Use `any` type without justification\n- Mutate state directly in NgRx\n- Skip unit tests for critical logic\n\n## Output Templates\n\nWhen implementing Angular features, provide:\n1. Component file with standalone configuration\n2. Service file if business logic is involved\n3. State management files if using NgRx\n4. Test file with comprehensive test cases\n5. Brief explanation of architectural decisions\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/frontend/angular-architect/)\n","skills/angular-architect/references/components.md":"# Standalone Components & Signals\n\n## Standalone Component Pattern\n\n```typescript\nimport { Component, signal, computed, effect } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n@Component({\n  selector: 'app-user-profile',\n  standalone: true,\n  imports: [CommonModule, FormsModule],\n  templateUrl: './user-profile.component.html',\n  styleUrl: './user-profile.component.scss',\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserProfileComponent {\n  // Signal-based state\n  count = signal(0);\n  doubleCount = computed(() => this.count() * 2);\n\n  constructor() {\n    // Side effects\n    effect(() => {\n      console.log(`Count is: ${this.count()}`);\n    });\n  }\n\n  increment() {\n    this.count.update(value => value + 1);\n  }\n}\n```\n\n## Input/Output with Signals\n\n```typescript\nimport { Component, input, output, model } from '@angular/core';\n\n@Component({\n  selector: 'app-search-box',\n  standalone: true,\n  template: `\n    <input\n      [value]=\"query()\"\n      (input)=\"onQueryChange($event)\"\n      [placeholder]=\"placeholder()\" />\n  `\n})\nexport class SearchBoxComponent {\n  // Signal inputs (Angular 17.1+)\n  placeholder = input<string>('Search...');\n  initialQuery = input<string>('');\n\n  // Signal outputs\n  queryChange = output<string>();\n\n  // Two-way binding with model signal\n  query = model<string>('');\n\n  onQueryChange(event: Event) {\n    const value = (event.target as HTMLInputElement).value;\n    this.query.set(value);\n    this.queryChange.emit(value);\n  }\n}\n\n// Parent usage\n@Component({\n  template: `\n    <app-search-box\n      [(query)]=\"searchQuery\"\n      [placeholder]=\"'Find users...'\"\n      (queryChange)=\"onSearch($event)\" />\n  `\n})\nexport class ParentComponent {\n  searchQuery = signal('');\n\n  onSearch(query: string) {\n    console.log('Searching:', query);\n  }\n}\n```\n\n## Smart vs Dumb Components\n\n```typescript\n// Smart Component (Container)\n@Component({\n  selector: 'app-users-container',\n  standalone: true,\n  imports: [UserListComponent],\n  template: `\n    <app-user-list\n      [users]=\"users()\"\n      [loading]=\"loading()\"\n      (userSelected)=\"onUserSelected($event)\" />\n  `\n})\nexport class UsersContainerComponent {\n  private usersService = inject(UsersService);\n\n  users = signal<User[]>([]);\n  loading = signal(true);\n\n  constructor() {\n    effect(() => {\n      this.usersService.getUsers().subscribe({\n        next: users => {\n          this.users.set(users);\n          this.loading.set(false);\n        },\n        error: err => console.error(err)\n      });\n    });\n  }\n\n  onUserSelected(user: User) {\n    // Handle business logic\n  }\n}\n\n// Dumb Component (Presentational)\n@Component({\n  selector: 'app-user-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div (click)=\"userSelected.emit(user)\">\n          {{ user.name }}\n        </div>\n      }\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserListComponent {\n  users = input.required<User[]>();\n  loading = input<boolean>(false);\n  userSelected = output<User>();\n}\n```\n\n## Content Projection\n\n```typescript\n// Card component with multiple slots\n@Component({\n  selector: 'app-card',\n  standalone: true,\n  template: `\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <ng-content select=\"[header]\"></ng-content>\n      </div>\n      <div class=\"card-body\">\n        <ng-content></ng-content>\n      </div>\n      <div class=\"card-footer\">\n        <ng-content select=\"[footer]\"></ng-content>\n      </div>\n    </div>\n  `\n})\nexport class CardComponent {}\n\n// Usage\n@Component({\n  template: `\n    <app-card>\n      <h2 header>Card Title</h2>\n      <p>Card content goes here</p>\n      <button footer>Action</button>\n    </app-card>\n  `\n})\nexport class ParentComponent {}\n```\n\n## Dependency Injection\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { UserService } from './user.service';\n\n@Component({\n  selector: 'app-user-dashboard',\n  standalone: true\n})\nexport class UserDashboardComponent {\n  // Modern inject() API\n  private userService = inject(UserService);\n  private router = inject(Router);\n\n  // Optional dependency\n  private logger = inject(LoggerService, { optional: true });\n\n  users = signal<User[]>([]);\n\n  ngOnInit() {\n    this.loadUsers();\n  }\n\n  loadUsers() {\n    this.userService.getUsers().subscribe({\n      next: users => this.users.set(users),\n      error: err => this.logger?.error('Failed to load users', err)\n    });\n  }\n}\n```\n\n## New Control Flow (@if, @for)\n\n```typescript\n@Component({\n  template: `\n    <!-- @if instead of *ngIf -->\n    @if (user(); as currentUser) {\n      <div>Hello, {{ currentUser.name }}</div>\n    } @else if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      <div>Please log in</div>\n    }\n\n    <!-- @for instead of *ngFor -->\n    @for (item of items(); track item.id) {\n      <div>{{ item.name }}</div>\n    } @empty {\n      <div>No items found</div>\n    }\n\n    <!-- @switch instead of *ngSwitch -->\n    @switch (status()) {\n      @case ('pending') {\n        <span>Pending...</span>\n      }\n      @case ('success') {\n        <span>Success!</span>\n      }\n      @default {\n        <span>Unknown</span>\n      }\n    }\n  `\n})\nexport class ModernControlFlowComponent {\n  user = signal<User | null>(null);\n  loading = signal(false);\n  items = signal<Item[]>([]);\n  status = signal<'pending' | 'success' | 'error'>('pending');\n}\n```\n\n## Performance: OnPush & TrackBy\n\n```typescript\n@Component({\n  selector: 'app-product-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @for (product of products(); track trackByProductId($index, product)) {\n      <app-product-card [product]=\"product\" />\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ProductListComponent {\n  products = input.required<Product[]>();\n\n  // TrackBy for optimal rendering\n  trackByProductId(index: number, product: Product): number {\n    return product.id;\n  }\n}\n```\n\n## Quick Reference\n\n| Pattern | Angular 17+ Approach |\n|---------|---------------------|\n| Component | Standalone by default |\n| State | Signals (`signal()`, `computed()`) |\n| Input | `input()`, `input.required()` |\n| Output | `output<T>()` |\n| Two-way | `model<T>()` |\n| DI | `inject()` function |\n| Control Flow | `@if`, `@for`, `@switch` |\n| Change Detection | `ChangeDetectionStrategy.OnPush` |\n","skills/angular-architect/references/ngrx.md":"# NgRx State Management\n\n## Store Setup\n\n```typescript\n// app.config.ts\nimport { provideStore } from '@ngrx/store';\nimport { provideEffects } from '@ngrx/effects';\nimport { provideStoreDevtools } from '@ngrx/store-devtools';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideStore({\n      users: usersReducer,\n      products: productsReducer\n    }),\n    provideEffects([UsersEffects, ProductsEffects]),\n    provideStoreDevtools({\n      maxAge: 25,\n      logOnly: !isDevMode()\n    })\n  ]\n};\n```\n\n## Actions (Modern)\n\n```typescript\n// users.actions.ts\nimport { createActionGroup, emptyProps, props } from '@ngrx/store';\nimport { User } from './user.model';\n\nexport const UsersActions = createActionGroup({\n  source: 'Users',\n  events: {\n    'Load Users': emptyProps(),\n    'Load Users Success': props<{ users: User[] }>(),\n    'Load Users Failure': props<{ error: string }>(),\n\n    'Add User': props<{ user: User }>(),\n    'Add User Success': props<{ user: User }>(),\n    'Add User Failure': props<{ error: string }>(),\n\n    'Update User': props<{ id: string; changes: Partial<User> }>(),\n    'Update User Success': props<{ user: User }>(),\n\n    'Delete User': props<{ id: string }>(),\n    'Delete User Success': props<{ id: string }>()\n  }\n});\n```\n\n## Reducer with Entity Adapter\n\n```typescript\n// users.reducer.ts\nimport { createReducer, on } from '@ngrx/store';\nimport { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';\nimport { UsersActions } from './users.actions';\nimport { User } from './user.model';\n\nexport interface UsersState extends EntityState<User> {\n  loading: boolean;\n  error: string | null;\n  selectedUserId: string | null;\n}\n\nexport const usersAdapter: EntityAdapter<User> = createEntityAdapter<User>({\n  selectId: (user: User) => user.id,\n  sortComparer: (a, b) => a.name.localeCompare(b.name)\n});\n\nconst initialState: UsersState = usersAdapter.getInitialState({\n  loading: false,\n  error: null,\n  selectedUserId: null\n});\n\nexport const usersReducer = createReducer(\n  initialState,\n\n  // Load users\n  on(UsersActions.loadUsers, (state) => ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n\n  on(UsersActions.loadUsersSuccess, (state, { users }) =>\n    usersAdapter.setAll(users, {\n      ...state,\n      loading: false\n    })\n  ),\n\n  on(UsersActions.loadUsersFailure, (state, { error }) => ({\n    ...state,\n    loading: false,\n    error\n  })),\n\n  // Add user\n  on(UsersActions.addUserSuccess, (state, { user }) =>\n    usersAdapter.addOne(user, state)\n  ),\n\n  // Update user\n  on(UsersActions.updateUserSuccess, (state, { user }) =>\n    usersAdapter.updateOne(\n      { id: user.id, changes: user },\n      state\n    )\n  ),\n\n  // Delete user\n  on(UsersActions.deleteUserSuccess, (state, { id }) =>\n    usersAdapter.removeOne(id, state)\n  )\n);\n```\n\n## Selectors\n\n```typescript\n// users.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport { usersAdapter, UsersState } from './users.reducer';\n\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\n\n// Entity adapter selectors\nconst {\n  selectIds,\n  selectEntities,\n  selectAll,\n  selectTotal\n} = usersAdapter.getSelectors();\n\nexport const selectUserIds = createSelector(\n  selectUsersState,\n  selectIds\n);\n\nexport const selectUserEntities = createSelector(\n  selectUsersState,\n  selectEntities\n);\n\nexport const selectAllUsers = createSelector(\n  selectUsersState,\n  selectAll\n);\n\nexport const selectUsersTotal = createSelector(\n  selectUsersState,\n  selectTotal\n);\n\nexport const selectUsersLoading = createSelector(\n  selectUsersState,\n  (state) => state.loading\n);\n\nexport const selectUsersError = createSelector(\n  selectUsersState,\n  (state) => state.error\n);\n\n// Parameterized selector\nexport const selectUserById = (id: string) =>\n  createSelector(\n    selectUserEntities,\n    (entities) => entities[id]\n  );\n\n// Composed selector\nexport const selectActiveUsers = createSelector(\n  selectAllUsers,\n  (users) => users.filter(user => user.isActive)\n);\n\n// Selector with multiple inputs\nexport const selectUserWithPosts = createSelector(\n  selectUserById,\n  selectAllPosts,\n  (user, posts) => ({\n    user,\n    posts: posts.filter(post => post.userId === user?.id)\n  })\n);\n```\n\n## Effects\n\n```typescript\n// users.effects.ts\nimport { Injectable, inject } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { catchError, map, mergeMap, exhaustMap } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\n\n@Injectable()\nexport class UsersEffects {\n  private actions$ = inject(Actions);\n  private usersService = inject(UsersService);\n\n  // Load users effect\n  loadUsers$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.loadUsers),\n      mergeMap(() =>\n        this.usersService.getAll().pipe(\n          map(users => UsersActions.loadUsersSuccess({ users })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Add user effect (exhaustMap prevents duplicate submits)\n  addUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.addUser),\n      exhaustMap(({ user }) =>\n        this.usersService.create(user).pipe(\n          map(createdUser => UsersActions.addUserSuccess({ user: createdUser })),\n          catchError(error =>\n            of(UsersActions.addUserFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Update user effect\n  updateUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.updateUser),\n      mergeMap(({ id, changes }) =>\n        this.usersService.update(id, changes).pipe(\n          map(user => UsersActions.updateUserSuccess({ user })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Delete user effect\n  deleteUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.deleteUser),\n      mergeMap(({ id }) =>\n        this.usersService.delete(id).pipe(\n          map(() => UsersActions.deleteUserSuccess({ id })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Non-dispatching effect (side effect only)\n  logUserActions$ = createEffect(\n    () =>\n      this.actions$.pipe(\n        ofType(\n          UsersActions.addUserSuccess,\n          UsersActions.updateUserSuccess,\n          UsersActions.deleteUserSuccess\n        ),\n        tap(action => console.log('User action:', action))\n      ),\n    { dispatch: false }\n  );\n}\n```\n\n## Component Integration\n\n```typescript\n// users-list.component.ts\nimport { Component, inject } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { UsersActions } from './store/users.actions';\nimport {\n  selectAllUsers,\n  selectUsersLoading,\n  selectUsersError\n} from './store/users.selectors';\n\n@Component({\n  selector: 'app-users-list',\n  standalone: true,\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else if (error(); as err) {\n      <div>Error: {{ err }}</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div>\n          {{ user.name }}\n          <button (click)=\"onDelete(user.id)\">Delete</button>\n        </div>\n      }\n    }\n  `\n})\nexport class UsersListComponent {\n  private store = inject(Store);\n\n  // Select data as signals\n  users = toSignal(this.store.select(selectAllUsers), { initialValue: [] });\n  loading = toSignal(this.store.select(selectUsersLoading), { initialValue: false });\n  error = toSignal(this.store.select(selectUsersError), { initialValue: null });\n\n  ngOnInit() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  onDelete(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n}\n```\n\n## Facade Pattern\n\n```typescript\n// users.facade.ts\n@Injectable({ providedIn: 'root' })\nexport class UsersFacade {\n  private store = inject(Store);\n\n  // Selectors\n  users$ = this.store.select(selectAllUsers);\n  loading$ = this.store.select(selectUsersLoading);\n  error$ = this.store.select(selectUsersError);\n\n  // Actions\n  loadUsers() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  addUser(user: User) {\n    this.store.dispatch(UsersActions.addUser({ user }));\n  }\n\n  updateUser(id: string, changes: Partial<User>) {\n    this.store.dispatch(UsersActions.updateUser({ id, changes }));\n  }\n\n  deleteUser(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n\n  getUserById(id: string) {\n    return this.store.select(selectUserById(id));\n  }\n}\n\n// Usage in component\n@Component({\n  selector: 'app-users',\n  standalone: true\n})\nexport class UsersComponent {\n  private facade = inject(UsersFacade);\n\n  users = toSignal(this.facade.users$, { initialValue: [] });\n  loading = toSignal(this.facade.loading$, { initialValue: false });\n\n  ngOnInit() {\n    this.facade.loadUsers();\n  }\n\n  onAdd(user: User) {\n    this.facade.addUser(user);\n  }\n}\n```\n\n## Quick Reference\n\n| Concept | Usage |\n|---------|-------|\n| Actions | `createActionGroup()` |\n| Reducer | `createReducer()`, `on()` |\n| Entity | `createEntityAdapter()` |\n| Selectors | `createSelector()`, `createFeatureSelector()` |\n| Effects | `createEffect()`, `ofType()` |\n| Store | `inject(Store)`, `store.select()`, `store.dispatch()` |\n| DevTools | `provideStoreDevtools()` |\n| Testing | Mock store, marble testing |\n","skills/angular-architect/references/routing.md":"# Angular Routing\n\n## Routes Configuration\n\n```typescript\n// app.routes.ts\nimport { Routes } from '@angular/router';\nimport { HomeComponent } from './home/home.component';\n\nexport const routes: Routes = [\n  {\n    path: '',\n    redirectTo: '/home',\n    pathMatch: 'full'\n  },\n  {\n    path: 'home',\n    component: HomeComponent,\n    title: 'Home'\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./users/users.component').then(m => m.UsersComponent),\n    title: 'Users'\n  },\n  {\n    path: 'users/:id',\n    loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent),\n    canActivate: [authGuard],\n    resolve: { user: userResolver }\n  },\n  {\n    path: 'admin',\n    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),\n    canActivate: [authGuard, adminGuard]\n  },\n  {\n    path: '**',\n    loadComponent: () => import('./not-found/not-found.component').then(m => m.NotFoundComponent),\n    title: '404 Not Found'\n  }\n];\n\n// app.config.ts\nimport { provideRouter, withComponentInputBinding } from '@angular/router';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideRouter(\n      routes,\n      withComponentInputBinding(),  // Bind route params to @Input()\n      withViewTransitions(),        // Enable view transitions\n      withPreloading(PreloadAllModules)\n    )\n  ]\n};\n```\n\n## Lazy Loading\n\n```typescript\n// Feature routes\n// admin/admin.routes.ts\nimport { Routes } from '@angular/router';\n\nexport const ADMIN_ROUTES: Routes = [\n  {\n    path: '',\n    loadComponent: () => import('./admin-dashboard.component').then(m => m.AdminDashboardComponent)\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./admin-users.component').then(m => m.AdminUsersComponent)\n  },\n  {\n    path: 'settings',\n    loadComponent: () => import('./admin-settings.component').then(m => m.AdminSettingsComponent)\n  }\n];\n```\n\n## Functional Guards\n\n```typescript\n// guards/auth.guard.ts\nimport { inject } from '@angular/core';\nimport { Router, CanActivateFn } from '@angular/router';\nimport { AuthService } from '../services/auth.service';\n\nexport const authGuard: CanActivateFn = (route, state) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.isAuthenticated()) {\n    return true;\n  }\n\n  // Redirect to login with return URL\n  return router.createUrlTree(['/login'], {\n    queryParams: { returnUrl: state.url }\n  });\n};\n\n// Admin guard\nexport const adminGuard: CanActivateFn = () => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.hasRole('admin')) {\n    return true;\n  }\n\n  return router.createUrlTree(['/unauthorized']);\n};\n\n// Can deactivate (unsaved changes)\nexport const canDeactivateGuard: CanDeactivateFn<FormComponent> = (component) => {\n  if (component.hasUnsavedChanges()) {\n    return confirm('You have unsaved changes. Are you sure you want to leave?');\n  }\n  return true;\n};\n```\n\n## Resolvers\n\n```typescript\n// resolvers/user.resolver.ts\nimport { inject } from '@angular/core';\nimport { ResolveFn } from '@angular/router';\nimport { catchError, of } from 'rxjs';\nimport { User } from '../models/user.model';\nimport { UsersService } from '../services/users.service';\n\nexport const userResolver: ResolveFn<User | null> = (route, state) => {\n  const usersService = inject(UsersService);\n  const id = route.paramMap.get('id')!;\n\n  return usersService.getById(id).pipe(\n    catchError(() => of(null))\n  );\n};\n\n// Component receives resolved data\n@Component({\n  selector: 'app-user-detail',\n  standalone: true,\n  template: `\n    @if (user) {\n      <h1>{{ user.name }}</h1>\n    } @else {\n      <p>User not found</p>\n    }\n  `\n})\nexport class UserDetailComponent {\n  user = input<User | null>(null);  // Resolved data bound as input\n}\n```\n\n## Route Parameters\n\n```typescript\nimport { Component, inject, input } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n  selector: 'app-product-detail',\n  standalone: true\n})\nexport class ProductDetailComponent {\n  private route = inject(ActivatedRoute);\n  private router = inject(Router);\n\n  // Modern approach: route params as inputs\n  id = input.required<string>();\n\n  // Legacy approach: subscribe to params\n  ngOnInit() {\n    this.route.paramMap.subscribe(params => {\n      const id = params.get('id');\n      this.loadProduct(id);\n    });\n\n    // Query params\n    this.route.queryParamMap.subscribe(params => {\n      const filter = params.get('filter');\n      const sort = params.get('sort');\n    });\n  }\n\n  // Navigate programmatically\n  goToEdit() {\n    this.router.navigate(['/products', this.id(), 'edit']);\n  }\n\n  // Navigate with query params\n  applyFilter(filter: string) {\n    this.router.navigate([], {\n      relativeTo: this.route,\n      queryParams: { filter },\n      queryParamsHandling: 'merge'  // Preserve other params\n    });\n  }\n}\n```\n\n## Router Events\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { Router, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n  selector: 'app-root',\n  standalone: true\n})\nexport class AppComponent {\n  private router = inject(Router);\n  loading = signal(false);\n\n  constructor() {\n    // Show loading on navigation start\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationStart)\n    ).subscribe(() => {\n      this.loading.set(true);\n    });\n\n    // Hide loading on navigation end\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationEnd)\n    ).subscribe(() => {\n      this.loading.set(false);\n    });\n\n    // Handle navigation errors\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationError)\n    ).subscribe((event: NavigationError) => {\n      console.error('Navigation error:', event.error);\n      this.loading.set(false);\n    });\n  }\n}\n```\n\n## Child Routes & Outlets\n\n```typescript\n// Parent route with child routes\nconst routes: Routes = [\n  {\n    path: 'dashboard',\n    component: DashboardComponent,\n    children: [\n      {\n        path: 'stats',\n        component: StatsComponent,\n        outlet: 'panel'  // Named outlet\n      },\n      {\n        path: 'charts',\n        component: ChartsComponent,\n        outlet: 'panel'\n      }\n    ]\n  }\n];\n\n// Dashboard component template\n@Component({\n  template: `\n    <div class=\"dashboard\">\n      <div class=\"main\">\n        <router-outlet></router-outlet>  <!-- Primary outlet -->\n      </div>\n      <div class=\"panel\">\n        <router-outlet name=\"panel\"></router-outlet>  <!-- Named outlet -->\n      </div>\n    </div>\n  `\n})\nexport class DashboardComponent {}\n\n// Navigate to named outlet\nthis.router.navigate(['/dashboard', { outlets: { panel: ['stats'] } }]);\n```\n\n## Preloading Strategies\n\n```typescript\n// Custom preloading strategy\nimport { Injectable } from '@angular/core';\nimport { PreloadingStrategy, Route } from '@angular/router';\nimport { Observable, of, timer } from 'rxjs';\nimport { mergeMap } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class CustomPreloadingStrategy implements PreloadingStrategy {\n  preload(route: Route, load: () => Observable<any>): Observable<any> {\n    // Only preload routes with data.preload = true\n    if (route.data?.['preload']) {\n      const delay = route.data?.['preloadDelay'] || 0;\n      return timer(delay).pipe(\n        mergeMap(() => load())\n      );\n    }\n    return of(null);\n  }\n}\n\n// Route config with preload data\nconst routes: Routes = [\n  {\n    path: 'important',\n    loadChildren: () => import('./important/important.routes'),\n    data: { preload: true, preloadDelay: 2000 }\n  }\n];\n\n// Register in app config\nprovideRouter(routes, withPreloading(CustomPreloadingStrategy))\n```\n\n## Route Guards with Observables\n\n```typescript\nexport const dataGuard: CanActivateFn = (route, state) => {\n  const dataService = inject(DataService);\n  const router = inject(Router);\n\n  return dataService.checkAccess(route.params['id']).pipe(\n    map(hasAccess => {\n      if (hasAccess) {\n        return true;\n      }\n      return router.createUrlTree(['/no-access']);\n    }),\n    catchError(() => {\n      return of(router.createUrlTree(['/error']));\n    })\n  );\n};\n```\n\n## Quick Reference\n\n| Feature | Usage |\n|---------|-------|\n| Routes | `Routes` array in app.routes.ts |\n| Lazy load | `loadComponent()`, `loadChildren()` |\n| Guards | `CanActivateFn`, `CanDeactivateFn` |\n| Resolvers | `ResolveFn<T>` |\n| Params | `route.paramMap`, `input<T>()` |\n| Query | `route.queryParamMap` |\n| Navigate | `router.navigate()`, `routerLink` |\n| Events | `router.events` |\n| Outlets | `<router-outlet name=\"...\">` |\n| Preload | `withPreloading()` |\n","skills/angular-architect/references/rxjs.md":"# RxJS Patterns\n\n## Essential Operators\n\n```typescript\nimport { Component, inject, signal } from '@angular/core';\nimport {\n  map, filter, switchMap, catchError,\n  debounceTime, distinctUntilChanged,\n  tap, shareReplay, takeUntil\n} from 'rxjs/operators';\nimport { Subject, of, EMPTY } from 'rxjs';\n\n@Component({\n  selector: 'app-search',\n  standalone: true\n})\nexport class SearchComponent {\n  private searchService = inject(SearchService);\n  private destroy$ = new Subject<void>();\n\n  searchTerm$ = new Subject<string>();\n  results = signal<SearchResult[]>([]);\n\n  ngOnInit() {\n    this.searchTerm$.pipe(\n      debounceTime(300),              // Wait 300ms after typing\n      distinctUntilChanged(),         // Only if value changed\n      filter(term => term.length > 2), // Minimum 3 characters\n      tap(() => this.loading.set(true)),\n      switchMap(term =>               // Cancel previous requests\n        this.searchService.search(term).pipe(\n          catchError(err => {\n            console.error(err);\n            return of([]);            // Return empty on error\n          })\n        )\n      ),\n      tap(() => this.loading.set(false)),\n      takeUntil(this.destroy$)        // Auto-unsubscribe\n    ).subscribe(results => this.results.set(results));\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Subject Types\n\n```typescript\nimport { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';\n\nexport class SubjectExamples {\n  // Subject: No initial value, only emits to future subscribers\n  private clickSubject = new Subject<MouseEvent>();\n  click$ = this.clickSubject.asObservable();\n\n  onClick(event: MouseEvent) {\n    this.clickSubject.next(event);\n  }\n\n  // BehaviorSubject: Has initial value, emits latest value to new subscribers\n  private loadingSubject = new BehaviorSubject<boolean>(false);\n  loading$ = this.loadingSubject.asObservable();\n\n  setLoading(loading: boolean) {\n    this.loadingSubject.next(loading);\n  }\n\n  // ReplaySubject: Replays N previous values to new subscribers\n  private activitySubject = new ReplaySubject<Activity>(3); // Last 3 activities\n  activity$ = this.activitySubject.asObservable();\n\n  // AsyncSubject: Only emits last value when completed\n  private finalResultSubject = new AsyncSubject<Result>();\n  finalResult$ = this.finalResultSubject.asObservable();\n}\n```\n\n## Higher-Order Operators\n\n```typescript\nimport { switchMap, mergeMap, concatMap, exhaustMap } from 'rxjs/operators';\n\nexport class HigherOrderExamples {\n  private http = inject(HttpClient);\n\n  // switchMap: Cancel previous, use latest (search, typeahead)\n  searchUsers(term$: Observable<string>) {\n    return term$.pipe(\n      switchMap(term => this.http.get<User[]>(`/api/users?q=${term}`))\n    );\n  }\n\n  // mergeMap: Process all concurrently (independent requests)\n  uploadFiles(files: File[]) {\n    return from(files).pipe(\n      mergeMap(file => this.http.post('/api/upload', file))\n    );\n  }\n\n  // concatMap: Process sequentially (order matters)\n  processQueue(tasks: Task[]) {\n    return from(tasks).pipe(\n      concatMap(task => this.http.post('/api/process', task))\n    );\n  }\n\n  // exhaustMap: Ignore new until current completes (prevent double-click)\n  saveForm(clicks$: Observable<void>, formData: any) {\n    return clicks$.pipe(\n      exhaustMap(() => this.http.post('/api/save', formData))\n    );\n  }\n}\n```\n\n## Error Handling\n\n```typescript\nimport { catchError, retry, retryWhen, delay, tap } from 'rxjs/operators';\nimport { throwError, of, timer } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class DataService {\n  private http = inject(HttpClient);\n\n  // Retry with exponential backoff\n  getData() {\n    return this.http.get<Data>('/api/data').pipe(\n      retryWhen(errors =>\n        errors.pipe(\n          mergeMap((error, index) => {\n            if (index >= 3) {\n              return throwError(() => error);\n            }\n            const delayMs = Math.pow(2, index) * 1000;\n            return timer(delayMs);\n          })\n        )\n      ),\n      catchError(err => {\n        console.error('Failed after retries:', err);\n        return of(null); // Fallback value\n      })\n    );\n  }\n\n  // Catch and rethrow with context\n  saveData(data: Data) {\n    return this.http.post('/api/data', data).pipe(\n      catchError(err => {\n        if (err.status === 401) {\n          // Handle auth error\n          return throwError(() => new Error('Unauthorized'));\n        }\n        return throwError(() => err);\n      })\n    );\n  }\n}\n```\n\n## Memory Management\n\n```typescript\nimport { Component, DestroyRef, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n@Component({\n  selector: 'app-auto-cleanup',\n  standalone: true\n})\nexport class AutoCleanupComponent {\n  private dataService = inject(DataService);\n  private destroyRef = inject(DestroyRef);\n\n  data = signal<Data[]>([]);\n\n  constructor() {\n    // Modern approach: takeUntilDestroyed\n    this.dataService.getData().pipe(\n      takeUntilDestroyed()  // Auto-cleanup on destroy\n    ).subscribe(data => this.data.set(data));\n\n    // Manual cleanup with DestroyRef\n    const subscription = this.dataService.getUpdates().subscribe();\n    this.destroyRef.onDestroy(() => subscription.unsubscribe());\n  }\n}\n\n// Legacy approach (still valid)\n@Component({\n  selector: 'app-manual-cleanup',\n  standalone: true\n})\nexport class ManualCleanupComponent implements OnDestroy {\n  private destroy$ = new Subject<void>();\n\n  ngOnInit() {\n    this.dataService.getData().pipe(\n      takeUntil(this.destroy$)\n    ).subscribe();\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Combining Observables\n\n```typescript\nimport { combineLatest, forkJoin, merge, zip } from 'rxjs';\n\nexport class CombiningExamples {\n  private http = inject(HttpClient);\n\n  // combineLatest: Emit when any source emits (latest values)\n  getDashboard() {\n    return combineLatest({\n      user: this.http.get<User>('/api/user'),\n      stats: this.http.get<Stats>('/api/stats'),\n      notifications: this.http.get<Notification[]>('/api/notifications')\n    }).pipe(\n      map(({ user, stats, notifications }) => ({\n        user,\n        stats,\n        notifications\n      }))\n    );\n  }\n\n  // forkJoin: Emit when all sources complete (like Promise.all)\n  loadAllData() {\n    return forkJoin({\n      users: this.http.get<User[]>('/api/users'),\n      products: this.http.get<Product[]>('/api/products'),\n      orders: this.http.get<Order[]>('/api/orders')\n    });\n  }\n\n  // merge: Emit when any source emits (flattens all)\n  getActivityFeed() {\n    return merge(\n      this.http.get<Activity[]>('/api/recent'),\n      this.http.get<Activity[]>('/api/trending')\n    );\n  }\n}\n```\n\n## Custom Operators\n\n```typescript\nimport { Observable, OperatorFunction } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\n// Custom operator for logging\nexport function debug<T>(tag: string): OperatorFunction<T, T> {\n  return (source: Observable<T>) =>\n    source.pipe(\n      tap({\n        next: value => console.log(`[${tag}] Next:`, value),\n        error: err => console.error(`[${tag}] Error:`, err),\n        complete: () => console.log(`[${tag}] Complete`)\n      })\n    );\n}\n\n// Usage\nthis.http.get('/api/data').pipe(\n  debug('API Call'),\n  map(data => transform(data))\n).subscribe();\n```\n\n## ShareReplay for Caching\n\n```typescript\nimport { shareReplay } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class ConfigService {\n  private http = inject(HttpClient);\n\n  // Cache config, share with all subscribers\n  config$ = this.http.get<Config>('/api/config').pipe(\n    shareReplay({ bufferSize: 1, refCount: true })\n  );\n\n  // All components get same config without extra HTTP calls\n  getConfig() {\n    return this.config$;\n  }\n}\n```\n\n## Quick Reference\n\n| Use Case | Operator |\n|----------|----------|\n| Transform values | `map`, `pluck` |\n| Filter values | `filter`, `distinctUntilChanged` |\n| Time-based | `debounceTime`, `throttleTime`, `delay` |\n| Cancel previous | `switchMap` |\n| Process all | `mergeMap` |\n| Sequential | `concatMap` |\n| Ignore new | `exhaustMap` |\n| Combine latest | `combineLatest` |\n| Wait for all | `forkJoin` |\n| Error handling | `catchError`, `retry` |\n| Cleanup | `takeUntilDestroyed`, `takeUntil` |\n| Share result | `shareReplay` |\n","skills/angular-architect/references/testing.md":"# Angular Testing\n\n## Component Testing\n\n```typescript\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { signal } from '@angular/core';\nimport { UserListComponent } from './user-list.component';\nimport { UsersService } from './users.service';\nimport { of } from 'rxjs';\n\ndescribe('UserListComponent', () => {\n  let component: UserListComponent;\n  let fixture: ComponentFixture<UserListComponent>;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  const mockUsers = [\n    { id: '1', name: 'John Doe', email: 'john@example.com' },\n    { id: '2', name: 'Jane Smith', email: 'jane@example.com' }\n  ];\n\n  beforeEach(async () => {\n    // Create spy object\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll', 'delete']);\n\n    await TestBed.configureTestingModule({\n      imports: [UserListComponent],  // Standalone component\n      providers: [\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    }).compileComponents();\n\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n    fixture = TestBed.createComponent(UserListComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n\n  it('should load users on init', () => {\n    usersService.getAll.and.returnValue(of(mockUsers));\n\n    fixture.detectChanges();  // Trigger ngOnInit\n\n    expect(usersService.getAll).toHaveBeenCalled();\n    expect(component.users()).toEqual(mockUsers);\n  });\n\n  it('should display users in template', () => {\n    component.users.set(mockUsers);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    const userElements = compiled.querySelectorAll('.user-item');\n\n    expect(userElements.length).toBe(2);\n    expect(userElements[0].textContent).toContain('John Doe');\n  });\n\n  it('should emit userSelected when user clicked', () => {\n    const emitSpy = spyOn(component.userSelected, 'emit');\n\n    component.onUserClick(mockUsers[0]);\n\n    expect(emitSpy).toHaveBeenCalledWith(mockUsers[0]);\n  });\n\n  it('should show loading state', () => {\n    component.loading.set(true);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    expect(compiled.querySelector('.loading')).toBeTruthy();\n  });\n});\n```\n\n## Service Testing\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';\nimport { UsersService } from './users.service';\nimport { User } from './user.model';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let httpMock: HttpTestingController;\n\n  const mockUsers: User[] = [\n    { id: '1', name: 'John', email: 'john@example.com' },\n    { id: '2', name: 'Jane', email: 'jane@example.com' }\n  ];\n\n  beforeEach(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule],\n      providers: [UsersService]\n    });\n\n    service = TestBed.inject(UsersService);\n    httpMock = TestBed.inject(HttpTestingController);\n  });\n\n  afterEach(() => {\n    httpMock.verify();  // Verify no outstanding requests\n  });\n\n  it('should fetch all users', (done) => {\n    service.getAll().subscribe(users => {\n      expect(users).toEqual(mockUsers);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('GET');\n    req.flush(mockUsers);\n  });\n\n  it('should create a user', (done) => {\n    const newUser: User = { id: '3', name: 'Bob', email: 'bob@example.com' };\n\n    service.create(newUser).subscribe(user => {\n      expect(user).toEqual(newUser);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('POST');\n    expect(req.request.body).toEqual(newUser);\n    req.flush(newUser);\n  });\n\n  it('should handle error', (done) => {\n    service.getAll().subscribe({\n      next: () => fail('should have failed'),\n      error: (error) => {\n        expect(error.status).toBe(500);\n        done();\n      }\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    req.flush('Server error', { status: 500, statusText: 'Internal Server Error' });\n  });\n});\n```\n\n## RxJS Marble Testing\n\n```typescript\nimport { TestScheduler } from 'rxjs/testing';\nimport { delay, map } from 'rxjs/operators';\n\ndescribe('RxJS Operators', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should map values', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--c--|', { a: 1, b: 2, c: 3 });\n      const expected = '    --x--y--z--|';\n      const result$ = source$.pipe(map(x => x * 10));\n\n      expectObservable(result$).toBe(expected, { x: 10, y: 20, z: 30 });\n    });\n  });\n\n  it('should delay emissions', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--|', { a: 1, b: 2 });\n      const expected = '    ----a--b--|';\n      const result$ = source$.pipe(delay(20));\n\n      expectObservable(result$).toBe(expected, { a: 1, b: 2 });\n    });\n  });\n});\n```\n\n## Testing with Signals\n\n```typescript\nimport { signal } from '@angular/core';\n\ndescribe('Counter Component', () => {\n  it('should update signal value', () => {\n    const count = signal(0);\n\n    expect(count()).toBe(0);\n\n    count.set(5);\n    expect(count()).toBe(5);\n\n    count.update(val => val + 1);\n    expect(count()).toBe(6);\n  });\n\n  it('should compute derived value', () => {\n    const count = signal(5);\n    const doubled = computed(() => count() * 2);\n\n    expect(doubled()).toBe(10);\n\n    count.set(10);\n    expect(doubled()).toBe(20);\n  });\n});\n```\n\n## Testing NgRx\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockStore, MockStore } from '@ngrx/store/testing';\nimport { UsersComponent } from './users.component';\nimport { selectAllUsers, selectUsersLoading } from './store/users.selectors';\n\ndescribe('UsersComponent with NgRx', () => {\n  let component: UsersComponent;\n  let fixture: ComponentFixture<UsersComponent>;\n  let store: MockStore;\n\n  const initialState = {\n    users: {\n      ids: ['1', '2'],\n      entities: {\n        '1': { id: '1', name: 'John' },\n        '2': { id: '2', name: 'Jane' }\n      },\n      loading: false\n    }\n  };\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [UsersComponent],\n      providers: [\n        provideMockStore({ initialState })\n      ]\n    }).compileComponents();\n\n    store = TestBed.inject(MockStore);\n    fixture = TestBed.createComponent(UsersComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should select users from store', () => {\n    store.overrideSelector(selectAllUsers, [\n      { id: '1', name: 'John' },\n      { id: '2', name: 'Jane' }\n    ]);\n\n    fixture.detectChanges();\n\n    expect(component.users().length).toBe(2);\n  });\n\n  it('should dispatch action on delete', () => {\n    const dispatchSpy = spyOn(store, 'dispatch');\n\n    component.onDelete('1');\n\n    expect(dispatchSpy).toHaveBeenCalledWith(\n      UsersActions.deleteUser({ id: '1' })\n    );\n  });\n});\n```\n\n## Testing Effects\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockActions } from '@ngrx/effects/testing';\nimport { Observable, of, throwError } from 'rxjs';\nimport { UsersEffects } from './users.effects';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\nimport { hot, cold } from 'jasmine-marbles';\n\ndescribe('UsersEffects', () => {\n  let actions$: Observable<any>;\n  let effects: UsersEffects;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  beforeEach(() => {\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        UsersEffects,\n        provideMockActions(() => actions$),\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    });\n\n    effects = TestBed.inject(UsersEffects);\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n  });\n\n  it('should load users successfully', () => {\n    const users = [{ id: '1', name: 'John' }];\n    const action = UsersActions.loadUsers();\n    const outcome = UsersActions.loadUsersSuccess({ users });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-b|', { b: users });\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n\n  it('should handle load users failure', () => {\n    const action = UsersActions.loadUsers();\n    const error = new Error('Failed to load');\n    const outcome = UsersActions.loadUsersFailure({ error: error.message });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-#|', {}, error);\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n});\n```\n\n## Testing Guards\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { Router } from '@angular/router';\nimport { authGuard } from './auth.guard';\nimport { AuthService } from './auth.service';\n\ndescribe('authGuard', () => {\n  let authService: jasmine.SpyObj<AuthService>;\n  let router: jasmine.SpyObj<Router>;\n\n  beforeEach(() => {\n    const authServiceSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated']);\n    const routerSpy = jasmine.createSpyObj('Router', ['createUrlTree']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        { provide: AuthService, useValue: authServiceSpy },\n        { provide: Router, useValue: routerSpy }\n      ]\n    });\n\n    authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;\n    router = TestBed.inject(Router) as jasmine.SpyObj<Router>;\n  });\n\n  it('should allow access when authenticated', () => {\n    authService.isAuthenticated.and.returnValue(true);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, {} as any)\n    );\n\n    expect(result).toBe(true);\n  });\n\n  it('should redirect when not authenticated', () => {\n    authService.isAuthenticated.and.returnValue(false);\n    const urlTree = {} as any;\n    router.createUrlTree.and.returnValue(urlTree);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, { url: '/protected' } as any)\n    );\n\n    expect(result).toBe(urlTree);\n    expect(router.createUrlTree).toHaveBeenCalledWith(\n      ['/login'],\n      { queryParams: { returnUrl: '/protected' } }\n    );\n  });\n});\n```\n\n## Quick Reference\n\n| Test Type | Key Tools |\n|-----------|-----------|\n| Component | `TestBed`, `ComponentFixture`, `detectChanges()` |\n| Service | `HttpClientTestingModule`, `HttpTestingController` |\n| RxJS | `TestScheduler`, marble diagrams |\n| NgRx Store | `provideMockStore`, `MockStore` |\n| Effects | `provideMockActions`, jasmine-marbles |\n| Guards | `TestBed.runInInjectionContext()` |\n| Signals | Direct value checks with `()` |\n| Spies | `jasmine.createSpyObj()`, `spyOn()` |\n","skills/api-designer/SKILL.md":"---\nname: api-designer\ndescription: Use when designing REST or GraphQL APIs, creating OpenAPI specifications, or planning API architecture. Invoke for resource modeling, versioning strategies, pagination patterns, error handling standards.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: api-architecture\n  triggers: API design, REST API, OpenAPI, API specification, API architecture, resource modeling, API versioning, GraphQL schema, API documentation\n  role: architect\n  scope: design\n  output-format: specification\n  related-skills: graphql-architect, fastapi-expert, nestjs-expert, spring-boot-engineer, security-reviewer\n---\n\n# API Designer\n\nSenior API architect specializing in REST and GraphQL APIs with comprehensive OpenAPI 3.1 specifications.\n\n## Core Workflow\n\n1. **Analyze domain** — Understand business requirements, data models, and client needs\n2. **Model resources** — Identify resources, relationships, and operations; sketch entity diagram before writing any spec\n3. **Design endpoints** — Define URI patterns, HTTP methods, request/response schemas\n4. **Specify contract** — Create OpenAPI 3.1 spec; validate before proceeding: `npx @redocly/cli lint openapi.yaml`\n5. **Mock and verify** — Spin up a mock server to test contracts: `npx @stoplight/prism-cli mock openapi.yaml`\n6. **Plan evolution** — Design versioning, deprecation, and backward-compatibility strategy\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| REST Patterns | `references/rest-patterns.md` | Resource design, HTTP methods, HATEOAS |\n| Versioning | `references/versioning.md` | API versions, deprecation, breaking changes |\n| Pagination | `references/pagination.md` | Cursor, offset, keyset pagination |\n| Error Handling | `references/error-handling.md` | Error responses, RFC 7807, status codes |\n| OpenAPI | `references/openapi.md` | OpenAPI 3.1, documentation, code generation |\n\n## Constraints\n\n### MUST DO\n- Follow REST principles (resource-oriented, proper HTTP methods)\n- Use consistent naming conventions (snake_case or camelCase — pick one, apply everywhere)\n- Include comprehensive OpenAPI 3.1 specification\n- Design proper error responses with actionable messages (RFC 7807)\n- Implement pagination for all collection endpoints\n- Version APIs with clear deprecation policies\n- Document authentication and authorization\n- Provide request/response examples\n\n### MUST NOT DO\n- Use verbs in resource URIs (use `/users/{id}`, not `/getUser/{id}`)\n- Return inconsistent response structures\n- Skip error code documentation\n- Ignore HTTP status code semantics\n- Design APIs without a versioning strategy\n- Expose implementation details in the API surface\n- Create breaking changes without a migration path\n- Omit rate limiting considerations\n\n## Templates\n\n### OpenAPI 3.1 Resource Endpoint (copy-paste starter)\n\n```yaml\nopenapi: \"3.1.0\"\ninfo:\n  title: Example API\n  version: \"1.1.0\"\npaths:\n  /users:\n    get:\n      summary: List users\n      operationId: listUsers\n      tags: [Users]\n      parameters:\n        - name: cursor\n          in: query\n          schema: { type: string }\n          description: Opaque cursor for pagination\n        - name: limit\n          in: query\n          schema: { type: integer, default: 20, maximum: 100 }\n      responses:\n        \"200\":\n          description: Paginated list of users\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [data, pagination]\n                properties:\n                  data:\n                    type: array\n                    items: { $ref: \"#/components/schemas/User\" }\n                  pagination:\n                    $ref: \"#/components/schemas/CursorPage\"\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"429\": { $ref: \"#/components/responses/TooManyRequests\" }\n  /users/{id}:\n    get:\n      summary: Get a user\n      operationId: getUser\n      tags: [Users]\n      parameters:\n        - name: id\n          in: path\n          required: true\n          schema: { type: string, format: uuid }\n      responses:\n        \"200\":\n          description: User found\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/User\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required: [id, email, created_at]\n      properties:\n        id:    { type: string, format: uuid, readOnly: true }\n        email: { type: string, format: email }\n        name:  { type: string }\n        created_at: { type: string, format: date-time, readOnly: true }\n\n    CursorPage:\n      type: object\n      required: [next_cursor, has_more]\n      properties:\n        next_cursor: { type: string, nullable: true }\n        has_more:    { type: boolean }\n\n    Problem:                       # RFC 7807 Problem Details\n      type: object\n      required: [type, title, status]\n      properties:\n        type:     { type: string, format: uri, example: \"https://api.example.com/errors/validation-error\" }\n        title:    { type: string, example: \"Validation Error\" }\n        status:   { type: integer, example: 400 }\n        detail:   { type: string, example: \"The 'email' field must be a valid email address.\" }\n        instance: { type: string, format: uri, example: \"/users/req-abc123\" }\n\n  responses:\n    BadRequest:\n      description: Invalid request parameters\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    Unauthorized:\n      description: Missing or invalid authentication\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    NotFound:\n      description: Resource not found\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    TooManyRequests:\n      description: Rate limit exceeded\n      headers:\n        Retry-After: { schema: { type: integer } }\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\nsecurity:\n  - BearerAuth: []\n```\n\n### RFC 7807 Error Response (copy-paste)\n\n```json\n{\n  \"type\": \"https://api.example.com/errors/validation-error\",\n  \"title\": \"Validation Error\",\n  \"status\": 422,\n  \"detail\": \"The 'email' field must be a valid email address.\",\n  \"instance\": \"/users/req-abc123\",\n  \"errors\": [\n    { \"field\": \"email\", \"message\": \"Must be a valid email address.\" }\n  ]\n}\n```\n\n- Always use `Content-Type: application/problem+json` for error responses.\n- `type` must be a stable, documented URI — never a generic string.\n- `detail` must be human-readable and actionable.\n- Extend with `errors[]` for field-level validation failures.\n\n## Output Checklist\n\nWhen delivering an API design, provide:\n1. Resource model and relationships (diagram or table)\n2. Endpoint specifications with URIs and HTTP methods\n3. OpenAPI 3.1 specification (YAML)\n4. Authentication and authorization flows\n5. Error response catalog (all 4xx/5xx with `type` URIs)\n6. Pagination and filtering patterns\n7. Versioning and deprecation strategy\n8. Validation result: `npx @redocly/cli lint openapi.yaml` passes with no errors\n\n## Knowledge Reference\n\nREST architecture, OpenAPI 3.1, GraphQL, HTTP semantics, JSON:API, HATEOAS, OAuth 2.0, JWT, RFC 7807 Problem Details, API versioning patterns, pagination strategies, rate limiting, webhook design, SDK generation\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/api-designer/)\n","skills/api-designer/references/error-handling.md":"# API Error Handling\n\n## Error Response Design\n\nConsistent, informative error responses are critical for API usability.\n\n## Standard Error Format\n\n### Basic Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 123 not found\",\n    \"details\": null\n  }\n}\n```\n\n### RFC 7807 Problem Details\n\nStandardized error format (application/problem+json):\n\n```http\nHTTP/1.1 404 Not Found\nContent-Type: application/problem+json\n\n{\n  \"type\": \"https://api.example.com/errors/resource-not-found\",\n  \"title\": \"Resource Not Found\",\n  \"status\": 404,\n  \"detail\": \"User with ID 123 does not exist\",\n  \"instance\": \"/users/123\"\n}\n```\n\n**Fields:**\n- `type` - URI reference identifying error type\n- `title` - Short, human-readable summary\n- `status` - HTTP status code\n- `detail` - Human-readable explanation specific to this occurrence\n- `instance` - URI reference for this specific occurrence\n\n### Extended Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be between 18 and 120\"\n      }\n    ],\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\",\n    \"documentation_url\": \"https://api.example.com/docs/errors#validation-error\"\n  }\n}\n```\n\n## Error Categories\n\n### 1. Validation Errors (400 Bad Request)\n\nClient sent invalid data.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"\",\n  \"email\": \"invalid-email\",\n  \"age\": 15\n}\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"name\",\n        \"code\": \"REQUIRED\",\n        \"message\": \"Name is required\"\n      },\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be at least 18\",\n        \"constraints\": {\n          \"min\": 18,\n          \"max\": 120\n        }\n      }\n    ]\n  }\n}\n```\n\n### 2. Authentication Errors (401 Unauthorized)\n\nMissing or invalid authentication credentials.\n\n```http\nGET /users/123\nAuthorization: Bearer <invalid-access-token>\n\nResponse: 401 Unauthorized\nWWW-Authenticate: Bearer realm=\"api\", error=\"invalid_token\"\n\n{\n  \"error\": {\n    \"code\": \"INVALID_TOKEN\",\n    \"message\": \"The access token is invalid or has expired\",\n    \"details\": {\n      \"reason\": \"token_expired\",\n      \"expired_at\": \"2024-01-15T10:00:00Z\"\n    }\n  }\n}\n```\n\n**Common auth error codes:**\n- `MISSING_TOKEN` - No auth token provided\n- `INVALID_TOKEN` - Token is malformed or invalid\n- `EXPIRED_TOKEN` - Token has expired\n- `REVOKED_TOKEN` - Token has been revoked\n\n### 3. Authorization Errors (403 Forbidden)\n\nAuthenticated but not authorized to perform action.\n\n```http\nDELETE /users/123\nAuthorization: Bearer <access-token>\n\nResponse: 403 Forbidden\n{\n  \"error\": {\n    \"code\": \"INSUFFICIENT_PERMISSIONS\",\n    \"message\": \"You do not have permission to delete this user\",\n    \"details\": {\n      \"required_permission\": \"users:delete\",\n      \"your_permissions\": [\"users:read\", \"users:update\"]\n    }\n  }\n}\n```\n\n### 4. Not Found Errors (404 Not Found)\n\nResource doesn't exist.\n\n```http\nGET /users/99999\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 99999 not found\",\n    \"details\": {\n      \"resource_type\": \"User\",\n      \"resource_id\": \"99999\"\n    }\n  }\n}\n```\n\n### 5. Conflict Errors (409 Conflict)\n\nRequest conflicts with current state.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"email\": \"existing@example.com\",\n  \"name\": \"John Doe\"\n}\n\nResponse: 409 Conflict\n{\n  \"error\": {\n    \"code\": \"RESOURCE_ALREADY_EXISTS\",\n    \"message\": \"User with email 'existing@example.com' already exists\",\n    \"details\": {\n      \"field\": \"email\",\n      \"value\": \"existing@example.com\",\n      \"existing_resource\": \"/users/123\"\n    }\n  }\n}\n```\n\n### 6. Rate Limiting (429 Too Many Requests)\n\nClient exceeded rate limit.\n\n```http\nGET /users\n\nResponse: 429 Too Many Requests\nRetry-After: 60\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1705320000\n\n{\n  \"error\": {\n    \"code\": \"RATE_LIMIT_EXCEEDED\",\n    \"message\": \"You have exceeded the rate limit\",\n    \"details\": {\n      \"limit\": 100,\n      \"window\": \"1 hour\",\n      \"retry_after\": 60,\n      \"reset_at\": \"2024-01-15T11:00:00Z\"\n    }\n  }\n}\n```\n\n### 7. Server Errors (500 Internal Server Error)\n\nUnexpected server error.\n\n```http\nGET /users/123\n\nResponse: 500 Internal Server Error\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\",\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\"\n  }\n}\n```\n\n**Never expose:**\n- Stack traces\n- Database errors\n- Internal paths\n- Sensitive configuration\n\n### 8. Service Unavailable (503 Service Unavailable)\n\nService temporarily unavailable.\n\n```http\nGET /users\n\nResponse: 503 Service Unavailable\nRetry-After: 300\n\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service is temporarily unavailable due to maintenance\",\n    \"details\": {\n      \"retry_after\": 300,\n      \"maintenance_end\": \"2024-01-15T12:00:00Z\"\n    }\n  }\n}\n```\n\n## Error Code Catalog\n\nDefine standard error codes for your API:\n\n```json\n{\n  \"VALIDATION_ERROR\": {\n    \"status\": 400,\n    \"description\": \"Request validation failed\",\n    \"subcodes\": {\n      \"REQUIRED\": \"Required field is missing\",\n      \"INVALID_FORMAT\": \"Field has invalid format\",\n      \"OUT_OF_RANGE\": \"Value is out of allowed range\",\n      \"INVALID_ENUM\": \"Value is not in allowed set\"\n    }\n  },\n  \"AUTHENTICATION_ERROR\": {\n    \"status\": 401,\n    \"description\": \"Authentication failed\",\n    \"subcodes\": {\n      \"MISSING_TOKEN\": \"No authentication token provided\",\n      \"INVALID_TOKEN\": \"Token is invalid\",\n      \"EXPIRED_TOKEN\": \"Token has expired\"\n    }\n  },\n  \"AUTHORIZATION_ERROR\": {\n    \"status\": 403,\n    \"description\": \"Insufficient permissions\",\n    \"subcodes\": {\n      \"INSUFFICIENT_PERMISSIONS\": \"Missing required permission\",\n      \"RESOURCE_FORBIDDEN\": \"Access to resource is forbidden\"\n    }\n  },\n  \"RESOURCE_NOT_FOUND\": {\n    \"status\": 404,\n    \"description\": \"Resource not found\"\n  },\n  \"CONFLICT_ERROR\": {\n    \"status\": 409,\n    \"description\": \"Request conflicts with current state\",\n    \"subcodes\": {\n      \"RESOURCE_ALREADY_EXISTS\": \"Resource already exists\",\n      \"CONCURRENT_MODIFICATION\": \"Resource was modified by another request\"\n    }\n  },\n  \"RATE_LIMIT_EXCEEDED\": {\n    \"status\": 429,\n    \"description\": \"Rate limit exceeded\"\n  },\n  \"INTERNAL_SERVER_ERROR\": {\n    \"status\": 500,\n    \"description\": \"Internal server error\"\n  }\n}\n```\n\n## Validation Error Details\n\n### Field-Level Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"credit_card.number\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Credit card number must be 16 digits\",\n        \"value_provided\": \"1234\",\n        \"constraints\": {\n          \"pattern\": \"^[0-9]{16}$\"\n        }\n      },\n      {\n        \"field\": \"items[0].quantity\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Quantity must be at least 1\",\n        \"value_provided\": 0,\n        \"constraints\": {\n          \"min\": 1,\n          \"max\": 1000\n        }\n      }\n    ]\n  }\n}\n```\n\n### Cross-Field Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"fields\": [\"start_date\", \"end_date\"],\n        \"code\": \"INVALID_RANGE\",\n        \"message\": \"End date must be after start date\",\n        \"values_provided\": {\n          \"start_date\": \"2024-01-20\",\n          \"end_date\": \"2024-01-15\"\n        }\n      }\n    ]\n  }\n}\n```\n\n## Request ID Tracking\n\nAlways include request ID for debugging:\n\n```http\nResponse Headers:\nX-Request-ID: req_abc123\n\nResponse Body:\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred\",\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\nClients can reference request ID in support tickets.\n\n## Error Documentation\n\nDocument all possible errors for each endpoint:\n\n```yaml\n/users/{id}:\n  get:\n    responses:\n      '200':\n        description: Success\n      '401':\n        description: Authentication failed\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              missing_token:\n                value:\n                  error:\n                    code: MISSING_TOKEN\n                    message: No authentication token provided\n              invalid_token:\n                value:\n                  error:\n                    code: INVALID_TOKEN\n                    message: Token is invalid or expired\n      '404':\n        description: User not found\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              not_found:\n                value:\n                  error:\n                    code: RESOURCE_NOT_FOUND\n                    message: User with ID 123 not found\n```\n\n## Retry Guidance\n\nHelp clients understand if they should retry:\n\n```json\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service temporarily unavailable\",\n    \"retry\": {\n      \"retryable\": true,\n      \"retry_after\": 60,\n      \"max_retries\": 3,\n      \"backoff\": \"exponential\"\n    }\n  }\n}\n```\n\n### Retryable Errors\n\n- 408 Request Timeout\n- 429 Too Many Requests (with Retry-After)\n- 500 Internal Server Error (sometimes)\n- 502 Bad Gateway\n- 503 Service Unavailable\n- 504 Gateway Timeout\n\n### Non-Retryable Errors\n\n- 400 Bad Request\n- 401 Unauthorized\n- 403 Forbidden\n- 404 Not Found\n- 409 Conflict\n- 422 Unprocessable Entity\n\n## Multi-Language Support\n\nSupport error messages in multiple languages:\n\n```http\nGET /users/invalid\nAccept-Language: es\n\nResponse: 404 Not Found\nContent-Language: es\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"Usuario con ID 'invalid' no encontrado\"\n  }\n}\n```\n\nAlways include `code` so clients can implement their own translations.\n\n## Best Practices\n\n1. **Use standard HTTP status codes** - Don't return 200 for errors\n2. **Include machine-readable codes** - Error codes for client logic\n3. **Provide human-readable messages** - Clear explanations\n4. **Be specific but safe** - Don't expose sensitive information\n5. **Include request ID** - For tracking and debugging\n6. **Document all errors** - Every possible error for each endpoint\n7. **Be consistent** - Same format across all endpoints\n8. **Help clients retry** - Indicate if error is retryable\n9. **Validate early** - Return validation errors immediately\n10. **Log errors server-side** - Track errors for monitoring\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Generic error messages** - \"Error occurred\" without details\n- **Exposing stack traces** - Security risk\n- **Inconsistent error format** - Different structure per endpoint\n- **Missing error codes** - Only human-readable messages\n- **Wrong status codes** - Returning 200 with error in body\n- **No request ID** - Makes debugging impossible\n- **Undocumented errors** - Clients don't know what to expect\n- **Too much information** - Exposing internal implementation\n","skills/api-designer/references/openapi.md":"# OpenAPI 3.1 Specification\n\n## What is OpenAPI?\n\nOpenAPI (formerly Swagger) is a standard for describing REST APIs. It enables:\n- Interactive documentation\n- Code generation (SDKs, clients, servers)\n- API testing tools\n- Contract validation\n- Mock servers\n\n## Basic Structure\n\n### Minimal OpenAPI 3.1 Spec\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 1.0.0\n  description: A sample API\n  contact:\n    name: API Support\n    email: support@example.com\n    url: https://example.com/support\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n\nservers:\n  - url: https://api.example.com/v1\n    description: Production server\n  - url: https://staging-api.example.com/v1\n    description: Staging server\n  - url: http://localhost:3000/v1\n    description: Local development\n\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users\n      operationId: listUsers\n      tags:\n        - Users\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  data:\n                    type: array\n                    items:\n                      $ref: '#/components/schemas/User'\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n      properties:\n        id:\n          type: integer\n          format: int64\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          example: John Doe\n```\n\n## Info Object\n\nMetadata about the API:\n\n```yaml\ninfo:\n  title: Users API\n  version: 1.0.0\n  description: |\n    # Users API\n\n    This API manages user accounts and profiles.\n\n    ## Features\n    - User CRUD operations\n    - Authentication with JWT\n    - Role-based authorization\n\n  termsOfService: https://example.com/terms\n\n  contact:\n    name: API Support Team\n    email: api-support@example.com\n    url: https://example.com/support\n\n  license:\n    name: MIT\n    url: https://opensource.org/licenses/MIT\n\n  x-api-id: users-api-v1\n  x-audience: external\n```\n\n## Servers\n\nDefine API base URLs:\n\n```yaml\nservers:\n  - url: https://api.example.com/v1\n    description: Production\n    variables:\n      version:\n        default: v1\n        enum:\n          - v1\n          - v2\n\n  - url: https://{environment}.example.com/v1\n    description: Dynamic environment\n    variables:\n      environment:\n        default: api\n        enum:\n          - api\n          - staging\n          - dev\n```\n\n## Paths and Operations\n\n### Complete Endpoint Example\n\n```yaml\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users with optional filtering\n      operationId: listUsers\n      tags:\n        - Users\n\n      parameters:\n        - name: offset\n          in: query\n          description: Number of items to skip\n          required: false\n          schema:\n            type: integer\n            minimum: 0\n            default: 0\n\n        - name: limit\n          in: query\n          description: Maximum number of items to return\n          required: false\n          schema:\n            type: integer\n            minimum: 1\n            maximum: 100\n            default: 20\n\n        - name: status\n          in: query\n          description: Filter by user status\n          required: false\n          schema:\n            type: string\n            enum:\n              - active\n              - inactive\n              - suspended\n\n      security:\n        - bearerAuth: []\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/UserListResponse'\n              examples:\n                success:\n                  $ref: '#/components/examples/UserListSuccess'\n\n        '401':\n          $ref: '#/components/responses/Unauthorized'\n\n        '429':\n          $ref: '#/components/responses/RateLimitExceeded'\n\n    post:\n      summary: Create user\n      description: Create a new user account\n      operationId: createUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/CreateUserRequest'\n            examples:\n              basic:\n                $ref: '#/components/examples/CreateUserBasic'\n\n      responses:\n        '201':\n          description: User created successfully\n          headers:\n            Location:\n              description: URL of the created user\n              schema:\n                type: string\n                format: uri\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '400':\n          $ref: '#/components/responses/ValidationError'\n\n        '409':\n          description: User already exists\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Error'\n\n  /users/{userId}:\n    parameters:\n      - name: userId\n        in: path\n        description: User ID\n        required: true\n        schema:\n          type: integer\n          format: int64\n\n    get:\n      summary: Get user\n      description: Retrieve a specific user by ID\n      operationId: getUser\n      tags:\n        - Users\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    put:\n      summary: Update user\n      description: Replace user data\n      operationId: updateUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/UpdateUserRequest'\n\n      responses:\n        '200':\n          description: User updated successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    delete:\n      summary: Delete user\n      description: Delete a user account\n      operationId: deleteUser\n      tags:\n        - Users\n\n      responses:\n        '204':\n          description: User deleted successfully\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n```\n\n## Components\n\nReusable components for your API spec.\n\n### Schemas\n\n```yaml\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n        - name\n      properties:\n        id:\n          type: integer\n          format: int64\n          readOnly: true\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n          example: John Doe\n        status:\n          type: string\n          enum:\n            - active\n            - inactive\n            - suspended\n          default: active\n        created_at:\n          type: string\n          format: date-time\n          readOnly: true\n          example: \"2024-01-15T10:30:00Z\"\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    CreateUserRequest:\n      type: object\n      required:\n        - email\n        - name\n      properties:\n        email:\n          type: string\n          format: email\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    UserListResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/User'\n        pagination:\n          $ref: '#/components/schemas/Pagination'\n\n    Pagination:\n      type: object\n      properties:\n        offset:\n          type: integer\n          minimum: 0\n        limit:\n          type: integer\n          minimum: 1\n        total:\n          type: integer\n          minimum: 0\n        has_more:\n          type: boolean\n\n    Error:\n      type: object\n      required:\n        - error\n      properties:\n        error:\n          type: object\n          required:\n            - code\n            - message\n          properties:\n            code:\n              type: string\n              example: RESOURCE_NOT_FOUND\n            message:\n              type: string\n              example: User with ID 123 not found\n            details:\n              type: object\n            request_id:\n              type: string\n              example: req_abc123\n```\n\n### Security Schemes\n\n```yaml\ncomponents:\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: JWT access token\n\n    apiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: API key for authentication\n\n    oauth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://auth.example.com/oauth/authorize\n          tokenUrl: https://auth.example.com/oauth/token\n          scopes:\n            users:read: Read user data\n            users:write: Create and update users\n            users:delete: Delete users\n```\n\nApply security globally or per-operation:\n\n```yaml\n# Global security\nsecurity:\n  - bearerAuth: []\n\n# Or per-operation\npaths:\n  /users:\n    get:\n      security:\n        - bearerAuth: []\n        - apiKey: []  # Alternative auth method\n```\n\n### Responses\n\nReusable response definitions:\n\n```yaml\ncomponents:\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: RESOURCE_NOT_FOUND\n              message: The requested resource was not found\n\n    Unauthorized:\n      description: Authentication required\n      headers:\n        WWW-Authenticate:\n          schema:\n            type: string\n          description: Authentication method\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    ValidationError:\n      description: Validation failed\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: VALIDATION_ERROR\n              message: Request validation failed\n              details:\n                - field: email\n                  code: INVALID_FORMAT\n                  message: Email must be a valid email address\n\n    RateLimitExceeded:\n      description: Rate limit exceeded\n      headers:\n        X-RateLimit-Limit:\n          schema:\n            type: integer\n          description: Request limit per hour\n        X-RateLimit-Remaining:\n          schema:\n            type: integer\n          description: Remaining requests\n        X-RateLimit-Reset:\n          schema:\n            type: integer\n            format: int64\n          description: Time when limit resets (Unix timestamp)\n        Retry-After:\n          schema:\n            type: integer\n          description: Seconds to wait before retry\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n```\n\n### Examples\n\n```yaml\ncomponents:\n  examples:\n    UserListSuccess:\n      summary: Successful user list response\n      value:\n        data:\n          - id: 1\n            email: john@example.com\n            name: John Doe\n            status: active\n            created_at: \"2024-01-15T10:30:00Z\"\n          - id: 2\n            email: jane@example.com\n            name: Jane Smith\n            status: active\n            created_at: \"2024-01-16T14:20:00Z\"\n        pagination:\n          offset: 0\n          limit: 20\n          total: 150\n          has_more: true\n\n    CreateUserBasic:\n      summary: Create user with minimal fields\n      value:\n        email: newuser@example.com\n        name: New User\n```\n\n## Data Types\n\n### Primitive Types\n\n```yaml\n# String\ntype: string\nexample: \"Hello World\"\n\n# String with format\ntype: string\nformat: email\nexample: \"user@example.com\"\n\n# Integer\ntype: integer\nformat: int64\nexample: 123\n\n# Number (float)\ntype: number\nformat: double\nexample: 99.99\n\n# Boolean\ntype: boolean\nexample: true\n\n# Date-time\ntype: string\nformat: date-time\nexample: \"2024-01-15T10:30:00Z\"\n\n# Date\ntype: string\nformat: date\nexample: \"2024-01-15\"\n\n# UUID\ntype: string\nformat: uuid\nexample: \"550e8400-e29b-41d4-a716-446655440000\"\n\n# URI\ntype: string\nformat: uri\nexample: \"https://example.com/users/123\"\n```\n\n### Arrays\n\n```yaml\ntype: array\nitems:\n  type: string\nminItems: 1\nmaxItems: 10\nuniqueItems: true\nexample: [\"tag1\", \"tag2\", \"tag3\"]\n\n# Array of objects\ntype: array\nitems:\n  $ref: '#/components/schemas/User'\n```\n\n### Objects\n\n```yaml\ntype: object\nrequired:\n  - name\n  - email\nproperties:\n  name:\n    type: string\n  email:\n    type: string\n    format: email\n  age:\n    type: integer\n    minimum: 0\n    maximum: 120\n\n# Additional properties\nadditionalProperties: false  # Strict - no extra properties\nadditionalProperties: true   # Allow any extra properties\nadditionalProperties:        # Extra properties must be strings\n  type: string\n```\n\n### Enums\n\n```yaml\ntype: string\nenum:\n  - active\n  - inactive\n  - suspended\ndefault: active\n```\n\n### OneOf / AnyOf / AllOf\n\n```yaml\n# OneOf - exactly one schema matches\noneOf:\n  - $ref: '#/components/schemas/CreditCard'\n  - $ref: '#/components/schemas/BankAccount'\n\n# AnyOf - one or more schemas match\nanyOf:\n  - $ref: '#/components/schemas/User'\n  - $ref: '#/components/schemas/Organization'\n\n# AllOf - all schemas must match (inheritance)\nallOf:\n  - $ref: '#/components/schemas/BaseUser'\n  - type: object\n    properties:\n      admin_level:\n        type: integer\n```\n\n## Validation\n\n### String Validation\n\n```yaml\ntype: string\nminLength: 1\nmaxLength: 100\npattern: \"^[a-zA-Z0-9_-]+$\"\nformat: email\n```\n\n### Number Validation\n\n```yaml\ntype: integer\nminimum: 0\nmaximum: 100\nexclusiveMinimum: true  # > 0 instead of >= 0\nmultipleOf: 5\n```\n\n### Array Validation\n\n```yaml\ntype: array\nminItems: 1\nmaxItems: 10\nuniqueItems: true\n```\n\n## Tags\n\nOrganize endpoints into logical groups:\n\n```yaml\ntags:\n  - name: Users\n    description: User management operations\n  - name: Orders\n    description: Order management\n  - name: Products\n    description: Product catalog\n\npaths:\n  /users:\n    get:\n      tags:\n        - Users\n```\n\n## Documentation\n\n### Markdown Support\n\n```yaml\ndescription: |\n  # User Management\n\n  This endpoint allows you to manage users.\n\n  ## Features\n  - Create users\n  - Update profiles\n  - Delete accounts\n\n  ## Authentication\n  Requires JWT bearer token.\n\n  ## Example\n  ```json\n  {\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\"\n  }\n  ```\n```\n\n## Code Generation\n\nGenerate SDKs from OpenAPI spec:\n\n```bash\n# Generate TypeScript client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g typescript-axios \\\n  -o ./client\n\n# Generate Python client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g python \\\n  -o ./python-client\n\n# Generate server stub\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g nodejs-express-server \\\n  -o ./server\n```\n\n## Validation Tools\n\nValidate OpenAPI spec:\n\n```bash\n# Using Swagger CLI\nswagger-cli validate openapi.yaml\n\n# Using Spectral (advanced linting)\nspectral lint openapi.yaml\n```\n\n## Best Practices\n\n1. **Use components** - Reuse schemas, responses, parameters\n2. **Add examples** - Include realistic examples for all schemas\n3. **Document thoroughly** - Every endpoint, parameter, response\n4. **Version your spec** - Track changes to the specification\n5. **Validate regularly** - Use tools to catch errors\n6. **Use $ref** - Reference components instead of duplicating\n7. **Include error responses** - Document all possible errors\n8. **Add operationId** - Unique ID for each operation (for code gen)\n9. **Tag endpoints** - Organize into logical groups\n10. **Provide security schemes** - Document authentication clearly\n","skills/api-designer/references/pagination.md":"# Pagination Patterns\n\n## Why Paginate?\n\nLarge collections can't be returned all at once due to:\n- Performance (slow queries, large payloads)\n- Memory constraints (server and client)\n- Network timeouts\n- Poor user experience\n\nAlways paginate collection endpoints.\n\n## Pagination Strategies\n\n### 1. Offset-Based Pagination\n\nMost common and intuitive. Uses `offset` (skip) and `limit` (page size).\n\n**Request:**\n```http\nGET /users?offset=20&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"offset\": 20,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=10&limit=10\",\n    \"next\": \"/users?offset=30&limit=10\",\n    \"last\": \"/users?offset=140&limit=10\"\n  }\n}\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to understand\n- Random access (jump to any page)\n- Shows total count\n\n**Disadvantages:**\n- Performance degrades with large offsets (database scans many rows)\n- Inconsistent results if data changes during pagination\n- Inefficient for real-time data\n- Database must count total rows (expensive)\n\n**Use when:**\n- Small to medium datasets\n- Data doesn't change frequently\n- Need random page access\n- Need total count\n\n### 2. Page-Based Pagination\n\nSimplified offset pagination using page numbers.\n\n**Request:**\n```http\nGET /users?page=3&per_page=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"page\": 3,\n    \"per_page\": 10,\n    \"total_pages\": 15,\n    \"total_count\": 150\n  },\n  \"links\": {\n    \"first\": \"/users?page=1&per_page=10\",\n    \"prev\": \"/users?page=2&per_page=10\",\n    \"next\": \"/users?page=4&per_page=10\",\n    \"last\": \"/users?page=15&per_page=10\"\n  }\n}\n```\n\n**Calculation:**\n- `offset = (page - 1) * per_page`\n- `total_pages = ceil(total_count / per_page)`\n\n**Same pros/cons as offset-based, but:**\n- More intuitive for users (page 1, page 2)\n- Common in web applications\n\n### 3. Cursor-Based Pagination\n\nUses an opaque cursor (pointer) to the next set of results.\n\n**Request:**\n```http\nGET /users?limit=10\nGET /users?cursor=<cursor>&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"next_cursor\": \"<next-cursor>\",\n    \"prev_cursor\": \"<prev-cursor>\",\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?cursor=<next-cursor>&limit=10\",\n    \"prev\": \"/users?cursor=<prev-cursor>&limit=10\"\n  }\n}\n```\n\n**Cursor structure (base64 encoded):**\n```json\n{\"id\": 30, \"sort\": \"created_at\"}\n```\n\n**Implementation:**\n```sql\n-- First page\nSELECT * FROM users ORDER BY created_at DESC LIMIT 10;\n\n-- Next page (cursor points to last item)\nSELECT * FROM users\nWHERE created_at < '2024-01-15T10:30:00Z'\nORDER BY created_at DESC\nLIMIT 10;\n```\n\n**Advantages:**\n- Consistent results (no skipped/duplicate items)\n- Efficient for large datasets\n- Works well with real-time data\n- No expensive COUNT query\n- Better database performance\n\n**Disadvantages:**\n- No random access (can't jump to page 10)\n- No total count\n- More complex to implement\n- Cursor is opaque (users can't modify it)\n\n**Use when:**\n- Large datasets\n- Data changes frequently\n- Infinite scroll UI\n- Real-time feeds\n- Performance is critical\n\n### 4. Keyset Pagination\n\nSimilar to cursor but uses actual field values instead of opaque cursor.\n\n**Request:**\n```http\nGET /users?after_id=20&limit=10\nGET /users?after_created_at=2024-01-15T10:30:00Z&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\", \"created_at\": \"2024-01-15T11:00:00Z\"},\n    {\"id\": 22, \"name\": \"User 22\", \"created_at\": \"2024-01-15T11:30:00Z\"}\n  ],\n  \"pagination\": {\n    \"after_id\": 30,\n    \"limit\": 10,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?after_id=30&limit=10\"\n  }\n}\n```\n\n**Implementation:**\n```sql\nSELECT * FROM users\nWHERE id > 20\nORDER BY id ASC\nLIMIT 10;\n```\n\n**Advantages:**\n- Very efficient (uses index)\n- Transparent cursor (human readable)\n- Consistent results\n- Simple implementation\n\n**Disadvantages:**\n- Requires indexed column\n- No random access\n- Sorting limited to cursor field\n- Complex for multi-field sorting\n\n**Use when:**\n- Simple ordering (by ID, timestamp)\n- Need efficient pagination\n- Want transparent cursor\n- Have proper indexes\n\n### 5. Seek Pagination (Time-Based)\n\nSpecialized keyset pagination for time-series data.\n\n**Request:**\n```http\nGET /events?since=2024-01-15T10:00:00Z&until=2024-01-15T11:00:00Z&limit=100\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"since\": \"2024-01-15T10:00:00Z\",\n    \"until\": \"2024-01-15T11:00:00Z\",\n    \"limit\": 100,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/events?since=2024-01-15T11:00:00Z&until=2024-01-15T12:00:00Z&limit=100\"\n  }\n}\n```\n\n**Use for:**\n- Time-series data\n- Logs and events\n- Activity streams\n- Analytics data\n\n## Default Limits\n\nAlways set reasonable defaults and maximum limits:\n\n```json\n{\n  \"default_limit\": 20,\n  \"max_limit\": 100,\n  \"min_limit\": 1\n}\n```\n\n**Validation:**\n```http\nGET /users?limit=1000\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"INVALID_LIMIT\",\n    \"message\": \"Limit must be between 1 and 100. Default is 20.\"\n  }\n}\n```\n\n## Response Format\n\n### Standard Pagination Object\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"limit\": 10,\n    \"offset\": 20,\n    \"total\": 150,\n    \"has_more\": true,\n    \"has_previous\": true\n  }\n}\n```\n\n### Link Header (RFC 5988)\n\n```http\nLink: </users?offset=0&limit=10>; rel=\"first\",\n      </users?offset=10&limit=10>; rel=\"prev\",\n      </users?offset=30&limit=10>; rel=\"next\",\n      </users?offset=140&limit=10>; rel=\"last\"\n```\n\n**Used by:** GitHub API\n\n### Embedded Links\n\n```json\n{\n  \"data\": [...],\n  \"_links\": {\n    \"self\": { \"href\": \"/users?offset=20&limit=10\" },\n    \"first\": { \"href\": \"/users?offset=0&limit=10\" },\n    \"prev\": { \"href\": \"/users?offset=10&limit=10\" },\n    \"next\": { \"href\": \"/users?offset=30&limit=10\" },\n    \"last\": { \"href\": \"/users?offset=140&limit=10\" }\n  }\n}\n```\n\n## Sorting with Pagination\n\nAlways support sorting when paginating:\n\n```http\nGET /users?sort=created_at&order=desc&limit=10\nGET /users?sort=-created_at&limit=10                    # Descending\nGET /users?sort=last_name,first_name&limit=10           # Multi-field\n```\n\n**For cursor pagination, cursor must include sort fields:**\n```json\n{\n  \"cursor\": {\n    \"id\": 123,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"sort_fields\": [\"created_at\", \"id\"]\n  }\n}\n```\n\n## Filtering with Pagination\n\nCombine filtering with pagination:\n\n```http\nGET /users?status=active&role=admin&offset=0&limit=10\n```\n\n**Important:** Apply filters before pagination:\n1. Filter records\n2. Count filtered results\n3. Apply pagination\n4. Return paginated subset\n\n## Total Count\n\n### Include Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"total\": 1523,\n    \"limit\": 10,\n    \"offset\": 20\n  }\n}\n```\n\n**Pros:**\n- Clients know total results\n- Can calculate total pages\n- Better UX (show \"Page 3 of 153\")\n\n**Cons:**\n- COUNT query is expensive\n- Slows down response\n- Inaccurate for large/changing datasets\n\n### Omit Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"has_more\": true,\n    \"limit\": 10\n  }\n}\n```\n\n**Use when:**\n- Large datasets (COUNT is too slow)\n- Real-time data (count changes constantly)\n- Cursor pagination\n- Infinite scroll UI\n\n### Optional Total Count\n\nLet client request total count:\n\n```http\nGET /users?limit=10&include_total=true\n```\n\n## Edge Cases\n\n### Empty Results\n\n```json\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 0,\n    \"limit\": 10,\n    \"total\": 0,\n    \"has_more\": false\n  }\n}\n```\n\n### Last Page\n\n```json\n{\n  \"data\": [{\"id\": 150, \"name\": \"Last User\"}],\n  \"pagination\": {\n    \"offset\": 140,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=130&limit=10\",\n    \"next\": null\n  }\n}\n```\n\n### Out of Range\n\n```http\nGET /users?offset=10000&limit=10\n\nResponse: 200 OK (empty results)\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 10000,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  }\n}\n```\n\nOr return 404 for pages that don't exist:\n```http\nGET /users?page=1000&per_page=10\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"PAGE_NOT_FOUND\",\n    \"message\": \"Page 1000 does not exist. Total pages: 15\"\n  }\n}\n```\n\n## Best Practices\n\n1. **Always paginate collections** - Never return unbounded lists\n2. **Set reasonable defaults** - Default limit of 20-50 items\n3. **Enforce maximum limits** - Prevent excessive loads (max 100-1000)\n4. **Include has_more flag** - Tell clients if more results exist\n5. **Provide navigation links** - Make it easy to get next/prev pages\n6. **Document pagination** - Explain cursor format, limits, defaults\n7. **Be consistent** - Use same pagination pattern across all endpoints\n8. **Consider performance** - Choose strategy based on data size/type\n9. **Support sorting** - Let clients control result order\n10. **Handle edge cases** - Empty results, last page, invalid cursors\n\n## Comparison Matrix\n\n| Feature | Offset | Page | Cursor | Keyset |\n|---------|--------|------|--------|--------|\n| Performance | Poor for large offsets | Poor | Excellent | Excellent |\n| Random access | Yes | Yes | No | No |\n| Total count | Yes | Yes | No | Optional |\n| Consistency | Poor | Poor | Excellent | Excellent |\n| Complexity | Simple | Simple | Medium | Medium |\n| Real-time data | Poor | Poor | Excellent | Excellent |\n| Database load | High | High | Low | Low |\n| Use case | Small datasets | Web UIs | Feeds/streams | Large datasets |\n","skills/api-designer/references/rest-patterns.md":"# REST Design Patterns\n\n## Resource-Oriented Architecture\n\nREST APIs are built around resources, not actions. Resources are the nouns of your API.\n\n### Resource Identification\n\n**Good Resource URIs:**\n```\nGET    /users                  # Collection\nGET    /users/{id}             # Individual resource\nGET    /users/{id}/orders      # Nested collection\nPOST   /users                  # Create resource\nPUT    /users/{id}             # Replace resource\nPATCH  /users/{id}             # Update resource\nDELETE /users/{id}             # Delete resource\n```\n\n**Bad Resource URIs:**\n```\nPOST   /getUser                # Verb in URI\nPOST   /createUser             # Verb in URI\nGET    /user?action=delete     # Action as query param\n```\n\n### Resource Naming Conventions\n\n- Use plural nouns for collections: `/users`, `/orders`, `/products`\n- Use lowercase and hyphens for readability: `/shipping-addresses`\n- Avoid deep nesting (max 2-3 levels): `/users/{id}/orders/{orderId}`\n- Use query parameters for filtering: `/users?status=active&role=admin`\n\n## HTTP Method Semantics\n\n### Safe and Idempotent Methods\n\n| Method | Safe | Idempotent | Use Case |\n|--------|------|------------|----------|\n| GET | Yes | Yes | Retrieve resource(s) |\n| POST | No | No | Create resource, non-idempotent operations |\n| PUT | No | Yes | Replace entire resource |\n| PATCH | No | No | Partial update |\n| DELETE | No | Yes | Remove resource |\n| HEAD | Yes | Yes | Get metadata only |\n| OPTIONS | Yes | Yes | Get allowed methods |\n\n### Method Usage\n\n**GET - Retrieve Resources**\n```http\nGET /users/123\nAccept: application/json\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"created_at\": \"2024-01-15T10:30:00Z\"\n}\n```\n\n**POST - Create Resources**\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\"\n}\n\nResponse: 201 Created\nLocation: /users/124\n{\n  \"id\": 124,\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\",\n  \"created_at\": \"2024-01-16T14:20:00Z\"\n}\n```\n\n**PUT - Replace Resource**\n```http\nPUT /users/123\nContent-Type: application/json\n\n{\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\",\n  \"updated_at\": \"2024-01-17T09:15:00Z\"\n}\n```\n\n**PATCH - Partial Update**\n```http\nPATCH /users/123\nContent-Type: application/json\n\n{\n  \"email\": \"john.updated@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john.updated@example.com\",\n  \"updated_at\": \"2024-01-17T10:00:00Z\"\n}\n```\n\n**DELETE - Remove Resource**\n```http\nDELETE /users/123\n\nResponse: 204 No Content\n```\n\n## HTTP Status Codes\n\n### Success Codes (2xx)\n\n- **200 OK** - Request succeeded (GET, PUT, PATCH)\n- **201 Created** - Resource created (POST), include Location header\n- **202 Accepted** - Request accepted for async processing\n- **204 No Content** - Success with no response body (DELETE)\n\n### Redirection (3xx)\n\n- **301 Moved Permanently** - Resource permanently moved\n- **302 Found** - Temporary redirect\n- **304 Not Modified** - Cached version is still valid\n\n### Client Errors (4xx)\n\n- **400 Bad Request** - Invalid request syntax or validation error\n- **401 Unauthorized** - Authentication required or failed\n- **403 Forbidden** - Authenticated but not authorized\n- **404 Not Found** - Resource doesn't exist\n- **405 Method Not Allowed** - HTTP method not supported for resource\n- **409 Conflict** - Request conflicts with current state (e.g., duplicate)\n- **422 Unprocessable Entity** - Valid syntax but semantic errors\n- **429 Too Many Requests** - Rate limit exceeded\n\n### Server Errors (5xx)\n\n- **500 Internal Server Error** - Unexpected server error\n- **502 Bad Gateway** - Invalid response from upstream server\n- **503 Service Unavailable** - Server temporarily unavailable\n- **504 Gateway Timeout** - Upstream server timeout\n\n## HATEOAS (Hypermedia)\n\n### Hypermedia-Driven APIs\n\nInclude links to related resources and available actions:\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" },\n    \"orders\": { \"href\": \"/users/123/orders\" },\n    \"update\": { \"href\": \"/users/123\", \"method\": \"PATCH\" },\n    \"delete\": { \"href\": \"/users/123\", \"method\": \"DELETE\" }\n  }\n}\n```\n\n### HAL (Hypertext Application Language)\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" }\n  },\n  \"_embedded\": {\n    \"orders\": [\n      {\n        \"id\": 456,\n        \"total\": 99.99,\n        \"_links\": {\n          \"self\": { \"href\": \"/orders/456\" }\n        }\n      }\n    ]\n  }\n}\n```\n\n## Content Negotiation\n\n### Accept Headers\n\n```http\nGET /users/123\nAccept: application/json\n\nGET /users/123\nAccept: application/xml\n\nGET /users/123\nAccept: application/hal+json\n```\n\n### Response Content-Type\n\n```http\nContent-Type: application/json; charset=utf-8\nContent-Type: application/problem+json\nContent-Type: application/hal+json\n```\n\n## Idempotency\n\n### Idempotent Operations\n\n**PUT - Always idempotent:**\nMultiple identical PUT requests produce the same result as a single request.\n\n**DELETE - Idempotent:**\nFirst DELETE returns 204, subsequent DELETEs return 404 (same end state).\n\n**POST - Not idempotent by default:**\nUse `Idempotency-Key` header for idempotent POST:\n\n```http\nPOST /payments\nIdempotency-Key: 550e8400-e29b-41d4-a716-446655440000\nContent-Type: application/json\n\n{\n  \"amount\": 100.00,\n  \"currency\": \"USD\"\n}\n```\n\nServer stores idempotency key and returns same response for duplicate requests.\n\n## Cache Control\n\n### Cache Headers\n\n```http\nCache-Control: public, max-age=3600\nCache-Control: private, no-cache\nCache-Control: no-store\nETag: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nLast-Modified: Wed, 15 Jan 2024 10:30:00 GMT\n```\n\n### Conditional Requests\n\n```http\nGET /users/123\nIf-None-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\n\nResponse: 304 Not Modified\n```\n\n```http\nPUT /users/123\nIf-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nContent-Type: application/json\n\n{\n  \"name\": \"Updated Name\"\n}\n\nResponse: 412 Precondition Failed (if ETag doesn't match)\n```\n\n## URI Patterns\n\n### Consistent URI Structure\n\n```\n/{version}/{resource}\n/{version}/{resource}/{id}\n/{version}/{resource}/{id}/{sub-resource}\n/{version}/{resource}/{id}/{sub-resource}/{sub-id}\n```\n\n### Query Parameters\n\n**Filtering:**\n```\nGET /users?status=active&role=admin\nGET /products?category=electronics&price_min=100&price_max=500\n```\n\n**Sorting:**\n```\nGET /users?sort=created_at\nGET /users?sort=-created_at          # Descending\nGET /users?sort=name,created_at      # Multiple fields\n```\n\n**Field Selection:**\n```\nGET /users?fields=id,name,email\nGET /users?exclude=password,social_security_number\n```\n\n**Search:**\n```\nGET /users?q=john\nGET /products?search=laptop\n```\n\n## Best Practices\n\n1. **Use nouns, not verbs** - Resources are nouns, methods are verbs\n2. **Plural collections** - Use `/users` not `/user`\n3. **Consistent naming** - Choose snake_case or camelCase and stick to it\n4. **Proper status codes** - Use appropriate HTTP status codes\n5. **Include metadata** - Pagination, filtering, sorting info in responses\n6. **Version your API** - Plan for evolution from day one\n7. **Document everything** - OpenAPI specs, examples, error codes\n8. **Security by default** - HTTPS, authentication, rate limiting\n9. **Support filtering** - Enable clients to get exactly what they need\n10. **Implement HATEOAS** - Make APIs self-documenting and discoverable\n","skills/api-designer/references/versioning.md":"# API Versioning Strategies\n\n## Why Version APIs?\n\nAPI versioning allows you to evolve your API while maintaining backward compatibility for existing clients. Breaking changes require a new version.\n\n### Breaking Changes\n\nChanges that require a new version:\n- Removing or renaming fields\n- Changing field types (string to integer)\n- Adding required fields to requests\n- Changing response structure\n- Removing endpoints\n- Changing HTTP status codes for same scenario\n- Changing authentication mechanisms\n\n### Non-Breaking Changes\n\nSafe changes that don't require a new version:\n- Adding new endpoints\n- Adding optional request fields\n- Adding new fields to responses (clients should ignore unknown fields)\n- Fixing bugs\n- Performance improvements\n- Adding new HTTP methods to existing resources\n\n## Versioning Strategies\n\n### 1. URI Versioning\n\nMost common and visible approach. Version is part of the URL path.\n\n```http\nGET /v1/users/123\nGET /v2/users/123\n```\n\n**Advantages:**\n- Clear and visible in URLs\n- Easy to understand and implement\n- Simple routing and caching\n- Can run multiple versions simultaneously\n\n**Disadvantages:**\n- Violates REST principle (same resource, different URIs)\n- Requires updating client code to change version\n- Can lead to URI proliferation\n\n**Implementation:**\n```\n/v1/users\n/v1/products\n/v2/users      # New version with breaking changes\n/v2/products\n```\n\n### 2. Header Versioning\n\nVersion specified in HTTP headers (Accept header or custom header).\n\n**Accept Header:**\n```http\nGET /users/123\nAccept: application/vnd.myapi.v1+json\n\nGET /users/123\nAccept: application/vnd.myapi.v2+json\n```\n\n**Custom Header:**\n```http\nGET /users/123\nAPI-Version: 1\n\nGET /users/123\nAPI-Version: 2\n```\n\n**Advantages:**\n- URIs remain stable\n- More RESTful (same resource, same URI)\n- Separates versioning from resource identification\n\n**Disadvantages:**\n- Less visible (harder to debug)\n- More complex routing\n- Difficult to test in browser\n- Cache complexity\n\n### 3. Query Parameter Versioning\n\nVersion specified as query parameter.\n\n```http\nGET /users/123?version=1\nGET /users/123?version=2\n\n# or\nGET /users/123?api-version=1\nGET /users/123?api-version=2\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to test\n- Visible in URLs\n\n**Disadvantages:**\n- Pollutes query string\n- Not semantic (version not a filter)\n- Can interfere with other query params\n\n### 4. Content Negotiation\n\nClient specifies desired version through content negotiation.\n\n```http\nGET /users/123\nAccept: application/vnd.myapi+json; version=1\n\nGET /users/123\nAccept: application/vnd.myapi+json; version=2\n```\n\n**Advantages:**\n- Very RESTful\n- Flexible content type negotiation\n- Stable URIs\n\n**Disadvantages:**\n- Complex implementation\n- Less intuitive for developers\n- Harder to test\n\n## Recommended Approach\n\n**URI versioning is recommended for most APIs** because:\n- It's the most explicit and discoverable\n- Easy to understand and debug\n- Simple to implement and maintain\n- Clear separation between versions\n\n```\n/v1/users\n/v2/users\n/v3/users\n```\n\n## Version Format\n\n### Major Versions Only\n\nUse simple major versions (v1, v2, v3) for public APIs:\n```\n/v1/users\n/v2/users\n```\n\n**Advantages:**\n- Simple and clear\n- Easy to communicate\n- Forces thoughtful breaking changes\n\n### Date-Based Versions\n\nSome APIs use dates for versions:\n```\n/2024-01-01/users\n/2024-06-15/users\n```\n\n**Used by:** Stripe, GitHub API\n\n**Advantages:**\n- Clear when version was released\n- Easy to understand timeline\n- No confusion about major/minor\n\n**Disadvantages:**\n- Less intuitive for clients\n- Harder to understand what changed\n\n## Version Lifecycle\n\n### 1. Introduction Phase\n\nNew version is released alongside existing version:\n```\n/v1/users  # Still supported\n/v2/users  # New version available\n```\n\nAnnounce new version:\n- Blog post explaining changes\n- Migration guide\n- Breaking changes list\n- Timeline for v1 deprecation\n\n### 2. Deprecation Phase\n\nMark old version as deprecated but keep it running:\n\n```http\nGET /v1/users/123\n\nResponse:\nDeprecation: true\nSunset: Wed, 15 Jan 2025 00:00:00 GMT\nLink: </v2/users/123>; rel=\"successor-version\"\n\n{\n  \"id\": 123,\n  \"name\": \"John Doe\"\n}\n```\n\n**Deprecation Headers:**\n- `Deprecation: true` - Indicates version is deprecated\n- `Sunset: <date>` - When version will be removed (RFC 8594)\n- `Link: <url>; rel=\"successor-version\"` - Points to new version\n\n### 3. Sunset Phase\n\nOld version is shut down on announced date.\n\nReturn 410 Gone for deprecated endpoints:\n```http\nGET /v1/users/123\n\nResponse: 410 Gone\n{\n  \"error\": {\n    \"code\": \"VERSION_SUNSET\",\n    \"message\": \"API v1 was sunset on 2025-01-15. Please use v2.\",\n    \"documentation_url\": \"https://api.example.com/docs/migration-v1-to-v2\"\n  }\n}\n```\n\n## Deprecation Policy\n\n### Recommended Timeline\n\n1. **Announce deprecation** - At least 6 months before sunset\n2. **Support period** - Run both versions for 6-12 months\n3. **Sunset date** - Clear date communicated in advance\n4. **Grace period** - 30 days of 410 Gone responses before complete shutdown\n\n### Communication Channels\n\n- API response headers\n- Email to registered developers\n- Blog posts and changelog\n- Dashboard notifications\n- Documentation updates\n- Status page announcements\n\n## Migration Strategy\n\n### Provide Migration Guide\n\n```markdown\n# Migrating from v1 to v2\n\n## Breaking Changes\n\n### User Resource Changes\n\n**v1:**\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**v2:**\n```json\n{\n  \"id\": 123,\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**Migration:**\n- Split `name` field into `first_name` and `last_name`\n- Update client code to use new fields\n```\n\n### Offer Tools\n\n- Migration scripts\n- SDK updates\n- API diff viewer\n- Compatibility layer (temporary)\n\n## Version Discovery\n\n### Root Endpoint\n\n```http\nGET /\n\nResponse:\n{\n  \"versions\": {\n    \"v1\": {\n      \"status\": \"deprecated\",\n      \"sunset_date\": \"2025-01-15\",\n      \"documentation_url\": \"https://api.example.com/docs/v1\"\n    },\n    \"v2\": {\n      \"status\": \"current\",\n      \"documentation_url\": \"https://api.example.com/docs/v2\"\n    },\n    \"v3\": {\n      \"status\": \"beta\",\n      \"documentation_url\": \"https://api.example.com/docs/v3\"\n    }\n  }\n}\n```\n\n### Version Info Endpoint\n\n```http\nGET /v2/version\n\nResponse:\n{\n  \"version\": \"v2\",\n  \"released\": \"2024-01-15\",\n  \"status\": \"stable\",\n  \"sunset_date\": null\n}\n```\n\n## OpenAPI Versioning\n\n### Separate Specs per Version\n\n```\nopenapi-v1.yaml\nopenapi-v2.yaml\nopenapi-v3.yaml\n```\n\nEach spec is complete and independent.\n\n### Single Spec with Servers\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 2.0.0\nservers:\n  - url: https://api.example.com/v1\n    description: Version 1 (deprecated)\n  - url: https://api.example.com/v2\n    description: Version 2 (current)\n```\n\n## Best Practices\n\n1. **Version from day one** - Start with /v1, not /api\n2. **Major versions only** - Use v1, v2, v3 (not v1.1, v1.2)\n3. **Long deprecation periods** - Give clients time to migrate (6-12 months)\n4. **Clear communication** - Use headers, docs, emails\n5. **Maintain old versions** - Support at least 2 versions simultaneously\n6. **Document changes** - Provide detailed migration guides\n7. **Use semantic versioning** - For internal/SDK versioning\n8. **Never break without warning** - Always announce breaking changes\n9. **Provide tools** - Migration scripts, updated SDKs\n10. **Monitor usage** - Track which versions are being used\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Breaking changes without version bump** - Breaks existing clients\n- **Too many versions** - Maintenance nightmare (max 2-3 active versions)\n- **Short deprecation periods** - Frustrates developers\n- **No migration path** - Makes upgrades painful\n- **Surprise sunsets** - Breaks production apps without warning\n- **Inconsistent versioning** - Different strategies for different endpoints\n- **Versioning individual endpoints** - Use consistent version across API\n","skills/architecture-designer/SKILL.md":"---\nname: architecture-designer\ndescription: Use when designing new high-level system architecture, reviewing existing designs, or making architectural decisions. Invoke to create architecture diagrams, write Architecture Decision Records (ADRs), evaluate technology trade-offs, design component interactions, and plan for scalability. Use for system design, architecture review, microservices structuring, ADR authoring, scalability planning, and infrastructure pattern selection — distinct from code-level design patterns or database-only design tasks.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.1\"\n  domain: api-architecture\n  triggers: architecture, system design, design pattern, microservices, scalability, ADR, technical design, infrastructure\n  role: expert\n  scope: design\n  output-format: document\n  related-skills: fullstack-guardian, devops-engineer, secure-code-guardian, microservices-architect, code-reviewer\n---\n\n# Architecture Designer\n\nSenior software architect specializing in system design, design patterns, and architectural decision-making.\n\n## Role Definition\n\nYou are a principal architect with 15+ years of experience designing scalable, distributed systems. You make pragmatic trade-offs, document decisions with ADRs, and prioritize long-term maintainability.\n\n## When to Use This Skill\n\n- Designing new system architecture\n- Choosing between architectural patterns\n- Reviewing existing architecture\n- Creating Architecture Decision Records (ADRs)\n- Planning for scalability\n- Evaluating technology choices\n\n## Core Workflow\n\n1. **Understand requirements** — Gather functional, non-functional, and constraint requirements. _Verify full requirements coverage before proceeding._\n2. **Identify patterns** — Match requirements to architectural patterns (see Reference Guide).\n3. **Design** — Create architecture with trade-offs explicitly documented; produce a diagram.\n4. **Document** — Write ADRs for all key decisions.\n5. **Review** — Validate with stakeholders. _If review fails, return to step 3 with recorded feedback._\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Architecture Patterns | `references/architecture-patterns.md` | Choosing monolith vs microservices |\n| ADR Template | `references/adr-template.md` | Documenting decisions |\n| System Design | `references/system-design.md` | Full system design template |\n| Database Selection | `references/database-selection.md` | Choosing database technology |\n| NFR Checklist | `references/nfr-checklist.md` | Gathering non-functional requirements |\n\n## Constraints\n\n### MUST DO\n- Document all significant decisions with ADRs\n- Consider non-functional requirements explicitly\n- Evaluate trade-offs, not just benefits\n- Plan for failure modes\n- Consider operational complexity\n- Review with stakeholders before finalizing\n\n### MUST NOT DO\n- Over-engineer for hypothetical scale\n- Choose technology without evaluating alternatives\n- Ignore operational costs\n- Design without understanding requirements\n- Skip security considerations\n\n## Output Templates\n\nWhen designing architecture, provide:\n1. Requirements summary (functional + non-functional)\n2. High-level architecture diagram (Mermaid preferred — see example below)\n3. Key decisions with trade-offs (ADR format — see example below)\n4. Technology recommendations with rationale\n5. Risks and mitigation strategies\n\n### Architecture Diagram (Mermaid)\n\n```mermaid\ngraph TD\n    Client[\"Client (Web/Mobile)\"] --> Gateway[\"API Gateway\"]\n    Gateway --> AuthSvc[\"Auth Service\"]\n    Gateway --> OrderSvc[\"Order Service\"]\n    OrderSvc --> DB[(\"Orders DB\\n(PostgreSQL)\")]\n    OrderSvc --> Queue[\"Message Queue\\n(RabbitMQ)\"]\n    Queue --> NotifySvc[\"Notification Service\"]\n```\n\n### ADR Example\n\n```markdown\n# ADR-001: Use PostgreSQL for Order Storage\n\n## Status\nAccepted\n\n## Context\nThe Order Service requires ACID-compliant transactions and complex relational queries\nacross orders, line items, and customers.\n\n## Decision\nUse PostgreSQL as the primary datastore for the Order Service.\n\n## Alternatives Considered\n- **MongoDB** — flexible schema, but lacks strong ACID guarantees across documents.\n- **DynamoDB** — excellent scalability, but complex query patterns require denormalization.\n\n## Consequences\n- Positive: Strong consistency, mature tooling, complex query support.\n- Negative: Vertical scaling limits; horizontal sharding adds operational complexity.\n\n## Trade-offs\nConsistency and query flexibility are prioritised over unlimited horizontal write scalability.\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/architecture-designer/)\n","skills/architecture-designer/references/adr-template.md":"# ADR Template\n\n## ADR Format\n\n```markdown\n# ADR-{number}: {Title}\n\n## Status\n[Proposed | Accepted | Deprecated | Superseded by ADR-XXX]\n\n## Context\n[Describe the situation and forces at play. What is the problem?\nWhat constraints exist? What are we trying to achieve?]\n\n## Decision\n[State the decision clearly. What are we going to do?]\n\n## Consequences\n\n### Positive\n- [Benefit 1]\n- [Benefit 2]\n\n### Negative\n- [Drawback 1]\n- [Drawback 2]\n\n### Neutral\n- [Side effect that is neither good nor bad]\n\n## Alternatives Considered\n[What other options were evaluated and why were they rejected?]\n\n## References\n- [Link to relevant documentation]\n- [Link to discussion/RFC]\n```\n\n## Example: Database Selection\n\n```markdown\n# ADR-001: Use PostgreSQL for primary database\n\n## Status\nAccepted\n\n## Context\nWe need a relational database for our e-commerce platform that:\n- Handles complex transactions with strong consistency\n- Supports JSON for flexible product attributes\n- Scales to millions of products and orders\n- Works well with our existing Python/Node stack\n\nTeam has experience with PostgreSQL and MySQL.\nBudget allows for managed database service.\n\n## Decision\nUse PostgreSQL as the primary database, hosted on AWS RDS.\n\n## Consequences\n\n### Positive\n- ACID compliance for financial transactions\n- Rich feature set (JSON, full-text search, CTEs)\n- Strong community and tooling\n- Excellent performance with proper indexing\n- Free and open source\n\n### Negative\n- Vertical scaling has limits (addressed with read replicas)\n- Requires DBA expertise for optimization\n- AWS RDS costs for high availability\n\n### Neutral\n- Team will need to learn PostgreSQL-specific features\n- Migration from current SQLite dev database needed\n\n## Alternatives Considered\n\n**MySQL**\n- Rejected: Less feature-rich for JSON operations\n- Considered: Similar cost, familiar to team\n\n**MongoDB**\n- Rejected: Relational data model needed for orders/inventory\n- Considered: Great for product catalog flexibility\n\n**CockroachDB**\n- Rejected: Higher cost, team unfamiliar\n- Considered: Better horizontal scaling\n\n## References\n- https://www.postgresql.org/docs/current/\n- Internal RFC: Database Selection for E-commerce Platform\n```\n\n## ADR Naming Convention\n\n```\ndocs/\n└── adr/\n    ├── 0001-use-postgresql-database.md\n    ├── 0002-adopt-microservices.md\n    ├── 0003-implement-event-sourcing.md\n    └── README.md\n```\n\n## Quick Reference\n\n| Section | Purpose | Key Question |\n|---------|---------|--------------|\n| Status | Current state | Is this active? |\n| Context | Background | Why are we deciding? |\n| Decision | The choice | What did we choose? |\n| Consequences | Impact | What happens now? |\n| Alternatives | Options | What else was considered? |\n","skills/architecture-designer/references/architecture-patterns.md":"# Architecture Patterns\n\n## Pattern Comparison\n\n| Pattern | Best For | Team Size | Trade-offs |\n|---------|----------|-----------|------------|\n| **Monolith** | Simple domain, small team | 1-10 | Simple deploy; hard to scale parts |\n| **Modular Monolith** | Growing complexity | 5-20 | Module boundaries; still single deploy |\n| **Microservices** | Complex domain, large org | 20+ | Independent scale; operational complexity |\n| **Serverless** | Variable load, event-driven | Any | Auto-scale; cold starts, vendor lock |\n| **Event-Driven** | Async processing | 10+ | Loose coupling; debugging complexity |\n\n## Monolith\n\n```\n┌─────────────────────────────────────┐\n│            Application              │\n│  ┌─────┐  ┌─────┐  ┌─────┐         │\n│  │Users│  │Orders│ │Products│       │\n│  └─────┘  └─────┘  └─────┘         │\n│  └──────────┬──────────────┘        │\n│          Database                    │\n└─────────────────────────────────────┘\n```\n\n**When to Use**:\n- Starting a new project\n- Small team (< 10 developers)\n- Simple domain\n- Rapid iteration needed\n\n**Pros**: Simple deployment, easy debugging, no network latency\n**Cons**: Hard to scale independently, technology locked, deployment risk\n\n## Microservices\n\n```\n┌──────────┐  ┌──────────┐  ┌──────────┐\n│  Users   │  │  Orders  │  │ Products │\n│ Service  │  │ Service  │  │ Service  │\n└────┬─────┘  └────┬─────┘  └────┬─────┘\n     │             │             │\n┌────▼────┐  ┌────▼────┐  ┌────▼────┐\n│ User DB │  │Order DB │  │ Prod DB │\n└─────────┘  └─────────┘  └─────────┘\n```\n\n**When to Use**:\n- Large team (20+ developers)\n- Complex domain with clear boundaries\n- Different scaling requirements per service\n- Polyglot technology needs\n\n**Pros**: Independent scaling, team autonomy, fault isolation\n**Cons**: Distributed system complexity, eventual consistency, operational overhead\n\n## Event-Driven\n\n```\n┌──────────┐     ┌─────────────┐     ┌──────────┐\n│ Producer │────▶│ Message Bus │────▶│ Consumer │\n└──────────┘     │  (Kafka)    │     └──────────┘\n                 └─────────────┘\n                       │\n                       ▼\n                 ┌──────────┐\n                 │ Consumer │\n                 └──────────┘\n```\n\n**When to Use**:\n- Async processing required\n- Loose coupling between services\n- Event sourcing needs\n- High throughput messaging\n\n**Pros**: Decoupled services, scalable, audit trail\n**Cons**: Eventual consistency, debugging complexity, message ordering\n\n## CQRS (Command Query Responsibility Segregation)\n\n```\n┌─────────┐         ┌─────────────┐\n│ Commands│────────▶│ Write Model │──┐\n└─────────┘         └─────────────┘  │\n                                     ▼\n                              ┌──────────┐\n                              │  Events  │\n                              └──────────┘\n                                     │\n┌─────────┐         ┌─────────────┐  │\n│ Queries │◀────────│ Read Model  │◀─┘\n└─────────┘         └─────────────┘\n```\n\n**When to Use**:\n- Read/write ratio heavily skewed\n- Complex read queries\n- Event sourcing architecture\n- Different optimization needs\n\n## Quick Reference\n\n| Requirement | Recommended Pattern |\n|-------------|---------------------|\n| Simple CRUD app | Monolith |\n| Growing startup | Modular Monolith |\n| Enterprise scale | Microservices |\n| Variable load | Serverless |\n| Async processing | Event-Driven |\n| Read-heavy | CQRS |\n","skills/architecture-designer/references/database-selection.md":"# Database Selection\n\n## Database Types\n\n| Type | Examples | Best For |\n|------|----------|----------|\n| **Relational** | PostgreSQL, MySQL | Transactions, complex queries, relationships |\n| **Document** | MongoDB, Firestore | Flexible schemas, rapid iteration |\n| **Key-Value** | Redis, DynamoDB | Caching, sessions, high throughput |\n| **Time-Series** | TimescaleDB, InfluxDB | Metrics, IoT, analytics |\n| **Graph** | Neo4j, Neptune | Relationships, social networks |\n| **Search** | Elasticsearch, Meilisearch | Full-text search, logs |\n\n## Relational (PostgreSQL, MySQL)\n\n```\nBest For:\n- Financial transactions (ACID compliance)\n- Complex queries with joins\n- Data integrity requirements\n- Structured, predictable schemas\n\nWhen to Avoid:\n- Highly variable schemas\n- Massive horizontal scaling needs\n- Simple key-value access patterns\n```\n\n| Feature | PostgreSQL | MySQL |\n|---------|------------|-------|\n| JSON support | Excellent (JSONB) | Good (JSON) |\n| Full-text search | Built-in | Basic |\n| Extensions | Rich ecosystem | Limited |\n| Replication | Streaming, logical | Statement, row-based |\n\n## Document (MongoDB, Firestore)\n\n```\nBest For:\n- Flexible, evolving schemas\n- Hierarchical data (nested documents)\n- Rapid prototyping\n- Content management\n\nWhen to Avoid:\n- Complex transactions across documents\n- Heavy relational queries\n- Strict schema requirements\n```\n\n## Key-Value (Redis, DynamoDB)\n\n```\nBest For:\n- Session storage\n- Caching layer\n- Real-time leaderboards\n- Rate limiting counters\n\nWhen to Avoid:\n- Complex queries\n- Relational data\n- Large value sizes (>1MB)\n```\n\n## Time-Series (TimescaleDB, InfluxDB)\n\n```\nBest For:\n- Metrics and monitoring\n- IoT sensor data\n- Financial tick data\n- Event logging with timestamps\n\nWhen to Avoid:\n- Frequent updates to existing records\n- Complex relational queries\n- Non-time-based access patterns\n```\n\n## Decision Matrix\n\n| Requirement | Recommended |\n|-------------|-------------|\n| ACID transactions | PostgreSQL, MySQL |\n| Flexible schema | MongoDB, Firestore |\n| High-speed caching | Redis |\n| Time-series data | TimescaleDB, InfluxDB |\n| Social relationships | Neo4j |\n| Full-text search | Elasticsearch |\n| Serverless scale | DynamoDB, Firestore |\n\n## Quick Reference\n\n| Question | If Yes → |\n|----------|----------|\n| Need ACID transactions? | Relational (PostgreSQL) |\n| Schema changes frequently? | Document (MongoDB) |\n| Sub-millisecond reads? | Key-Value (Redis) |\n| Time-based queries? | Time-Series |\n| Traversing relationships? | Graph (Neo4j) |\n| Full-text search primary? | Elasticsearch |\n","skills/architecture-designer/references/nfr-checklist.md":"# Non-Functional Requirements Checklist\n\n## NFR Categories\n\n### Scalability\n\n| Question | Common Targets |\n|----------|----------------|\n| Expected concurrent users? | 100 / 1K / 10K / 100K |\n| Requests per second? | 10 / 100 / 1000 / 10000 |\n| Data volume? | GB / TB / PB |\n| Growth rate? | 10% / 50% / 100% per year |\n| Peak vs average load? | 2x / 5x / 10x |\n\n### Performance\n\n| Question | Common Targets |\n|----------|----------------|\n| API response time? | < 100ms / 200ms / 500ms p95 |\n| Page load time? | < 1s / 2s / 3s |\n| Database query time? | < 10ms / 50ms / 100ms |\n| Batch processing throughput? | 1K / 10K / 100K records/hour |\n\n### Availability\n\n| Target | Downtime/Year | Use Case |\n|--------|---------------|----------|\n| 99% | 3.65 days | Internal tools |\n| 99.9% | 8.76 hours | Business apps |\n| 99.95% | 4.38 hours | E-commerce |\n| 99.99% | 52.6 minutes | Financial systems |\n| 99.999% | 5.26 minutes | Life-critical |\n\n### Security\n\n| Question | Considerations |\n|----------|----------------|\n| Authentication required? | JWT, OAuth, SAML, MFA |\n| Authorization model? | RBAC, ABAC, ACL |\n| Data sensitivity? | Public, internal, confidential, PII |\n| Compliance requirements? | GDPR, HIPAA, PCI DSS, SOC 2 |\n| Encryption needs? | At rest, in transit, end-to-end |\n\n### Reliability\n\n| Question | Considerations |\n|----------|----------------|\n| Acceptable data loss? | RPO: 0 / 1hr / 24hr |\n| Recovery time target? | RTO: 1hr / 4hr / 24hr |\n| Backup frequency? | Real-time / hourly / daily |\n| Disaster recovery? | Single region / multi-region |\n\n### Maintainability\n\n| Question | Considerations |\n|----------|----------------|\n| Deployment frequency? | Daily / weekly / monthly |\n| Deployment strategy? | Blue-green, canary, rolling |\n| Monitoring requirements? | Logs, metrics, traces, alerts |\n| On-call requirements? | 24/7, business hours |\n\n### Cost\n\n| Question | Considerations |\n|----------|----------------|\n| Infrastructure budget? | $/month, $/user, $/request |\n| Operational budget? | FTE for maintenance |\n| Cost optimization? | Reserved instances, spot instances |\n| Cost alerts? | Thresholds for notification |\n\n## Template\n\n```markdown\n## Non-Functional Requirements\n\n### Performance\n- API response time: < 200ms p95\n- Page load time: < 2s\n- Database query time: < 50ms\n\n### Scalability\n- Concurrent users: 10,000\n- Requests per second: 1,000\n- Data volume: 1TB\n\n### Availability\n- Target: 99.9% (8.76 hours/year downtime)\n- RPO: 1 hour\n- RTO: 4 hours\n\n### Security\n- Authentication: JWT with refresh tokens\n- Authorization: Role-based (admin, user, guest)\n- Compliance: GDPR, SOC 2\n\n### Observability\n- Logging: Structured JSON to ELK\n- Metrics: Prometheus + Grafana\n- Tracing: OpenTelemetry\n- Alerts: PagerDuty integration\n```\n\n## Quick Reference\n\n| Category | Key Metric |\n|----------|------------|\n| Performance | Response time (p95) |\n| Scalability | Concurrent users, RPS |\n| Availability | Uptime percentage |\n| Reliability | RPO, RTO |\n| Security | Compliance requirements |\n| Cost | $/month budget |\n","skills/architecture-designer/references/system-design.md":"# System Design Template\n\n## Design Template\n\n```markdown\n# System: {System Name}\n\n## Requirements\n\n### Functional\n- [What the system must do]\n- [Core features and capabilities]\n\n### Non-Functional\n- **Performance**: Response time < 200ms p95\n- **Availability**: 99.9% uptime (8.76 hours downtime/year)\n- **Scalability**: Support 10,000 concurrent users\n- **Security**: PCI DSS compliance required\n\n### Constraints\n- Budget: $X/month for infrastructure\n- Timeline: MVP in 3 months\n- Team: 5 backend, 3 frontend engineers\n\n## High-Level Architecture\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│   Client    │────▶│  API Gateway │────▶│  Service    │\n│   (Web)     │     │   (Kong)    │     │  (Node.js)  │\n└─────────────┘     └─────────────┘     └─────────────┘\n                           │                   │\n                           ▼                   ▼\n                    ┌─────────────┐     ┌─────────────┐\n                    │    Auth     │     │  Database   │\n                    │  (Auth0)    │     │ (PostgreSQL)│\n                    └─────────────┘     └─────────────┘\n```\n\n## Component Details\n\n### API Layer\n- Technology: Node.js with Express/NestJS\n- Responsibilities: Request routing, validation, auth\n- Scaling: Horizontal via load balancer\n\n### Data Layer\n- Primary: PostgreSQL (transactions, relationships)\n- Cache: Redis (sessions, hot data)\n- Storage: S3 (files, images)\n\n### External Services\n- Auth: Auth0 (SSO, MFA)\n- Email: SendGrid (transactional)\n- Monitoring: Datadog (APM, logs)\n\n## Key Decisions\n\n| Decision | Rationale |\n|----------|-----------|\n| PostgreSQL over MongoDB | Relational data, ACID needed |\n| Redis for caching | Sub-ms latency required |\n| Auth0 over custom | Reduce security risk |\n\n## Scaling Strategy\n\n### Current (MVP)\n- Single region deployment\n- 2 API instances behind ALB\n- Single RDS instance\n\n### Future (10x growth)\n- Multi-region with CDN\n- Auto-scaling API (2-10 instances)\n- RDS read replicas\n\n## Security Considerations\n- All traffic over TLS 1.3\n- JWT tokens with 15-min expiry\n- Rate limiting: 100 req/min per user\n- WAF for common attacks\n\n## Failure Modes\n\n| Failure | Impact | Mitigation |\n|---------|--------|------------|\n| DB down | Full outage | Multi-AZ failover |\n| Cache down | Degraded perf | Fallback to DB |\n| Auth down | No new logins | Cache valid tokens |\n```\n\n## Quick Reference\n\n| Section | Key Questions |\n|---------|---------------|\n| Requirements | What must it do? How well? |\n| Architecture | What components? How connected? |\n| Decisions | Why these choices? |\n| Scaling | How to grow? |\n| Failures | What can break? How to recover? |\n","skills/atlassian-mcp/SKILL.md":"---\nname: atlassian-mcp\ndescription: Integrates with Atlassian products to manage project tracking and documentation via MCP protocol. Use when querying Jira issues with JQL filters, creating and updating tickets with custom fields, searching or editing Confluence pages with CQL, managing sprints and backlogs, setting up MCP server authentication, syncing documentation, or debugging Atlassian API integrations.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.2.0\"\n  domain: platform\n  triggers: Jira, Confluence, Atlassian, MCP, tickets, issues, wiki, JQL, CQL, sprint, backlog, project management\n  role: expert\n  scope: implementation\n  output-format: code\n  related-skills: mcp-developer, api-designer, security-reviewer\n---\n\n# Atlassian MCP Expert\n\n## When to Use This Skill\n\n- Querying Jira issues with JQL filters\n- Searching or creating Confluence pages\n- Automating sprint workflows and backlog management\n- Setting up MCP server authentication (OAuth/API tokens)\n- Syncing meeting notes to Jira tickets\n- Generating documentation from issue data\n- Debugging Atlassian API integration issues\n- Choosing between official vs open-source MCP servers\n\n## Core Workflow\n\n1. **Select server** - Choose official cloud, open-source, or self-hosted MCP server\n2. **Authenticate** - Configure OAuth 2.1, API tokens, or PAT credentials\n3. **Design queries** - Write JQL for Jira, CQL for Confluence; validate with `maxResults=1` before full execution\n4. **Implement workflow** - Build tool calls, handle pagination, error recovery\n5. **Verify permissions** - Confirm required scopes with a read-only probe before any write or bulk operation\n6. **Deploy** - Configure IDE integration, test permissions, monitor rate limits\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Server Setup | `references/mcp-server-setup.md` | Installation, choosing servers, configuration |\n| Jira Operations | `references/jira-queries.md` | JQL syntax, issue CRUD, sprints, boards, issue linking |\n| Confluence Ops | `references/confluence-operations.md` | CQL search, page creation, spaces, comments |\n| Authentication | `references/authentication-patterns.md` | OAuth 2.0, API tokens, permission scopes |\n| Common Workflows | `references/common-workflows.md` | Issue triage, doc sync, sprint automation |\n\n## Quick-Start Examples\n\n### JQL Query Samples\n```\n# Open issues assigned to current user in a sprint\nproject = PROJ AND status = \"In Progress\" AND assignee = currentUser() ORDER BY priority DESC\n\n# Unresolved bugs created in the last 7 days\nproject = PROJ AND issuetype = Bug AND status != Done AND created >= -7d ORDER BY created DESC\n\n# Validate before bulk: test with maxResults=1 first\nproject = PROJ AND sprint in openSprints() AND status = Open ORDER BY created DESC\n```\n\n### CQL Query Samples\n```\n# Find pages updated in a specific space recently\nspace = \"ENG\" AND type = page AND lastModified >= \"2024-01-01\" ORDER BY lastModified DESC\n\n# Search page text for a keyword\nspace = \"ENG\" AND type = page AND text ~ \"deployment runbook\"\n```\n\n### Minimal MCP Server Configuration\nAtlassian Cloud: connect to the hosted Atlassian Rovo MCP Server, then run `/mcp` to sign in with OAuth.\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nServer/Data Center: run the community `mcp-atlassian` server (sooperset) with personal access tokens.\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.example.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"${JIRA_PERSONAL_TOKEN}\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.example.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"${CONFLUENCE_PERSONAL_TOKEN}\"\n      }\n    }\n  }\n}\n```\n> **Note:** Always load tokens from environment variables or a secrets manager; never hardcode credentials. Tool names differ between servers, see `references/mcp-server-setup.md`.\n\n## Constraints\n\n### MUST DO\n- Respect user permissions and workspace access controls\n- Validate JQL/CQL queries before execution (use `maxResults=1` probe first)\n- Handle rate limits with exponential backoff\n- Use pagination for large result sets (50-100 items per page)\n- Implement error recovery for network failures\n- Log API calls for debugging and audit trails\n- Test with read-only operations first\n- Document required permission scopes\n- Confirm before any write or bulk operation against production data\n\n### MUST NOT DO\n- Hardcode API tokens or OAuth secrets in code\n- Ignore rate limit headers from Atlassian APIs\n- Create issues without validating required fields\n- Skip input sanitization on user-provided query strings\n- Deploy without testing permission boundaries\n- Update production data without confirmation prompts\n- Mix different authentication methods in same session\n- Expose sensitive issue data in logs or error messages\n\n## Output Templates\n\nWhen implementing Atlassian MCP features, provide:\n1. MCP server configuration (JSON/environment vars)\n2. Query examples (JQL/CQL with explanations)\n3. Tool call implementation with error handling\n4. Authentication setup instructions\n5. Brief explanation of permission requirements\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/platform/atlassian-mcp/)\n","skills/atlassian-mcp/references/authentication-patterns.md":"# Authentication Patterns\n\n---\n\n## Authentication Methods Overview\n\n| Method | Platform | Use Case | Security Level |\n|--------|----------|----------|----------------|\n| OAuth 2.1 | Cloud | User-facing apps, integrations | Highest |\n| API Token | Cloud | Personal automation, scripts | Medium |\n| PAT | Server/DC | Server integrations | Medium |\n| Basic Auth | Legacy | Deprecated, avoid | Low |\n\n## OAuth 2.1 (Atlassian Cloud)\n\n### Authorization Code Flow\n\nFor applications that act on behalf of users.\n\n**Step 1: Register Your App**\n\n1. Go to [developer.atlassian.com](https://developer.atlassian.com/console/myapps/)\n2. Create new app\n3. Configure OAuth 2.0 (3LO)\n4. Add callback URL\n5. Request necessary scopes\n\n**Step 2: Authorization Request**\n\n```typescript\nconst authUrl = new URL('https://auth.atlassian.com/authorize');\nauthUrl.searchParams.set('audience', 'api.atlassian.com');\nauthUrl.searchParams.set('client_id', CLIENT_ID);\nauthUrl.searchParams.set('scope', 'read:jira-work write:jira-work read:confluence-content.all write:confluence-content');\nauthUrl.searchParams.set('redirect_uri', REDIRECT_URI);\nauthUrl.searchParams.set('state', generateState());\nauthUrl.searchParams.set('response_type', 'code');\nauthUrl.searchParams.set('prompt', 'consent');\n\n// Redirect user to authUrl.toString()\n```\n\n**Step 3: Exchange Code for Token**\n\n```typescript\nasync function exchangeCodeForToken(code: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'authorization_code',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      code,\n      redirect_uri: REDIRECT_URI,\n    }),\n  });\n\n  if (!response.ok) {\n    throw new Error(`Token exchange failed: ${response.statusText}`);\n  }\n\n  return response.json();\n}\n\ninterface TokenResponse {\n  access_token: string;\n  refresh_token: string;\n  expires_in: number;\n  scope: string;\n  token_type: 'Bearer';\n}\n```\n\n**Step 4: Refresh Token**\n\n```typescript\nasync function refreshAccessToken(refreshToken: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'refresh_token',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      refresh_token: refreshToken,\n    }),\n  });\n\n  return response.json();\n}\n```\n\n**Step 5: Get Accessible Resources**\n\n```typescript\nasync function getAccessibleResources(accessToken: string): Promise<Resource[]> {\n  const response = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    {\n      headers: {\n        Authorization: `Bearer ${accessToken}`,\n        Accept: 'application/json',\n      },\n    }\n  );\n\n  return response.json();\n}\n\ninterface Resource {\n  id: string;        // Cloud ID\n  name: string;      // Site name\n  url: string;       // https://your-site.atlassian.net\n  scopes: string[];\n  avatarUrl: string;\n}\n```\n\n### OAuth Scopes Reference\n\n**Jira Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:jira-work` | Read issues, projects, boards |\n| `write:jira-work` | Create/update issues |\n| `manage:jira-project` | Manage project settings |\n| `manage:jira-configuration` | Manage global settings |\n| `read:jira-user` | Read user profiles |\n| `manage:jira-data-provider` | Data provider integrations |\n\n**Confluence Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:confluence-content.all` | Read all content |\n| `write:confluence-content` | Create/update content |\n| `read:confluence-content.summary` | Read content summaries |\n| `read:confluence-space.summary` | Read space summaries |\n| `write:confluence-space` | Create/manage spaces |\n| `read:confluence-user` | Read user profiles |\n\n**Granular Scopes (v2):**\n\n```\nread:issue-details:jira\nwrite:issue:jira\nread:sprint:jira-software\nwrite:sprint:jira-software\nread:page:confluence\nwrite:page:confluence\nread:comment:confluence\nwrite:comment:confluence\n```\n\n## API Tokens (Atlassian Cloud)\n\n### Creating API Token\n\n1. Go to [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens)\n2. Click \"Create API token\"\n3. Give it a descriptive label\n4. Copy token immediately (shown only once)\n\n### Using API Token\n\n```typescript\n// Basic authentication with API token\nconst credentials = Buffer.from(`${email}:${apiToken}`).toString('base64');\n\nconst response = await fetch('https://your-site.atlassian.net/rest/api/3/myself', {\n  headers: {\n    Authorization: `Basic ${credentials}`,\n    Accept: 'application/json',\n  },\n});\n\n// For MCP server configuration\nconst config = {\n  JIRA_URL: 'https://your-site.atlassian.net',\n  JIRA_USERNAME: 'your-email@company.com',\n  JIRA_API_TOKEN: '<jira-api-token>',\n};\n```\n\n### Token Security Best Practices\n\n```typescript\n// Store tokens securely\nimport { SecretManagerServiceClient } from '@google-cloud/secret-manager';\nimport { SecretsManager } from '@aws-sdk/client-secrets-manager';\n\n// Option 1: Environment variables (development only)\nconst token = process.env.ATLASSIAN_API_TOKEN;\n\n// Option 2: GCP Secret Manager\nasync function getTokenFromGCP(secretName: string): Promise<string> {\n  const client = new SecretManagerServiceClient();\n  const [version] = await client.accessSecretVersion({\n    name: `projects/my-project/secrets/${secretName}/versions/latest`,\n  });\n  return version.payload?.data?.toString() || '';\n}\n\n// Option 3: AWS Secrets Manager\nasync function getTokenFromAWS(secretName: string): Promise<string> {\n  const client = new SecretsManager({ region: 'us-east-1' });\n  const response = await client.getSecretValue({ SecretId: secretName });\n  return response.SecretString || '';\n}\n\n// Option 4: HashiCorp Vault\nasync function getTokenFromVault(path: string): Promise<string> {\n  const response = await fetch(`${VAULT_ADDR}/v1/${path}`, {\n    headers: { 'X-Vault-Token': VAULT_TOKEN },\n  });\n  const data = await response.json();\n  return data.data.data.token;\n}\n```\n\n## Personal Access Tokens (Server/Data Center)\n\n### Creating PAT\n\n**Jira Server/DC:**\n1. Profile > Personal Access Tokens\n2. Create token\n3. Set expiration date\n4. Select permissions\n\n**Confluence Server/DC:**\n1. Profile > Settings > Personal Access Tokens\n2. Create token\n3. Configure permissions\n\n### Using PAT\n\n```typescript\n// Bearer token authentication\nconst response = await fetch('https://jira.internal.company.com/rest/api/2/myself', {\n  headers: {\n    Authorization: `Bearer ${personalAccessToken}`,\n    Accept: 'application/json',\n  },\n});\n\n// MCP server configuration\nconst config = {\n  JIRA_URL: 'https://jira.internal.company.com',\n  JIRA_PERSONAL_TOKEN: '<jira-personal-access-token>',\n};\n```\n\n### PAT Permissions\n\n| Permission | Jira | Confluence |\n|------------|------|------------|\n| Read | Browse projects, view issues | View pages |\n| Write | Create/edit issues | Create/edit pages |\n| Admin | Project administration | Space administration |\n\n## Token Management\n\n### Token Lifecycle Manager\n\n```typescript\ninterface TokenInfo {\n  accessToken: string;\n  refreshToken?: string;\n  expiresAt: Date;\n  scopes: string[];\n}\n\nclass TokenManager {\n  private tokenInfo: TokenInfo | null = null;\n  private refreshThreshold = 5 * 60 * 1000; // 5 minutes\n\n  async getValidToken(): Promise<string> {\n    if (!this.tokenInfo) {\n      throw new Error('Not authenticated');\n    }\n\n    // Check if token needs refresh\n    const timeUntilExpiry = this.tokenInfo.expiresAt.getTime() - Date.now();\n\n    if (timeUntilExpiry < this.refreshThreshold) {\n      await this.refreshToken();\n    }\n\n    return this.tokenInfo.accessToken;\n  }\n\n  private async refreshToken(): Promise<void> {\n    if (!this.tokenInfo?.refreshToken) {\n      throw new Error('No refresh token available');\n    }\n\n    const response = await refreshAccessToken(this.tokenInfo.refreshToken);\n\n    this.tokenInfo = {\n      accessToken: response.access_token,\n      refreshToken: response.refresh_token || this.tokenInfo.refreshToken,\n      expiresAt: new Date(Date.now() + response.expires_in * 1000),\n      scopes: response.scope.split(' '),\n    };\n  }\n\n  isAuthenticated(): boolean {\n    return this.tokenInfo !== null && this.tokenInfo.expiresAt > new Date();\n  }\n\n  getScopes(): string[] {\n    return this.tokenInfo?.scopes || [];\n  }\n\n  hasScope(scope: string): boolean {\n    return this.getScopes().includes(scope);\n  }\n}\n```\n\n### Token Rotation Strategy\n\n```typescript\nclass TokenRotationManager {\n  private rotationInterval = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n  async checkAndRotate(tokenCreatedAt: Date): Promise<boolean> {\n    const age = Date.now() - tokenCreatedAt.getTime();\n\n    if (age > this.rotationInterval) {\n      console.warn('API token is due for rotation');\n      return true;\n    }\n\n    return false;\n  }\n\n  async sendRotationReminder(email: string, tokenLabel: string): Promise<void> {\n    // Integrate with your notification system\n    await sendEmail({\n      to: email,\n      subject: 'Atlassian API Token Rotation Reminder',\n      body: `Your API token \"${tokenLabel}\" is due for rotation.\n             Please create a new token and update your integrations.`,\n    });\n  }\n}\n```\n\n## Permission Verification\n\n### Check Current Permissions\n\n```typescript\nasync function verifyPermissions(\n  client: MCPClient,\n  requiredOperations: string[]\n): Promise<PermissionReport> {\n  const report: PermissionReport = {\n    hasAllPermissions: true,\n    details: [],\n  };\n\n  for (const operation of requiredOperations) {\n    try {\n      switch (operation) {\n        case 'read:jira':\n          await client.callTool({\n            name: 'jira_get_issue',\n            arguments: { issue_key: 'TEST-1' },\n          });\n          break;\n        case 'write:jira':\n          // Create and immediately delete a test issue\n          const created = await client.callTool({\n            name: 'jira_create_issue',\n            arguments: {\n              project_key: 'TEST',\n              issue_type: 'Task',\n              summary: '[Permission Test] Delete me',\n            },\n          });\n          // Clean up\n          await client.callTool({\n            name: 'jira_delete_issue',\n            arguments: { issue_key: JSON.parse(created.content[0].text).key },\n          });\n          break;\n        case 'read:confluence':\n          await client.callTool({\n            name: 'confluence_search',\n            arguments: { cql: 'type = page', limit: 1 },\n          });\n          break;\n      }\n\n      report.details.push({ operation, status: 'granted' });\n    } catch (error: any) {\n      report.hasAllPermissions = false;\n      report.details.push({\n        operation,\n        status: 'denied',\n        error: error.message,\n      });\n    }\n  }\n\n  return report;\n}\n```\n\n## Security Checklist\n\n### Do:\n- Use OAuth 2.1 for user-facing applications\n- Store secrets in dedicated secrets management systems\n- Implement token rotation policies\n- Use minimal required scopes\n- Log authentication events (without secrets)\n- Implement rate limiting at application level\n- Validate tokens before use\n\n### Don't:\n- Hardcode tokens in source code\n- Log tokens or secrets\n- Share tokens between environments\n- Use Basic Auth (deprecated)\n- Request more scopes than needed\n- Store tokens in browser localStorage\n- Commit `.env` files with real credentials\n\n### Environment Configuration Template\n\n```bash\n# .env.example (commit this)\nATLASSIAN_SITE_URL=https://your-site.atlassian.net\nATLASSIAN_AUTH_TYPE=oauth  # or 'api_token' or 'pat'\n\n# OAuth settings (if using OAuth)\nATLASSIAN_CLIENT_ID=\nATLASSIAN_CLIENT_SECRET=\n\n# API Token settings (if using API token)\nATLASSIAN_USERNAME=\nATLASSIAN_API_TOKEN=\n\n# PAT settings (if using Server/DC)\nATLASSIAN_PERSONAL_TOKEN=\n```\n\n```bash\n# .gitignore\n.env\n.env.local\n.env.*.local\ncredentials.json\n**/secrets/**\n```\n\n## Troubleshooting\n\n### Common Authentication Errors\n\n**401 Unauthorized:**\n- Invalid or expired token\n- Wrong authentication method\n- Missing Authorization header\n\n**403 Forbidden:**\n- Token valid but lacks required scope\n- Resource-level permission denied\n- IP allowlist blocking request\n\n**Token refresh fails:**\n- Refresh token expired (after 90 days of inactivity)\n- Client secret changed\n- App permissions revoked\n\n### Debug Authentication\n\n```typescript\nasync function debugAuth(token: string): Promise<void> {\n  // Check token validity\n  const meResponse = await fetch(\n    'https://api.atlassian.com/me',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  console.log('Token status:', meResponse.status);\n\n  if (meResponse.ok) {\n    const me = await meResponse.json();\n    console.log('Authenticated as:', me.email);\n  }\n\n  // Check accessible resources\n  const resourcesResponse = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  if (resourcesResponse.ok) {\n    const resources = await resourcesResponse.json();\n    console.log('Accessible sites:', resources.map((r: any) => r.name));\n  }\n}\n```\n\n## Related References\n\n- `mcp-server-setup.md` - Server configuration with credentials\n- `jira-queries.md` - Operations that require authentication\n- `confluence-operations.md` - Content operations with auth\n","skills/atlassian-mcp/references/common-workflows.md":"# Common Workflows\n\n---\n\n## Issue Triage Workflow\n\nAutomatically categorize, prioritize, and route incoming issues.\n\n### Triage Bot Implementation\n\n```typescript\ninterface TriageResult {\n  issueKey: string;\n  category: string;\n  priority: string;\n  assignee: string | null;\n  labels: string[];\n  actions: string[];\n}\n\nasync function triageNewIssue(\n  client: MCPClient,\n  issueKey: string\n): Promise<TriageResult> {\n  // Get issue details\n  const issueResult = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: {\n      issue_key: issueKey,\n      expand: [\"changelog\"]\n    }\n  });\n\n  const issue = JSON.parse(issueResult.content[0].text);\n  const result: TriageResult = {\n    issueKey,\n    category: \"uncategorized\",\n    priority: issue.fields.priority?.name || \"Medium\",\n    assignee: null,\n    labels: [],\n    actions: []\n  };\n\n  // Categorize based on content\n  const summary = issue.fields.summary.toLowerCase();\n  const description = (issue.fields.description?.content?.[0]?.content?.[0]?.text || \"\").toLowerCase();\n  const text = `${summary} ${description}`;\n\n  // Category detection\n  if (text.match(/\\b(crash|down|outage|critical|emergency)\\b/)) {\n    result.category = \"incident\";\n    result.priority = \"Highest\";\n    result.labels.push(\"incident\", \"urgent\");\n  } else if (text.match(/\\b(bug|error|fail|broken|doesn't work)\\b/)) {\n    result.category = \"bug\";\n    result.labels.push(\"bug\", \"needs-investigation\");\n  } else if (text.match(/\\b(feature|enhancement|request|add|improve)\\b/)) {\n    result.category = \"feature\";\n    result.labels.push(\"feature-request\");\n  } else if (text.match(/\\b(docs|documentation|readme|guide)\\b/)) {\n    result.category = \"documentation\";\n    result.labels.push(\"documentation\");\n  }\n\n  // Component detection for routing\n  const componentMap: Record<string, { component: string; team: string }> = {\n    \"api|rest|endpoint\": { component: \"API\", team: \"backend-team\" },\n    \"ui|button|page|screen|css\": { component: \"Frontend\", team: \"frontend-team\" },\n    \"database|sql|query|migration\": { component: \"Database\", team: \"data-team\" },\n    \"auth|login|password|sso\": { component: \"Authentication\", team: \"security-team\" },\n    \"deploy|ci|cd|pipeline\": { component: \"DevOps\", team: \"platform-team\" }\n  };\n\n  for (const [pattern, { component, team }] of Object.entries(componentMap)) {\n    if (text.match(new RegExp(`\\\\b(${pattern})\\\\b`))) {\n      result.labels.push(component.toLowerCase());\n      result.actions.push(`Route to ${team}`);\n      break;\n    }\n  }\n\n  // Apply triage results\n  await client.callTool({\n    name: \"jira_update_issue\",\n    arguments: {\n      issue_key: issueKey,\n      fields: {\n        priority: { name: result.priority },\n        labels: [...new Set([...issue.fields.labels || [], ...result.labels])]\n      }\n    }\n  });\n\n  // Add triage comment\n  await client.callTool({\n    name: \"jira_add_comment\",\n    arguments: {\n      issue_key: issueKey,\n      body: `*Automated Triage Results*\n\nCategory: ${result.category}\nPriority: ${result.priority}\nLabels added: ${result.labels.join(\", \")}\n\n${result.actions.length > 0 ? `Recommended actions:\\n${result.actions.map(a => `- ${a}`).join(\"\\n\")}` : \"\"}`\n    }\n  });\n\n  return result;\n}\n```\n\n### Bulk Triage New Issues\n\n```typescript\nasync function triageBacklog(client: MCPClient, projectKey: string): Promise<void> {\n  // Find untriaged issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND labels IS EMPTY AND created >= -7d AND resolution IS EMPTY`,\n      max_results: 50,\n      fields: [\"summary\", \"description\", \"priority\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  console.log(`Found ${response.total} issues to triage`);\n\n  for (const issue of response.issues) {\n    const result = await triageNewIssue(client, issue.key);\n    console.log(`Triaged ${issue.key}: ${result.category} (${result.priority})`);\n\n    // Rate limiting\n    await delay(500);\n  }\n}\n```\n\n## Documentation Sync Workflow\n\nKeep Confluence documentation in sync with Jira issues.\n\n### Generate Release Notes\n\n```typescript\nasync function generateReleaseNotes(\n  client: MCPClient,\n  projectKey: string,\n  version: string,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get all issues in the release\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND fixVersion = \"${version}\" AND resolution = Done ORDER BY issuetype, priority DESC`,\n      max_results: 200,\n      fields: [\"summary\", \"issuetype\", \"priority\", \"assignee\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n\n  // Group by issue type\n  const grouped: Record<string, any[]> = {};\n  for (const issue of response.issues) {\n    const type = issue.fields.issuetype.name;\n    if (!grouped[type]) grouped[type] = [];\n    grouped[type].push(issue);\n  }\n\n  // Build Confluence page content\n  let content = `\n<h2>Release ${version}</h2>\n<p>Released on ${new Date().toISOString().split('T')[0]}</p>\n\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">2</ac:parameter>\n</ac:structured-macro>\n\n<h2>Summary</h2>\n<p>This release includes ${response.total} changes.</p>\n<table>\n  <tr><th>Type</th><th>Count</th></tr>\n  ${Object.entries(grouped).map(([type, issues]) =>\n    `<tr><td>${type}</td><td>${issues.length}</td></tr>`\n  ).join('')}\n</table>\n`;\n\n  // Add sections for each issue type\n  const typeOrder = [\"New Feature\", \"Improvement\", \"Bug\", \"Task\"];\n  const orderedTypes = [...new Set([...typeOrder, ...Object.keys(grouped)])];\n\n  for (const type of orderedTypes) {\n    if (!grouped[type]) continue;\n\n    content += `\n<h2>${type}s</h2>\n<table>\n  <tr><th>Key</th><th>Summary</th><th>Assignee</th></tr>\n  ${grouped[type].map(issue => `\n    <tr>\n      <td><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issue.key}</ac:parameter></ac:structured-macro></td>\n      <td>${escapeHtml(issue.fields.summary)}</td>\n      <td>${issue.fields.assignee?.displayName || 'Unassigned'}</td>\n    </tr>\n  `).join('')}\n</table>\n`;\n  }\n\n  // Create or update Confluence page\n  const pageTitle = `Release Notes - ${version}`;\n\n  // Check if page exists\n  const existingSearch = await client.callTool({\n    name: \"confluence_search\",\n    arguments: {\n      cql: `space = \"${confluenceSpaceKey}\" AND title = \"${pageTitle}\"`,\n      limit: 1\n    }\n  });\n\n  const existingResults = JSON.parse(existingSearch.content[0].text);\n\n  if (existingResults.results.length > 0) {\n    // Update existing page\n    const pageId = existingResults.results[0].id;\n    const currentVersion = existingResults.results[0].version.number;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: pageId,\n        title: pageTitle,\n        body: content,\n        version_number: currentVersion + 1,\n        version_message: `Updated release notes for ${version}`\n      }\n    });\n\n    return pageId;\n  } else {\n    // Create new page\n    const newPage = await client.callTool({\n      name: \"confluence_create_page\",\n      arguments: {\n        space_key: confluenceSpaceKey,\n        title: pageTitle,\n        body: content,\n        labels: [\"release-notes\", `version-${version.replace(/\\./g, '-')}`]\n      }\n    });\n\n    return JSON.parse(newPage.content[0].text).id;\n  }\n}\n```\n\n### Sync Meeting Notes to Jira\n\n```typescript\ninterface ActionItem {\n  description: string;\n  assignee: string;\n  dueDate?: string;\n}\n\nasync function syncMeetingNotes(\n  client: MCPClient,\n  confluencePageId: string,\n  jiraProjectKey: string\n): Promise<string[]> {\n  // Get meeting notes content\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: confluencePageId,\n      expand: [\"body.storage\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n  const content = page.body.storage.value;\n\n  // Extract action items (look for checkbox patterns)\n  const actionItems = extractActionItems(content);\n  const createdIssues: string[] = [];\n\n  for (const item of actionItems) {\n    // Create Jira task\n    const newIssue = await client.callTool({\n      name: \"jira_create_issue\",\n      arguments: {\n        project_key: jiraProjectKey,\n        issue_type: \"Task\",\n        summary: item.description,\n        description: {\n          type: \"doc\",\n          version: 1,\n          content: [{\n            type: \"paragraph\",\n            content: [{\n              type: \"text\",\n              text: `Action item from meeting: ${page.title}\\n\\nSource: ${page._links.webui}`\n            }]\n          }]\n        },\n        assignee: item.assignee,\n        due_date: item.dueDate,\n        labels: [\"meeting-action\", \"auto-created\"]\n      }\n    });\n\n    const issueKey = JSON.parse(newIssue.content[0].text).key;\n    createdIssues.push(issueKey);\n  }\n\n  // Update Confluence page with Jira links\n  if (createdIssues.length > 0) {\n    const jiraLinksSection = `\n<h2>Linked Jira Issues</h2>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">key IN (${createdIssues.join(',')})</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n`;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: confluencePageId,\n        title: page.title,\n        body: content + jiraLinksSection,\n        version_number: page.version.number + 1\n      }\n    });\n  }\n\n  return createdIssues;\n}\n\nfunction extractActionItems(html: string): ActionItem[] {\n  // Parse action items from common patterns\n  const items: ActionItem[] = [];\n\n  // Pattern: [x] or [ ] followed by @mention and task description\n  const taskPattern = /\\[[ x]\\]\\s*@([a-zA-Z.]+)\\s*[-:]\\s*(.+?)(?=<|$)/gi;\n  let match;\n\n  while ((match = taskPattern.exec(html)) !== null) {\n    items.push({\n      assignee: match[1],\n      description: match[2].trim()\n    });\n  }\n\n  return items;\n}\n```\n\n## Sprint Automation Workflow\n\nAutomate sprint ceremonies and reporting.\n\n### Sprint Planning Assistant\n\n```typescript\ninterface SprintPlanningReport {\n  totalPoints: number;\n  byAssignee: Record<string, number>;\n  byComponent: Record<string, number>;\n  riskyItems: string[];\n  recommendations: string[];\n}\n\nasync function analyzeSprintPlanning(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number\n): Promise<SprintPlanningReport> {\n  // Get sprint issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"assignee\", \"customfield_10001\", \"components\", \"labels\", \"priority\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  const issues = response.issues;\n\n  const report: SprintPlanningReport = {\n    totalPoints: 0,\n    byAssignee: {},\n    byComponent: {},\n    riskyItems: [],\n    recommendations: []\n  };\n\n  for (const issue of issues) {\n    const points = issue.fields.customfield_10001 || 0; // Story points\n    const assignee = issue.fields.assignee?.displayName || \"Unassigned\";\n    const components = issue.fields.components?.map((c: any) => c.name) || [\"No Component\"];\n\n    report.totalPoints += points;\n    report.byAssignee[assignee] = (report.byAssignee[assignee] || 0) + points;\n\n    for (const component of components) {\n      report.byComponent[component] = (report.byComponent[component] || 0) + points;\n    }\n\n    // Identify risks\n    if (points === 0) {\n      report.riskyItems.push(`${issue.key}: No story points estimated`);\n    }\n    if (points >= 8) {\n      report.riskyItems.push(`${issue.key}: Large story (${points} points) - consider breaking down`);\n    }\n    if (assignee === \"Unassigned\") {\n      report.riskyItems.push(`${issue.key}: No assignee`);\n    }\n  }\n\n  // Generate recommendations\n  const avgPointsPerPerson = report.totalPoints / Object.keys(report.byAssignee).length;\n\n  for (const [assignee, points] of Object.entries(report.byAssignee)) {\n    if (points > avgPointsPerPerson * 1.5) {\n      report.recommendations.push(`${assignee} has ${points} points (${Math.round((points / avgPointsPerPerson - 1) * 100)}% above average) - consider rebalancing`);\n    }\n  }\n\n  return report;\n}\n```\n\n### Sprint Retrospective Generator\n\n```typescript\nasync function generateRetroBoard(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get completed sprint data\n  const sprintResult = await client.callTool({\n    name: \"jira_get_sprint_report\",\n    arguments: {\n      board_id: boardId,\n      sprint_id: sprintId\n    }\n  });\n\n  const sprintReport = JSON.parse(sprintResult.content[0].text);\n\n  // Get sprint issues with details\n  const issuesResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"status\", \"resolution\", \"customfield_10001\", \"assignee\"]\n    }\n  });\n\n  const issues = JSON.parse(issuesResult.content[0].text).issues;\n\n  // Calculate metrics\n  const completed = issues.filter((i: any) => i.fields.resolution);\n  const incomplete = issues.filter((i: any) => !i.fields.resolution);\n  const completedPoints = completed.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n  const plannedPoints = issues.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n\n  // Create retrospective page\n  const content = `\n<h2>Sprint Retrospective: ${sprintReport.sprint.name}</h2>\n<p><em>Generated on ${new Date().toISOString().split('T')[0]}</em></p>\n\n<h3>Sprint Metrics</h3>\n<table>\n  <tr><th>Metric</th><th>Value</th></tr>\n  <tr><td>Start Date</td><td>${sprintReport.sprint.startDate}</td></tr>\n  <tr><td>End Date</td><td>${sprintReport.sprint.endDate}</td></tr>\n  <tr><td>Issues Planned</td><td>${issues.length}</td></tr>\n  <tr><td>Issues Completed</td><td>${completed.length}</td></tr>\n  <tr><td>Completion Rate</td><td>${Math.round(completed.length / issues.length * 100)}%</td></tr>\n  <tr><td>Points Planned</td><td>${plannedPoints}</td></tr>\n  <tr><td>Points Completed</td><td>${completedPoints}</td></tr>\n  <tr><td>Velocity</td><td>${completedPoints} points</td></tr>\n</table>\n\n<h3>Completed Items</h3>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS NOT EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee</ac:parameter>\n</ac:structured-macro>\n\n${incomplete.length > 0 ? `\n<h3>Incomplete Items (Spillover)</h3>\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>${incomplete.length} items were not completed and will spill over to the next sprint.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee,status</ac:parameter>\n</ac:structured-macro>\n` : ''}\n\n<h3>Discussion Points</h3>\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What went well?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What could be improved?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Action Items</ac:parameter>\n  <ac:rich-text-body>\n    <ac:task-list>\n      <ac:task><ac:task-body><em>Add action items here...</em></ac:task-body></ac:task>\n    </ac:task-list>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  const newPage = await client.callTool({\n    name: \"confluence_create_page\",\n    arguments: {\n      space_key: confluenceSpaceKey,\n      title: `Sprint Retro - ${sprintReport.sprint.name}`,\n      body: content,\n      labels: [\"sprint-retro\", \"team-meeting\"]\n    }\n  });\n\n  return JSON.parse(newPage.content[0].text).id;\n}\n```\n\n## Issue-Documentation Link Workflow\n\nBidirectional linking between code changes and documentation.\n\n### Link PR to Documentation\n\n```typescript\nasync function linkPRToDocumentation(\n  client: MCPClient,\n  issueKey: string,\n  docPageId: string\n): Promise<void> {\n  // Add remote link to Jira issue\n  await client.callTool({\n    name: \"jira_add_remote_link\",\n    arguments: {\n      issue_key: issueKey,\n      url: `https://your-site.atlassian.net/wiki/spaces/DEV/pages/${docPageId}`,\n      title: \"Related Documentation\",\n      icon_url: \"https://your-site.atlassian.net/wiki/favicon.ico\"\n    }\n  });\n\n  // Add Jira macro to Confluence page\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: docPageId,\n      expand: [\"body.storage\", \"version\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n\n  // Check if link already exists\n  if (page.body.storage.value.includes(issueKey)) {\n    return; // Already linked\n  }\n\n  const jiraLink = `\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Related Issue</ac:parameter>\n  <ac:rich-text-body>\n    <p><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issueKey}</ac:parameter></ac:structured-macro></p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  await client.callTool({\n    name: \"confluence_update_page\",\n    arguments: {\n      page_id: docPageId,\n      title: page.title,\n      body: jiraLink + page.body.storage.value,\n      version_number: page.version.number + 1\n    }\n  });\n}\n```\n\n## Utility Functions\n\n```typescript\nfunction escapeHtml(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#039;');\n}\n\nfunction delay(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function withRetry<T>(\n  operation: () => Promise<T>,\n  maxRetries = 3,\n  baseDelay = 1000\n): Promise<T> {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      if (attempt === maxRetries) throw error;\n\n      const delay = baseDelay * Math.pow(2, attempt - 1);\n      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n## Related References\n\n- `jira-queries.md` - JQL syntax for workflow queries\n- `confluence-operations.md` - Page creation and formatting\n- `authentication-patterns.md` - Secure API access for automated workflows\n","skills/atlassian-mcp/references/confluence-operations.md":"# Confluence Operations\n\n---\n\n## CQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `space = \"DEV\"` |\n| `!=` | Not equal | `type != attachment` |\n| `~` | Contains | `title ~ \"API\"` |\n| `!~` | Does not contain | `text !~ \"deprecated\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `lastModified >= \"2024-01-01\"` |\n| `IN` | Multiple values | `space IN (\"DEV\", \"OPS\")` |\n| `NOT IN` | Exclude | `creator NOT IN (\"bot\")` |\n\n### Field Reference\n\n**Content Fields:**\n```cql\ntype = page                        -- Pages only\ntype = blogpost                    -- Blog posts\ntype = attachment                  -- Attachments\ntype = comment                     -- Comments\nspace = \"DEVDOCS\"                  -- Specific space\nspace.type = global                -- Global spaces\nspace.type = personal              -- Personal spaces\n```\n\n**Search Fields:**\n```cql\ntitle ~ \"architecture\"             -- Title contains\ntext ~ \"kubernetes\"                -- Full text search\ncontent ~ \"deployment\"             -- Content body\nlabel = \"official\"                 -- Has label\nlabel IN (\"api\", \"reference\")      -- Multiple labels\n```\n\n**Date Fields:**\n```cql\ncreated >= \"2024-01-01\"            -- Created after date\nlastModified >= now(\"-30d\")        -- Modified in last 30 days\ncreated >= startOfYear()           -- Created this year\nlastModified >= startOfMonth()     -- Modified this month\n```\n\n**User Fields:**\n```cql\ncreator = currentUser()            -- Created by me\ncontributor = \"john.doe\"           -- Edited by user\nmention = currentUser()            -- Mentions me\nwatcher = currentUser()            -- Pages I watch\nfavourite = currentUser()          -- My favorites\n```\n\n## Essential CQL Patterns\n\n### Documentation Search\n\n```cql\n-- API documentation in dev space\nspace = \"DEV\" AND label = \"api-docs\" AND type = page\n\n-- Recently updated architecture docs\nlabel = \"architecture\" AND lastModified >= now(\"-7d\")\n\n-- Search for code examples\ntext ~ \"```\" AND label = \"tutorial\"\n\n-- Find outdated documentation\nlabel = \"needs-review\" OR lastModified <= now(\"-180d\")\n\n-- Meeting notes from this month\nlabel = \"meeting-notes\" AND created >= startOfMonth()\n```\n\n### Space Management\n\n```cql\n-- All pages in multiple spaces\nspace IN (\"DEV\", \"OPS\", \"PRODUCT\") AND type = page\n\n-- Personal space content\nspace.type = personal AND creator = currentUser()\n\n-- Archived content\nlabel = \"archived\" AND space = \"LEGACY\"\n\n-- Templates\ntype = page AND label = \"template\"\n```\n\n### Content Discovery\n\n```cql\n-- Popular pages (frequently viewed)\ntype = page AND space = \"DOCS\" ORDER BY lastModified DESC\n\n-- Draft pages\nlabel = \"draft\" AND type = page\n\n-- Pages without labels\ntype = page AND space = \"DEV\" AND label IS NULL\n\n-- Orphan pages (no parent)\ntype = page AND ancestor IS NULL AND space = \"DOCS\"\n```\n\n## MCP Tool Calls\n\n### Searching Content\n\n```typescript\n// Basic CQL search\nconst searchResult = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'space = \"DEV\" AND label = \"api-docs\"',\n    limit: 25,\n    expand: [\"body.storage\", \"version\", \"ancestors\"]\n  }\n});\n\n// Parse results\nconst results = JSON.parse(searchResult.content[0].text);\nfor (const page of results.results) {\n  console.log(`${page.title} - ${page._links.webui}`);\n}\n\n// Search with content preview\nconst searchWithContent = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'text ~ \"deployment\" AND space = \"OPS\"',\n    limit: 10,\n    excerpt: true\n  }\n});\n```\n\n### Getting Page Content\n\n```typescript\n// Get page by ID\nconst page = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"body.view\", \"version\", \"ancestors\", \"children.page\"]\n  }\n});\n\n// Get page by space and title\nconst pageByTitle = await client.callTool({\n  name: \"confluence_get_page_by_title\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Reference\"\n  }\n});\n\n// Get page children\nconst children = await client.callTool({\n  name: \"confluence_get_children\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"page\"]\n  }\n});\n```\n\n### Creating Pages\n\n```typescript\n// Create page with storage format (XHTML)\nconst newPage = await client.callTool({\n  name: \"confluence_create_page\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Authentication Guide\",\n    parent_id: \"123456\",  // Optional parent page\n    body: `\n      <h2>Overview</h2>\n      <p>This guide covers authentication methods for our API.</p>\n\n      <h2>OAuth 2.0</h2>\n      <p>We support OAuth 2.0 with the following grant types:</p>\n      <ul>\n        <li>Authorization Code</li>\n        <li>Client Credentials</li>\n      </ul>\n\n      <ac:structured-macro ac:name=\"code\">\n        <ac:parameter ac:name=\"language\">bash</ac:parameter>\n        <ac:plain-text-body><![CDATA[curl -X POST https://api.example.com/oauth/token \\\\\n  -d \"grant_type=client_credentials\" \\\\\n  -d \"client_id=<client-id>\" \\\\\n  -d \"client_secret=<client-secret>\"]]></ac:plain-text-body>\n      </ac:structured-macro>\n\n      <h2>API Tokens</h2>\n      <p>For simple integrations, use API tokens:</p>\n      <ac:structured-macro ac:name=\"info\">\n        <ac:rich-text-body>\n          <p>API tokens are tied to your user account and have the same permissions.</p>\n        </ac:rich-text-body>\n      </ac:structured-macro>\n    `,\n    labels: [\"api-docs\", \"authentication\", \"official\"]\n  }\n});\n\nconsole.log(`Created page: ${newPage.content[0].text}`);\n```\n\n### Updating Pages\n\n```typescript\n// Update page content\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: \"API Authentication Guide (Updated)\",\n    body: \"<h2>Updated Content</h2><p>New documentation here...</p>\",\n    version_number: 5,  // Current version + 1\n    version_message: \"Added OAuth 2.1 section\"\n  }\n});\n\n// Append to existing page\nconst currentPage = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\nconst pageData = JSON.parse(currentPage.content[0].text);\nconst currentBody = pageData.body.storage.value;\nconst newSection = `\n  <h2>New Section</h2>\n  <p>Additional content appended to the page.</p>\n`;\n\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: pageData.title,\n    body: currentBody + newSection,\n    version_number: pageData.version.number + 1\n  }\n});\n```\n\n### Working with Comments\n\n```typescript\n// Add comment to page\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    body: \"<p>This section needs to be updated for v2.0 changes.</p>\"\n  }\n});\n\n// Get page comments\nconst comments = await client.callTool({\n  name: \"confluence_get_comments\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\n// Reply to comment\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    parent_comment_id: \"789012\",\n    body: \"<p>Good catch! I'll update this section.</p>\"\n  }\n});\n```\n\n### Managing Labels\n\n```typescript\n// Add labels to page\nawait client.callTool({\n  name: \"confluence_add_labels\",\n  arguments: {\n    page_id: \"123456\",\n    labels: [\"reviewed\", \"q1-2024\", \"api-v2\"]\n  }\n});\n\n// Remove label\nawait client.callTool({\n  name: \"confluence_remove_label\",\n  arguments: {\n    page_id: \"123456\",\n    label: \"draft\"\n  }\n});\n\n// Get page labels\nconst labels = await client.callTool({\n  name: \"confluence_get_labels\",\n  arguments: {\n    page_id: \"123456\"\n  }\n});\n```\n\n### Space Operations\n\n```typescript\n// Get space information\nconst space = await client.callTool({\n  name: \"confluence_get_space\",\n  arguments: {\n    space_key: \"DEV\",\n    expand: [\"description\", \"homepage\"]\n  }\n});\n\n// List all spaces\nconst spaces = await client.callTool({\n  name: \"confluence_list_spaces\",\n  arguments: {\n    type: \"global\",\n    limit: 100\n  }\n});\n\n// Get space content\nconst spaceContent = await client.callTool({\n  name: \"confluence_get_space_content\",\n  arguments: {\n    space_key: \"DEV\",\n    depth: \"root\",  // or \"all\"\n    expand: [\"children.page\"]\n  }\n});\n```\n\n## Storage Format Reference\n\n### Common Macros\n\n```xml\n<!-- Code block -->\n<ac:structured-macro ac:name=\"code\">\n  <ac:parameter ac:name=\"language\">python</ac:parameter>\n  <ac:parameter ac:name=\"title\">Example</ac:parameter>\n  <ac:plain-text-body><![CDATA[print(\"Hello, World!\")]]></ac:plain-text-body>\n</ac:structured-macro>\n\n<!-- Info panel -->\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Note</ac:parameter>\n  <ac:rich-text-body>\n    <p>Important information here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Warning panel -->\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>Be careful with this operation!</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Table of contents -->\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">3</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issue link -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"key\">PROJ-123</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issues table -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">project = PROJ AND sprint in openSprints()</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n\n<!-- Expand section -->\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Click to expand</ac:parameter>\n  <ac:rich-text-body>\n    <p>Hidden content here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Include page -->\n<ac:structured-macro ac:name=\"include\">\n  <ac:parameter ac:name=\"\"><ri:page ri:content-title=\"Shared Footer\" /></ac:parameter>\n</ac:structured-macro>\n```\n\n### Formatting Elements\n\n```xml\n<!-- Status badge -->\n<ac:structured-macro ac:name=\"status\">\n  <ac:parameter ac:name=\"colour\">Green</ac:parameter>\n  <ac:parameter ac:name=\"title\">APPROVED</ac:parameter>\n</ac:structured-macro>\n\n<!-- User mention -->\n<ac:link><ri:user ri:account-id=\"557058:f3c7...\" /></ac:link>\n\n<!-- Page link -->\n<ac:link><ri:page ri:content-title=\"Target Page\" ri:space-key=\"DEV\" /></ac:link>\n\n<!-- Attachment -->\n<ac:link><ri:attachment ri:filename=\"diagram.png\" /></ac:link>\n\n<!-- Image from attachment -->\n<ac:image><ri:attachment ri:filename=\"screenshot.png\" /></ac:image>\n\n<!-- External image -->\n<ac:image><ri:url ri:value=\"https://example.com/image.png\" /></ac:image>\n```\n\n## Pagination Handling\n\n```typescript\nasync function getAllPages(cql: string): Promise<Page[]> {\n  const allPages: Page[] = [];\n  let start = 0;\n  const limit = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"confluence_search\",\n      arguments: {\n        cql,\n        start,\n        limit,\n        expand: [\"body.storage\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allPages.push(...response.results);\n\n    if (response.results.length < limit || !response._links.next) {\n      break;\n    }\n\n    start += limit;\n  }\n\n  return allPages;\n}\n```\n\n## Error Handling\n\n```typescript\nasync function safeConfluenceCall<T>(operation: () => Promise<T>): Promise<T> {\n  try {\n    return await operation();\n  } catch (error: any) {\n    const status = error.response?.status;\n\n    switch (status) {\n      case 404:\n        throw new Error(`Page or space not found: ${error.message}`);\n      case 403:\n        throw new Error(`Permission denied. Check space permissions.`);\n      case 409:\n        throw new Error(`Version conflict. Page was modified. Refresh and retry.`);\n      case 429:\n        const retryAfter = error.response?.headers?.['retry-after'] || 60;\n        throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);\n      default:\n        throw error;\n    }\n  }\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```cql\n-- Too broad (slow)\ntext ~ \"the\"\n\n-- Missing quotes\nspace = DEV DOCS  -- WRONG\nspace = \"DEV DOCS\"  -- CORRECT\n\n-- Invalid date format\ncreated >= 2024-01-01  -- WRONG\ncreated >= \"2024-01-01\"  -- CORRECT\n```\n\n**Best Practices:**\n- Always specify space when possible for faster queries\n- Use labels for categorization and filtering\n- Combine text search with specific fields\n- Cache frequently accessed page content\n- Handle version conflicts gracefully\n\n## Related References\n\n- `common-workflows.md` - Documentation sync and automation\n- `jira-queries.md` - Linking Confluence pages to Jira issues\n- `authentication-patterns.md` - API access configuration\n","skills/atlassian-mcp/references/jira-queries.md":"# Jira Queries and Operations\n\n---\n\n## JQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `project = \"PROJ\"` |\n| `!=` | Not equal | `status != Done` |\n| `~` | Contains (text search) | `summary ~ \"login bug\"` |\n| `!~` | Does not contain | `description !~ \"test\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `created >= -7d` |\n| `IN` | Multiple values | `status IN (Open, \"In Progress\")` |\n| `NOT IN` | Exclude values | `assignee NOT IN (john, jane)` |\n| `IS` | Null check | `assignee IS EMPTY` |\n| `IS NOT` | Not null | `resolution IS NOT EMPTY` |\n| `WAS` | Historical state | `status WAS \"In Progress\"` |\n| `CHANGED` | Field changed | `status CHANGED FROM Open` |\n\n### Field Reference\n\n**Standard Fields:**\n```jql\nproject = PROJ\nissuetype = Bug\nstatus = \"In Progress\"\npriority = High\nassignee = currentUser()\nreporter = \"john.doe\"\nresolution = Unresolved\nlabels = backend\ncomponent = \"API\"\nfixVersion = \"2.0\"\naffectsVersion = \"1.5\"\n```\n\n**Date Fields:**\n```jql\ncreated >= -30d                    -- Last 30 days\nupdated >= \"2024-01-01\"           -- Since specific date\ndue <= endOfWeek()                 -- Due this week\nresolved >= startOfMonth()         -- Resolved this month\n```\n\n**Text Search:**\n```jql\nsummary ~ \"authentication\"         -- Summary contains\ndescription ~ \"error AND login\"    -- Description search\ntext ~ \"payment failed\"            -- All text fields\ncomment ~ \"blocked\"                -- Comment contains\n```\n\n## Essential JQL Patterns\n\n### Sprint and Backlog Queries\n\n```jql\n-- Current sprint issues\nsprint in openSprints() AND project = PROJ\n\n-- Backlog items\nsprint IS EMPTY AND resolution IS EMPTY AND project = PROJ\n\n-- Sprint completion\nsprint = \"Sprint 23\" AND status = Done\n\n-- Spillover from last sprint\nsprint in closedSprints() AND resolution IS EMPTY\n\n-- Ready for sprint planning\nstatus = \"Ready for Dev\" AND sprint IS EMPTY\n```\n\n### Bug Tracking\n\n```jql\n-- Open bugs by priority\nissuetype = Bug AND resolution IS EMPTY ORDER BY priority DESC\n\n-- Critical production bugs\nissuetype = Bug AND priority IN (Highest, High)\n  AND labels = production AND resolution IS EMPTY\n\n-- Bugs created this week\nissuetype = Bug AND created >= startOfWeek()\n\n-- Bugs without reproduction steps\nissuetype = Bug AND \"Reproduction Steps\" IS EMPTY\n  AND resolution IS EMPTY\n\n-- Regression bugs\nissuetype = Bug AND labels = regression AND fixVersion = \"2.0\"\n```\n\n### Team Workload\n\n```jql\n-- My open issues\nassignee = currentUser() AND resolution IS EMPTY\n\n-- Unassigned high priority\nassignee IS EMPTY AND priority IN (Highest, High)\n  AND resolution IS EMPTY\n\n-- Team member workload\nassignee = \"jane.smith\" AND sprint in openSprints()\n\n-- Blocked issues\nstatus = Blocked OR labels = blocked\n\n-- Stale issues (no update in 14 days)\nupdated <= -14d AND resolution IS EMPTY\n```\n\n### Release Management\n\n```jql\n-- Release candidates\nfixVersion = \"2.0\" AND status = \"Ready for Release\"\n\n-- Missing fix version\nresolution = Done AND fixVersion IS EMPTY AND updated >= -30d\n\n-- Release blockers\nfixVersion = \"2.0\" AND priority = Blocker AND resolution IS EMPTY\n\n-- Changelog items\nfixVersion = \"2.0\" AND resolution = Done ORDER BY issuetype\n```\n\n## MCP Tool Calls\n\n### Searching Issues\n\n```typescript\n// Basic JQL search\nconst searchResult = await client.callTool({\n  name: \"jira_search\",\n  arguments: {\n    jql: \"project = PROJ AND sprint in openSprints()\",\n    max_results: 50,\n    fields: [\"summary\", \"status\", \"assignee\", \"priority\"]\n  }\n});\n\n// Parse response\nconst issues = JSON.parse(searchResult.content[0].text);\nfor (const issue of issues.issues) {\n  console.log(`${issue.key}: ${issue.fields.summary}`);\n}\n```\n\n### Getting Issue Details\n\n```typescript\n// Get single issue with all fields\nconst issue = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    expand: [\"changelog\", \"comments\", \"transitions\"]\n  }\n});\n\n// Get issue with specific fields\nconst issuePartial = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: [\"summary\", \"description\", \"customfield_10001\"]\n  }\n});\n```\n\n### Creating Issues\n\n```typescript\n// Create a bug\nconst newBug = await client.callTool({\n  name: \"jira_create_issue\",\n  arguments: {\n    project_key: \"PROJ\",\n    issue_type: \"Bug\",\n    summary: \"Login fails with SSO enabled\",\n    description: {\n      type: \"doc\",\n      version: 1,\n      content: [\n        {\n          type: \"paragraph\",\n          content: [{ type: \"text\", text: \"Users cannot log in when SSO is enabled.\" }]\n        },\n        {\n          type: \"heading\",\n          attrs: { level: 3 },\n          content: [{ type: \"text\", text: \"Steps to Reproduce\" }]\n        },\n        {\n          type: \"orderedList\",\n          content: [\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Enable SSO in settings\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Log out\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Attempt to log in via SSO\" }] }] }\n          ]\n        }\n      ]\n    },\n    priority: \"High\",\n    labels: [\"sso\", \"authentication\", \"production\"],\n    components: [\"Authentication\"],\n    assignee: \"jane.smith\"\n  }\n});\n\nconsole.log(`Created: ${newBug.content[0].text}`); // PROJ-456\n```\n\n### Updating Issues\n\n```typescript\n// Update issue fields\nawait client.callTool({\n  name: \"jira_update_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: {\n      summary: \"Updated summary\",\n      priority: { name: \"Highest\" },\n      labels: [\"urgent\", \"production\"]\n    }\n  }\n});\n\n// Add comment\nawait client.callTool({\n  name: \"jira_add_comment\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    body: \"Investigating this issue. Initial analysis suggests a race condition.\"\n  }\n});\n\n// Transition issue\nawait client.callTool({\n  name: \"jira_transition_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    transition: \"In Progress\"\n  }\n});\n```\n\n### Sprint Operations\n\n```typescript\n// Get active sprints\nconst sprints = await client.callTool({\n  name: \"jira_get_sprints\",\n  arguments: {\n    board_id: 42,\n    state: \"active\"\n  }\n});\n\n// Move issue to sprint\nawait client.callTool({\n  name: \"jira_move_to_sprint\",\n  arguments: {\n    sprint_id: 123,\n    issue_keys: [\"PROJ-100\", \"PROJ-101\", \"PROJ-102\"]\n  }\n});\n\n// Get sprint report\nconst report = await client.callTool({\n  name: \"jira_get_sprint_report\",\n  arguments: {\n    board_id: 42,\n    sprint_id: 123\n  }\n});\n```\n\n### Issue Linking\n\nUse `jira_create_issue_link` to create dependency relationships between issues.\n\n> **The parameter names are counterintuitive.** The naming reflects Jira's internal \"inward/outward\" link direction, not natural English. Verify every link call against the table below.\n\n#### Parameter Semantics for \"Blocks\" Links\n\n| Parameter | Role | Meaning |\n|-----------|------|---------|\n| `inward_issue_key` | **Blocker** | This issue blocks the other |\n| `outward_issue_key` | **Blocked** | This issue is blocked by the other |\n\n**Memory aid:** `inward_issue_key` = the issue receiving the inward description (\"is blocked by\") — but it is the *blocker*. Think: \"the inward key is where the arrow points FROM.\"\n\n#### Single Blocks Link\n\n```typescript\n// Make AUTH-1 block AUTH-2\n// AUTH-1 will show: \"blocks AUTH-2\"\n// AUTH-2 will show: \"is blocked by AUTH-1\"\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-1\",   // blocker\n    outward_issue_key: \"AUTH-2\"   // blocked\n  }\n});\n```\n\n#### Linking a Dependency Chain\n\nWhen creating a chain A → B → C (A blocks B, B blocks C):\n\n```typescript\nconst chain = [\n  { blocker: \"AUTH-1\", blocked: \"AUTH-2\" },\n  { blocker: \"AUTH-2\", blocked: \"AUTH-3\" },\n  { blocker: \"AUTH-3\", blocked: \"AUTH-4\" }\n];\n\nfor (const dep of chain) {\n  await client.callTool({\n    name: \"jira_create_issue_link\",\n    arguments: {\n      link_type: \"Blocks\",\n      inward_issue_key: dep.blocker,\n      outward_issue_key: dep.blocked\n    }\n  });\n\n  // Respect rate limits between link operations\n  await delay(100);\n}\n```\n\n#### Other Link Types\n\nThe same `inward`/`outward` pattern applies to all link types:\n\n| Link Type | `inward_issue_key` shows | `outward_issue_key` shows |\n|-----------|--------------------------|---------------------------|\n| `Blocks` | \"blocks [outward]\" | \"is blocked by [inward]\" |\n| `Duplicate` | \"duplicates [outward]\" | \"is duplicated by [inward]\" |\n| `Relates` | \"relates to [outward]\" | \"relates to [inward]\" |\n\n```typescript\n// Mark PROJ-10 as a duplicate of PROJ-5\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Duplicate\",\n    inward_issue_key: \"PROJ-10\",   // the duplicate\n    outward_issue_key: \"PROJ-5\"    // the original\n  }\n});\n```\n\n#### Anti-Pattern: Reversed Parameters\n\n```typescript\n// WRONG — This makes AUTH-2 block AUTH-1 (backwards!)\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-2\",   // accidentally made AUTH-2 the blocker\n    outward_issue_key: \"AUTH-1\"   // accidentally made AUTH-1 the blocked\n  }\n});\n```\n\nAlways verify: after creating a link, the blocker (`inward_issue_key`) should display \"blocks [outward]\" in its Jira issue view.\n\n## Pagination Handling\n\n```typescript\nasync function getAllIssues(jql: string): Promise<Issue[]> {\n  const allIssues: Issue[] = [];\n  let startAt = 0;\n  const maxResults = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"jira_search\",\n      arguments: {\n        jql,\n        start_at: startAt,\n        max_results: maxResults,\n        fields: [\"summary\", \"status\", \"assignee\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allIssues.push(...response.issues);\n\n    if (startAt + response.issues.length >= response.total) {\n      break;\n    }\n\n    startAt += maxResults;\n  }\n\n  return allIssues;\n}\n```\n\n## Bulk Operations\n\n```typescript\n// Bulk update with JQL\nasync function bulkUpdateLabels(jql: string, addLabels: string[]) {\n  const issues = await getAllIssues(jql);\n\n  for (const issue of issues) {\n    const existingLabels = issue.fields.labels || [];\n    await client.callTool({\n      name: \"jira_update_issue\",\n      arguments: {\n        issue_key: issue.key,\n        fields: {\n          labels: [...new Set([...existingLabels, ...addLabels])]\n        }\n      }\n    });\n\n    // Respect rate limits\n    await delay(100);\n  }\n}\n\n// Usage\nawait bulkUpdateLabels(\n  'project = PROJ AND sprint in openSprints() AND labels = backend',\n  ['q4-priority', 'needs-review']\n);\n```\n\n## Error Handling\n\n```typescript\nasync function safeJiraCall<T>(\n  operation: () => Promise<T>,\n  retries = 3\n): Promise<T> {\n  for (let attempt = 1; attempt <= retries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      const status = error.response?.status;\n\n      // Don't retry client errors (except rate limits)\n      if (status >= 400 && status < 500 && status !== 429) {\n        throw error;\n      }\n\n      // Rate limited - wait and retry\n      if (status === 429) {\n        const retryAfter = parseInt(error.response?.headers?.['retry-after'] || '60');\n        console.log(`Rate limited. Waiting ${retryAfter}s...`);\n        await delay(retryAfter * 1000);\n        continue;\n      }\n\n      // Server error - exponential backoff\n      if (attempt < retries) {\n        const backoff = Math.pow(2, attempt) * 1000;\n        console.log(`Attempt ${attempt} failed. Retrying in ${backoff}ms...`);\n        await delay(backoff);\n      } else {\n        throw error;\n      }\n    }\n  }\n  throw new Error('Unexpected end of retry loop');\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```jql\n-- Too broad (slow, may timeout)\nproject IS NOT EMPTY\n\n-- Missing quotes for multi-word values\nstatus = In Progress  -- WRONG\nstatus = \"In Progress\"  -- CORRECT\n\n-- Case sensitivity issues\nassignee = John  -- May fail\nassignee = \"john.doe@company.com\"  -- CORRECT\n\n-- Inefficient ordering\nORDER BY created  -- Missing direction\nORDER BY created DESC  -- CORRECT\n```\n\n## Related References\n\n- `common-workflows.md` - End-to-end workflow patterns\n- `authentication-patterns.md` - Credential setup for API calls\n- `confluence-operations.md` - Linking Jira issues to Confluence pages\n","skills/atlassian-mcp/references/mcp-server-setup.md":"# MCP Server Setup\n\n---\n\n## Server Options Overview\n\n### Atlassian Rovo MCP Server (official, recommended)\n\nAtlassian hosts the official MCP server. There is nothing to install: clients connect over Streamable HTTP to\n\n```\nhttps://mcp.atlassian.com/v2/mcp\n```\n\n**Capabilities:**\n- Jira, Confluence, Jira Service Management, Bitbucket Cloud, Compass, Loom, Goals, Projects, Teams, Focus, Talent and the Teamwork Graph\n- OAuth 2.1 browser sign-in, or API tokens for headless clients (admin-enabled)\n- Acts with the signed-in user's existing Atlassian permissions\n- Tools are grouped into permission groups (`read_jira`, `write_jira`, `search_jira`, `read_confluence`, ...) that org admins grant or revoke; `delete_jira` and `manage_jira` are off by default\n- Exposes a small set of primary tools and lets the client discover the rest on demand; append `?tools=all` for a flat list (useful behind MCP gateways)\n- Atlassian Cloud only\n\n**Endpoint versions:**\n\n| Endpoint | Status |\n|----------|--------|\n| `https://mcp.atlassian.com/v2/mcp` | Recommended for new setups |\n| `https://mcp.atlassian.com/v1/mcp`, `/v1/mcp/authv2` | Still supported; existing v1 connections start exposing v2 tools |\n| `https://mcp.atlassian.com/v1/sse` | Legacy SSE endpoint, unsupported after 30 June 2026; move to `/v2/mcp` |\n\nSources: [atlassian/atlassian-mcp-server](https://github.com/atlassian/atlassian-mcp-server), [Getting started with the Atlassian Rovo MCP Server](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/), [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/).\n\n### Community Servers\n\nUse these only when the official server does not fit, mainly for Jira or Confluence **Server/Data Center**.\n\n**mcp-atlassian (sooperset)** - Python, the most complete community option, and the only one here with Server/DC support. Published on PyPI as `mcp-atlassian` and as a Docker image:\n```bash\n# Run without installing (recommended)\nuvx mcp-atlassian --help\n\n# Or install\nuv tool install mcp-atlassian\npip install mcp-atlassian\n\n# Or Docker\ndocker pull ghcr.io/sooperset/mcp-atlassian:latest\n```\n\n**atlassian-mcp (xuanxt)** - TypeScript, Jira and Confluence Cloud only. Published on npm as `@xuandev/atlassian-mcp`:\n```bash\nnpx -y @xuandev/atlassian-mcp\n```\n\n### Comparison Matrix\n\n| Feature | Rovo MCP Server | sooperset | xuanxt |\n|---------|-----------------|-----------|--------|\n| Hosting | Atlassian-hosted | Self-run (stdio, Docker, HTTP) | Self-run |\n| Jira / Confluence Cloud | Yes | Yes | Yes |\n| Jira / Confluence Server/DC | No | Yes | No |\n| Other Atlassian products | JSM, Bitbucket, Compass, Loom, Goals, ... | Jira and Confluence only | Jira and Confluence only |\n| OAuth 2.1 | Yes | Yes (bring your own OAuth app) | No |\n| API token | Yes, if an admin enables it | Yes | Yes |\n| PAT (Server/DC) | No | Yes | No |\n| Admin controls | Permission groups, domain and IP allowlists, audit log | Env vars (`READ_ONLY_MODE`, filters) | Env vars |\n\n## Connecting to the Rovo MCP Server\n\n### Claude Code\n\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nThen run `/mcp` in a session and complete the OAuth sign-in in the browser.\n\n### Claude Desktop\n\nInstall it from **Settings → Extensions → Browse extensions → Plugins**, then search for **Atlassian**.\n\n### Other Clients\n\nAny client that speaks Streamable HTTP takes the server URL directly. Clients that only run local stdio servers can bridge with the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) proxy (Node.js 18+):\n\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-remote\", \"https://mcp.atlassian.com/v2/mcp\"]\n    }\n  }\n}\n```\n\n### Headless: API Token Authentication\n\nFor automations and other non-interactive clients. An org admin must first enable API token authentication (**Atlassian Administration → Rovo → Rovo MCP server → Authentication**). Jira Service Management tools require it.\n\n| Mechanism | Header |\n|-----------|--------|\n| Personal API token | `Authorization: Basic <base64(email:api_token)>` |\n| Service account API key | `Authorization: Bearer <api_key>` |\n\n```bash\n# Build the Basic credential from environment variables; never paste tokens inline\nexport ATLASSIAN_BASIC_AUTH=$(printf '%s:%s' \"$ATLASSIAN_EMAIL\" \"$ATLASSIAN_API_TOKEN\" | base64 -w0)\n\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp \\\n  --header \"Authorization: Basic ${ATLASSIAN_BASIC_AUTH}\"\n```\n\nCreate the token with only the scopes the tools need. See [Configuring authentication via API token](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/configuring-authentication-via-api-token/).\n\n### Admin Prerequisites\n\n- **Domain allowlist:** OAuth connections must come from a client domain allowed under Atlassian Administration → Rovo → Rovo MCP server. Atlassian-supported clients are allowed by default.\n- **IP allowlist:** if the org uses IP allowlisting, tool calls must come from an allowed IP. Hosted AI clients call from the provider's egress IPs, not the user's VPN.\n- **Audit:** every tool call is logged under Insights → Audit log (filter for *Rovo MCP User Actions*).\n\nSee [Control Atlassian Rovo MCP Server settings](https://support.atlassian.com/security-and-access-policies/docs/control-atlassian-rovo-mcp-server-settings/).\n\n## Configuring sooperset (Server/DC or Self-Hosted)\n\n**Cloud with API token:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"CONFLUENCE_URL\": \"https://your-company.atlassian.net/wiki\",\n        \"CONFLUENCE_USERNAME\": \"your-email@company.com\",\n        \"CONFLUENCE_API_TOKEN\": \"<confluence-api-token>\",\n        \"JIRA_URL\": \"https://your-company.atlassian.net\",\n        \"JIRA_USERNAME\": \"your-email@company.com\",\n        \"JIRA_API_TOKEN\": \"<jira-api-token>\"\n      }\n    }\n  }\n}\n```\n\n**Server/Data Center with PAT:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.company.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"<jira-personal-access-token>\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.company.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"<confluence-personal-access-token>\"\n      }\n    }\n  }\n}\n```\n\n### Environment Variables Reference\n\nFrom the project's [`.env.example`](https://github.com/sooperset/mcp-atlassian/blob/main/.env.example).\n\n**Jira:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `JIRA_URL` | Base URL of the Jira instance | Yes |\n| `JIRA_USERNAME` | Account email (Cloud) | Cloud only |\n| `JIRA_API_TOKEN` | API token (Cloud) | Cloud only |\n| `JIRA_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `JIRA_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Confluence:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `CONFLUENCE_URL` | Base URL, with `/wiki` for Cloud | Yes |\n| `CONFLUENCE_USERNAME` | Account email (Cloud) | Cloud only |\n| `CONFLUENCE_API_TOKEN` | API token (Cloud) | Cloud only |\n| `CONFLUENCE_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `CONFLUENCE_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Scope, safety and tuning:**\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `READ_ONLY_MODE` | Disable all create, update and delete tools | false |\n| `JIRA_PROJECTS_FILTER` | Comma-separated Jira project keys to limit operations to | All |\n| `CONFLUENCE_SPACES_FILTER` | Comma-separated Confluence space keys to limit operations to | All |\n| `TOOLSETS` | Tool groups to enable (`default`, `all`, or names such as `jira_agile`) | All |\n| `ENABLED_TOOLS` | Comma-separated tool names to enable | All in active toolsets |\n| `JIRA_TIMEOUT` / `CONFLUENCE_TIMEOUT` | HTTP timeout in seconds | 75 |\n| `ATLASSIAN_RETRY_TOTAL` | Retry attempts for read requests | Off |\n| `ATLASSIAN_REQUESTS_PER_SECOND` | Client-side request rate cap | Off |\n| `MCP_VERBOSE` / `MCP_VERY_VERBOSE` | INFO / DEBUG logging | WARNING |\n\n## Tool Names Differ by Server\n\nEach server names its tools differently. The examples in `jira-queries.md`, `confluence-operations.md` and `common-workflows.md` use sooperset's names (`jira_search`, `jira_get_issue`, ...). With the Rovo MCP Server, list the tools your client sees (or connect with `?tools=all`) and check [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/) before writing tool calls. Its tools take a `cloudId`; get it from `getAccessibleAtlassianResources`, or pin it in your agent instructions to skip that call.\n\n## Verification and Testing\n\n```bash\n# Rovo MCP Server: confirm the connection and auth status\nclaude mcp list\n\n# sooperset: confirm it starts and sees its environment\nuvx mcp-atlassian --help\nenv | grep -E \"^(JIRA|CONFLUENCE)_\" | sed 's/=.*/=***/'\n```\n\n### Test Connection (sooperset)\n\n```typescript\n// test-connection.ts\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nasync function testConnection(): Promise<void> {\n  const transport = new StdioClientTransport({\n    command: \"uvx\",\n    args: [\"mcp-atlassian\"],\n    env: process.env as Record<string, string>,\n  });\n\n  const client = new Client(\n    { name: \"test-client\", version: \"1.0.0\" },\n    { capabilities: {} }\n  );\n\n  await client.connect(transport);\n\n  // List available tools\n  const tools = await client.listTools();\n  console.log(\"Available tools:\", tools.tools.map(t => t.name));\n\n  // Test a simple read operation\n  const result = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: { issue_key: \"TEST-1\" }\n  });\n  console.log(\"Test result:\", result);\n\n  await client.close();\n}\n\ntestConnection().catch(console.error);\n```\n\n## When to Use Each Server\n\n**Choose the Rovo MCP Server when:**\n- Using Atlassian Cloud (the default choice)\n- Need products beyond Jira and Confluence\n- Want OAuth without running your own OAuth app\n- Need admin-managed permissions and audit logging\n\n**Choose sooperset when:**\n- Running Jira or Confluence Server/Data Center\n- Need PAT authentication\n- Must run the server inside your own network\n- Want local controls such as `READ_ONLY_MODE` or project and space filters\n\n**Choose xuanxt when:**\n- Want a small TypeScript server for Cloud that you can fork and extend\n\n## Troubleshooting\n\n**Rovo MCP Server: sign-in fails after moving from v1 to v2:**\nSome clients keep a cached client ID or `.well-known` credentials from v1. Clear them and authenticate again.\n\n**Rovo MCP Server: 403 or \"not allowed\":**\n- The client domain is not on the org's allowlist, or\n- IP allowlisting blocks the caller. Hosted clients call from the provider's IPs, so being on the VPN is not enough. Ask an org admin to review both.\n\n**Rovo MCP Server: a tool is missing:**\nIts permission group is not granted (`delete_jira` and `manage_jira` are off by default), or it needs the other auth method (JSM tools need an API token, Compass tools need OAuth).\n\n**\"Authentication failed\" with sooperset:**\n```bash\n# Verify API token is valid (Cloud)\ncurl -u \"<email>:<api-token>\" \\\n  \"https://your-company.atlassian.net/rest/api/3/myself\"\n\n# Verify PAT is valid (Server/DC)\ncurl -H \"Authorization: Bearer <personal-access-token>\" \\\n  \"https://jira.internal.company.com/rest/api/2/myself\"\n```\n\n**Rate limited (HTTP 429) with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"ATLASSIAN_REQUESTS_PER_SECOND\": \"5\",\n        \"ATLASSIAN_RETRY_TOTAL\": \"5\"\n      }\n    }\n  }\n}\n```\n\n**Debug logging with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"MCP_VERY_VERBOSE\": \"true\"\n      }\n    }\n  }\n}\n```\n\n## Security Best Practices\n\n1. **Prefer OAuth for people, scoped API tokens for automation** - Never share one user's token across a team\n2. **Never commit credentials** - Use environment variables or a secrets manager\n3. **Rotate API tokens regularly** - Set calendar reminders for 90-day rotation\n4. **Grant minimal permission groups** - Leave `delete_jira` and `manage_jira` off unless needed\n5. **Review the audit log** - Rovo MCP tool calls are recorded per user\n6. **Restrict network access** - Use domain and IP allowlists\n\n## Related References\n\n- `authentication-patterns.md` - OAuth 2.1 and API token setup details\n- `jira-queries.md` - JQL syntax after connection is established\n- `confluence-operations.md` - CQL and page operations\n","skills/chaos-engineer/SKILL.md":"---\nname: chaos-engineer\ndescription: Designs chaos experiments, creates failure injection frameworks, and facilitates game day exercises for distributed systems — producing runbooks, experiment manifests, rollback procedures, and post-mortem templates. Use when designing chaos experiments, implementing failure injection frameworks, or conducting game day exercises. Invoke for chaos experiments, resilience testing, blast radius control, game days, antifragile systems, fault injection, Chaos Monkey, Litmus Chaos.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: devops\n  triggers: chaos engineering, resilience testing, failure injection, game day, blast radius, chaos experiment, fault injection, Chaos Monkey, Litmus Chaos, antifragile\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: sre-engineer, devops-engineer, kubernetes-specialist\n---\n\n# Chaos Engineer\n\n## When to Use This Skill\n\n- Designing and executing chaos experiments\n- Implementing failure injection frameworks (Chaos Monkey, Litmus, etc.)\n- Planning and conducting game day exercises\n- Building blast radius controls and safety mechanisms\n- Setting up continuous chaos testing in CI/CD\n- Improving system resilience based on experiment findings\n\n## Core Workflow\n\n1. **System Analysis** - Map architecture, dependencies, critical paths, and failure modes\n2. **Experiment Design** - Define hypothesis, steady state, blast radius, and safety controls\n3. **Execute Chaos** - Run controlled experiments with monitoring and quick rollback\n4. **Learn & Improve** - Document findings, implement fixes, enhance monitoring\n5. **Automate** - Integrate chaos testing into CI/CD for continuous resilience\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Experiments | `references/experiment-design.md` | Designing hypothesis, blast radius, rollback |\n| Infrastructure | `references/infrastructure-chaos.md` | Server, network, zone, region failures |\n| Kubernetes | `references/kubernetes-chaos.md` | Pod, node, Litmus, chaos mesh experiments |\n| Tools & Automation | `references/chaos-tools.md` | Chaos Monkey, Gremlin, Pumba, CI/CD integration |\n| Game Days | `references/game-days.md` | Planning, executing, learning from game days |\n\n## Safety Checklist\n\nNon-obvious constraints that must be enforced on every experiment:\n\n- **Steady state first** — define and verify baseline metrics before injecting any failure\n- **Blast radius cap** — start with the smallest possible impact scope; expand only after validation\n- **Automated rollback ≤ 30 seconds** — abort path must be scripted and tested before the experiment begins\n- **Single variable** — change only one failure condition at a time until behaviour is well understood\n- **No production without safety nets** — customer-facing environments require circuit breakers, feature flags, or canary isolation\n- **Close the loop** — every experiment must produce a written learning summary and at least one tracked improvement\n\n## Output Templates\n\nWhen implementing chaos engineering, provide:\n1. Experiment design document (hypothesis, metrics, blast radius)\n2. Implementation code (failure injection scripts/manifests)\n3. Monitoring setup and alert configuration\n4. Rollback procedures and safety controls\n5. Learning summary and improvement recommendations\n\n## Concrete Example: Pod Failure Experiment (Litmus Chaos)\n\nThe following shows a complete experiment — from hypothesis to rollback — using Litmus Chaos on Kubernetes.\n\n### Step 1 — Define steady state and apply the experiment\n\n```bash\n# Verify baseline: p99 latency < 200ms, error rate < 0.1%\nkubectl get deploy my-service -n production\nkubectl top pods -n production -l app=my-service\n```\n\n### Step 2 — Create and apply a Litmus ChaosEngine manifest\n\n```yaml\n# chaos-pod-delete.yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: my-service-pod-delete\n  namespace: production\nspec:\n  appinfo:\n    appns: production\n    applabel: \"app=my-service\"\n    appkind: deployment\n  # Limit blast radius: only 1 replica at a time\n  engineState: active\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: \"60\"          # seconds\n            - name: CHAOS_INTERVAL\n              value: \"20\"          # delete one pod every 20s\n            - name: FORCE\n              value: \"false\"\n            - name: PODS_AFFECTED_PERC\n              value: \"33\"          # max 33% of replicas affected\n```\n\n```bash\n# Apply the experiment\nkubectl apply -f chaos-pod-delete.yaml\n\n# Watch experiment status\nkubectl describe chaosengine my-service-pod-delete -n production\nkubectl get chaosresult my-service-pod-delete-pod-delete -n production -w\n```\n\n### Step 3 — Monitor during the experiment\n\n```bash\n# Tail application logs for errors\nkubectl logs -l app=my-service -n production --since=2m -f\n\n# Check ChaosResult verdict when complete\nkubectl get chaosresult my-service-pod-delete-pod-delete \\\n  -n production -o jsonpath='{.status.experimentStatus.verdict}'\n```\n\n### Step 4 — Rollback / abort if steady state is violated\n\n```bash\n# Immediately stop the experiment\nkubectl patch chaosengine my-service-pod-delete \\\n  -n production --type merge -p '{\"spec\":{\"engineState\":\"stop\"}}'\n\n# Confirm all pods are healthy\nkubectl rollout status deployment/my-service -n production\n```\n\n## Concrete Example: Network Latency with toxiproxy\n\n```bash\n# Install toxiproxy CLI\nbrew install toxiproxy   # macOS; use the binary release on Linux\n\n# Start toxiproxy server (runs alongside your service)\ntoxiproxy-server &\n\n# Create a proxy for your downstream dependency\ntoxiproxy-cli create -l 0.0.0.0:22222 -u downstream-db:5432 db-proxy\n\n# Inject 300ms latency with 10% jitter — blast radius: this proxy only\ntoxiproxy-cli toxic add db-proxy -t latency -a latency=300 -a jitter=30\n\n# Run your load test / observe metrics here ...\n\n# Remove the toxic to restore normal behaviour\ntoxiproxy-cli toxic remove db-proxy -n latency_downstream\n```\n\n## Concrete Example: Chaos Monkey (Spinnaker / standalone)\n\n```bash\n# chaos-monkey-config.yml — restrict to a single ASG\ndeployment:\n  enabled: true\n  regionIndependence: false\nchaos:\n  enabled: true\n  meanTimeBetweenKillsInWorkDays: 2\n  minTimeBetweenKillsInWorkDays: 1\n  grouping: APP           # kill one instance per app, not per cluster\n  exceptions:\n    - account: production\n      region: us-east-1\n      detail: \"*-canary\"  # never kill canary instances\n\n# Apply and trigger a manual kill for testing\nchaos-monkey --app my-service --account staging --dry-run false\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/devops/chaos-engineer/)\n","skills/chaos-engineer/references/chaos-tools.md":"# Chaos Engineering Tools & Automation\n\n## Chaos Monkey (Netflix)\n\n```python\n# Chaos Monkey configuration for Spinnaker\n{\n  \"enabled\": true,\n  \"schedule\": {\n    \"enabled\": true,\n    \"frequency\": 1,  # Run once per day\n    \"frequencyUnit\": \"DAYS\",\n    \"start\": \"09:00\",\n    \"end\": \"15:00\",\n    \"timezone\": \"America/Los_Angeles\"\n  },\n  \"grouping\": \"cluster\",\n  \"regionsAreIndependent\": true,\n  \"exceptions\": [\n    {\n      \"type\": \"Opt-In\",\n      \"account\": \"production\",\n      \"stack\": \"*\",\n      \"detail\": \"*\"\n    }\n  ],\n  \"minTimeBetweenKillsInWorkDays\": 2,\n  \"maxAppsPerDay\": 5,\n  \"clusters\": [\n    {\n      \"app\": \"myapp\",\n      \"stack\": \"production\",\n      \"enabled\": true,\n      \"regions\": [\"us-east-1\", \"us-west-2\"],\n      \"meanTimeBetweenKillsInWorkDays\": 2,\n      \"minTimeBetweenKillsInWorkDays\": 1,\n      \"maxTerminationsPerDay\": 1\n    }\n  ]\n}\n```\n\n```bash\n#!/bin/bash\n# Simpl Chaos Monkey implementation\n\nINSTANCE_COUNT=5\nKILL_PERCENTAGE=20\n\n# Get running instances from ASG\nINSTANCES=$(aws autoscaling describe-auto-scaling-groups \\\n  --auto-scaling-group-names my-asg \\\n  --query 'AutoScalingGroups[0].Instances[?LifecycleState==`InService`].InstanceId' \\\n  --output text)\n\n# Calculate number to terminate\nTOTAL=$(echo \"$INSTANCES\" | wc -w)\nTO_KILL=$(( TOTAL * KILL_PERCENTAGE / 100 ))\n\nif [ $TO_KILL -eq 0 ]; then\n  TO_KILL=1\nfi\n\n# Randomly select and terminate instances\necho \"$INSTANCES\" | tr ' ' '\\n' | shuf | head -n $TO_KILL | while read instance; do\n  echo \"Terminating instance: $instance\"\n  aws ec2 terminate-instances --instance-ids \"$instance\"\n  sleep 30  # Wait between terminations\ndone\n```\n\n## Gremlin Integration\n\n```python\nimport requests\nfrom typing import Literal\n\nclass GremlinClient:\n    def __init__(self, api_key: str, team_id: str):\n        self.api_key = api_key\n        self.team_id = team_id\n        self.base_url = \"https://api.gremlin.com/v1\"\n        self.headers = {\n            \"Authorization\": f\"Key {api_key}\",\n            \"Content-Type\": \"application/json\"\n        }\n\n    def create_cpu_attack(\n        self,\n        targets: list[str],\n        length: int = 60,\n        cores: int = 1,\n        percent: int = 50\n    ):\n        \"\"\"Launch CPU resource attack.\"\"\"\n        payload = {\n            \"command\": {\n                \"type\": \"cpu\",\n                \"args\": [\n                    \"-l\", str(length),\n                    \"-c\", str(cores),\n                    \"-p\", str(percent)\n                ]\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def create_network_attack(\n        self,\n        targets: list[str],\n        attack_type: Literal[\"latency\", \"packet_loss\", \"blackhole\"],\n        length: int = 60,\n        **kwargs\n    ):\n        \"\"\"Launch network attack.\"\"\"\n        args = [\"-l\", str(length)]\n\n        if attack_type == \"latency\":\n            # kwargs: delay_ms, jitter_ms\n            args.extend([\"-m\", str(kwargs.get('delay_ms', 100))])\n            if 'jitter_ms' in kwargs:\n                args.extend([\"-j\", str(kwargs['jitter_ms'])])\n\n        elif attack_type == \"packet_loss\":\n            # kwargs: percent\n            args.extend([\"-p\", str(kwargs.get('percent', 10))])\n\n        elif attack_type == \"blackhole\":\n            # kwargs: port, protocol\n            if 'port' in kwargs:\n                args.extend([\"--port\", str(kwargs['port'])])\n            if 'protocol' in kwargs:\n                args.extend([\"--protocol\", kwargs['protocol']])\n\n        payload = {\n            \"command\": {\n                \"type\": attack_type,\n                \"args\": args\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def halt_attack(self, attack_id: str):\n        \"\"\"Stop running attack.\"\"\"\n        response = requests.delete(\n            f\"{self.base_url}/attacks/{attack_id}\",\n            headers=self.headers\n        )\n        return response.status_code == 200\n\n    def create_scenario(self, name: str, attacks: list[dict]):\n        \"\"\"Create reusable attack scenario.\"\"\"\n        payload = {\n            \"name\": name,\n            \"description\": f\"Chaos scenario: {name}\",\n            \"graph\": {\n                \"nodes\": attacks\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/scenarios\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n# Example usage\ngremlin = GremlinClient(api_key=\"<gremlin-api-key>\", team_id=\"<gremlin-team-id>\")\n\n# CPU attack on specific containers\ngremlin.create_cpu_attack(\n    targets=[\"container-id-123\", \"container-id-456\"],\n    length=300,  # 5 minutes\n    cores=2,\n    percent=80\n)\n\n# Network latency attack\ngremlin.create_network_attack(\n    targets=[\"host-abc\"],\n    attack_type=\"latency\",\n    length=180,\n    delay_ms=500,\n    jitter_ms=100\n)\n```\n\n## CI/CD Integration\n\n```yaml\n# GitHub Actions workflow for chaos testing\nname: Chaos Engineering Tests\n\non:\n  schedule:\n    - cron: '0 10 * * 1-5'  # Weekdays at 10 AM\n  workflow_dispatch:  # Manual trigger\n\njobs:\n  chaos-tests:\n    runs-on: ubuntu-latest\n    environment: staging\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v3\n\n      - name: Setup kubectl\n        uses: azure/setup-kubectl@v3\n\n      - name: Configure AWS credentials\n        uses: aws-actions/configure-aws-credentials@v2\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: us-east-1\n\n      - name: Update kubeconfig\n        run: |\n          aws eks update-kubeconfig --name staging-cluster --region us-east-1\n\n      - name: Install Litmus\n        run: |\n          kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v2.14.0.yaml\n          kubectl wait --for=condition=Ready pods -l app.kubernetes.io/component=operator --timeout=300s\n\n      - name: Run pod-delete chaos experiment\n        run: |\n          kubectl apply -f .github/chaos/pod-delete-experiment.yaml\n          kubectl wait --for=condition=Complete chaosengine/pod-delete-chaos --timeout=600s\n\n      - name: Verify system recovery\n        run: |\n          # Check all pods are running\n          kubectl wait --for=condition=Ready pods -l app=myapp --timeout=300s\n\n          # Verify no error rate spike\n          ERROR_RATE=$(curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\"5..\\\"}[5m])\" | jq -r '.data.result[0].value[1]')\n\n          if (( $(echo \"$ERROR_RATE > 0.01\" | bc -l) )); then\n            echo \"Error rate too high: $ERROR_RATE\"\n            exit 1\n          fi\n\n      - name: Cleanup chaos resources\n        if: always()\n        run: |\n          kubectl delete chaosengine --all\n          kubectl delete chaosexperiments --all\n\n      - name: Report results to Slack\n        if: failure()\n        uses: slackapi/slack-github-action@v1\n        with:\n          payload: |\n            {\n              \"text\": \"Chaos test failed in staging\",\n              \"blocks\": [\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"Chaos engineering test failed. System did not recover properly.\"\n                  }\n                }\n              ]\n            }\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}\n```\n\n## Jenkins Pipeline\n\n```groovy\n// Jenkinsfile for chaos testing\npipeline {\n    agent any\n\n    parameters {\n        choice(\n            name: 'ENVIRONMENT',\n            choices: ['dev', 'staging'],\n            description: 'Target environment'\n        )\n        choice(\n            name: 'CHAOS_TYPE',\n            choices: ['pod-delete', 'network-latency', 'cpu-stress'],\n            description: 'Type of chaos experiment'\n        )\n        string(\n            name: 'DURATION',\n            defaultValue: '300',\n            description: 'Chaos duration in seconds'\n        )\n    }\n\n    stages {\n        stage('Pre-flight Check') {\n            steps {\n                script {\n                    // Verify steady state before chaos\n                    def errorRate = sh(\n                        script: '''\n                            curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\\\"5..\\\\\"}[5m])\" | jq -r '.data.result[0].value[1]'\n                        ''',\n                        returnStdout: true\n                    ).trim()\n\n                    if (errorRate.toFloat() > 0.01) {\n                        error(\"System not in steady state. Error rate: ${errorRate}\")\n                    }\n                }\n            }\n        }\n\n        stage('Run Chaos Experiment') {\n            steps {\n                script {\n                    def chaosManifest = \"\"\"\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: jenkins-chaos-${env.BUILD_NUMBER}\n  namespace: ${params.ENVIRONMENT}\nspec:\n  appinfo:\n    appns: '${params.ENVIRONMENT}'\n    applabel: 'app=myapp'\n    appkind: 'deployment'\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: ${params.CHAOS_TYPE}\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '${params.DURATION}'\n\"\"\"\n\n                    writeFile file: 'chaos-manifest.yaml', text: chaosManifest\n\n                    sh '''\n                        kubectl apply -f chaos-manifest.yaml\n                        kubectl wait --for=condition=Complete chaosengine/jenkins-chaos-${BUILD_NUMBER} --timeout=900s\n                    '''\n                }\n            }\n        }\n\n        stage('Verify Recovery') {\n            steps {\n                sh '''\n                    # Wait for system to stabilize\n                    sleep 60\n\n                    # Check pod status\n                    kubectl get pods -n ${ENVIRONMENT} -l app=myapp\n\n                    # Verify all pods running\n                    READY_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '[.items[] | select(.status.phase==\"Running\")] | length')\n                    TOTAL_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '.items | length')\n\n                    if [ \"$READY_PODS\" -ne \"$TOTAL_PODS\" ]; then\n                        echo \"Not all pods recovered: $READY_PODS/$TOTAL_PODS ready\"\n                        exit 1\n                    fi\n                '''\n            }\n        }\n\n        stage('Extract Learnings') {\n            steps {\n                script {\n                    // Get chaos result\n                    def chaosResult = sh(\n                        script: \"kubectl get chaosresult -n ${params.ENVIRONMENT} -o json\",\n                        returnStdout: true\n                    )\n\n                    // Parse and store results\n                    writeFile file: \"chaos-result-${env.BUILD_NUMBER}.json\", text: chaosResult\n\n                    // Archive results\n                    archiveArtifacts artifacts: \"chaos-result-${env.BUILD_NUMBER}.json\"\n                }\n            }\n        }\n    }\n\n    post {\n        always {\n            // Cleanup\n            sh '''\n                kubectl delete chaosengine jenkins-chaos-${BUILD_NUMBER} -n ${ENVIRONMENT} || true\n            '''\n        }\n\n        failure {\n            // Notify team\n            slackSend(\n                color: 'danger',\n                message: \"Chaos test failed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}\"\n            )\n        }\n\n        success {\n            slackSend(\n                color: 'good',\n                message: \"Chaos test passed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}. System recovered successfully.\"\n            )\n        }\n    }\n}\n```\n\n## Continuous Chaos Dashboard\n\n```python\n# Flask app for chaos monitoring dashboard\nfrom flask import Flask, render_template, jsonify\nimport requests\nfrom datetime import datetime, timedelta\n\napp = Flask(__name__)\n\nclass ChaosDashboard:\n    def __init__(self, prometheus_url: str):\n        self.prometheus = prometheus_url\n\n    def get_experiment_metrics(self, hours: int = 24):\n        \"\"\"Get chaos experiment results from last N hours.\"\"\"\n        end = datetime.now()\n        start = end - timedelta(hours=hours)\n\n        query = f'''\n            sum by (experiment, verdict) (\n                increase(litmuschaos_experiment_verdict[{hours}h])\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n    def get_mttr_trend(self):\n        \"\"\"Get MTTR trend over time.\"\"\"\n        query = '''\n            avg_over_time(\n                avg(\n                    time() - timestamp(\n                        kube_pod_status_phase{phase=\"Running\"} == 1\n                    )\n                )[7d:]\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n@app.route('/api/chaos-summary')\ndef chaos_summary():\n    dashboard = ChaosDashboard(prometheus_url=\"http://prometheus:9090\")\n\n    return jsonify({\n        \"experiments\": dashboard.get_experiment_metrics(hours=24),\n        \"mttr_trend\": dashboard.get_mttr_trend(),\n        \"timestamp\": datetime.now().isoformat()\n    })\n\nif __name__ == '__main__':\n    app.run(host='0.0.0.0', port=5000)\n```\n\n## Quick Reference\n\n| Tool | Use Case | Integration |\n|------|----------|-------------|\n| Chaos Monkey | Random instance termination | Spinnaker/AWS ASG |\n| Gremlin | SaaS chaos platform | API/Web UI |\n| Litmus | Kubernetes chaos | Kubectl/Helm |\n| Chaos Mesh | Advanced K8s chaos | CRDs/Dashboard |\n| Toxiproxy | Network proxy chaos | Docker/API |\n| Pumba | Container chaos | Docker CLI |\n","skills/chaos-engineer/references/experiment-design.md":"# Chaos Experiment Design\n\n## Experiment Template\n\n```yaml\nname: \"Database Connection Pool Exhaustion\"\nhypothesis: \"When the database connection pool is exhausted, the application will gracefully degrade and return 503 errors without cascading failures\"\n\nsteady_state:\n  metrics:\n    - name: \"Error Rate\"\n      threshold: \"< 0.1%\"\n      source: \"prometheus\"\n      query: \"rate(http_requests_total{status=~'5..'}[5m])\"\n    - name: \"Latency P99\"\n      threshold: \"< 500ms\"\n      source: \"datadog\"\n    - name: \"Active Connections\"\n      threshold: \"> 10\"\n      query: \"pg_stat_activity_count\"\n\nblast_radius:\n  environment: \"staging\"\n  traffic_percentage: 10\n  duration_seconds: 300\n  max_error_rate: \"5%\"\n  auto_rollback: true\n\ninjection:\n  type: \"resource_exhaustion\"\n  target: \"database_connections\"\n  method: \"connection_leak\"\n  parameters:\n    leak_rate: 5  # connections per second\n    max_leaked: 50\n\nsafety:\n  rollback_triggers:\n    - \"error_rate > 5%\"\n    - \"manual_kill_switch\"\n    - \"duration_exceeded\"\n  rollback_time_limit_seconds: 30\n  alerts:\n    - slack: \"#chaos-engineering\"\n    - pagerduty: \"chaos-team\"\n\nsuccess_criteria:\n  - \"Circuit breakers activate within 10s\"\n  - \"503 errors returned (not 500)\"\n  - \"No cascading failures to other services\"\n  - \"System recovers within 60s of rollback\"\n```\n\n## Hypothesis Formulation\n\n```python\ndef create_hypothesis(component: str, failure: str, expected_behavior: str) -> dict:\n    \"\"\"\n    Create well-formed chaos hypothesis.\n\n    Format: \"Given [normal state], when [failure occurs],\n             then [expected behavior], measured by [metrics]\"\n    \"\"\"\n    return {\n        \"given\": f\"System is in steady state with {component} functioning normally\",\n        \"when\": f\"{failure} occurs\",\n        \"then\": expected_behavior,\n        \"measured_by\": [\n            \"Error rate remains below threshold\",\n            \"Latency stays within SLO\",\n            \"No data loss or corruption\",\n            \"Recovery time within RTO\"\n        ]\n    }\n\n# Example\nhypothesis = create_hypothesis(\n    component=\"payment service\",\n    failure=\"50% packet loss to payment gateway\",\n    expected_behavior=\"Requests timeout gracefully, retry queue activates, \"\n                     \"users see clear error messages\"\n)\n```\n\n## Blast Radius Control\n\n```python\nfrom dataclasses import dataclass\nfrom enum import Enum\n\nclass BlastRadiusLevel(Enum):\n    MINIMAL = \"single_instance_dev\"\n    LOW = \"single_instance_staging\"\n    MEDIUM = \"percentage_staging\"\n    HIGH = \"percentage_production\"\n    CRITICAL = \"full_production\"\n\n@dataclass\nclass BlastRadiusConfig:\n    level: BlastRadiusLevel\n    environment: str\n    target_percentage: float  # 0-100\n    canary_users: list[str]\n    feature_flag: str\n    auto_rollback: bool\n    max_duration_seconds: int\n\n    def validate(self):\n        \"\"\"Enforce safety rules.\"\"\"\n        if self.level == BlastRadiusLevel.CRITICAL:\n            raise ValueError(\"CRITICAL blast radius requires explicit approval\")\n\n        if self.environment == \"production\" and self.target_percentage > 10:\n            if not self.feature_flag or not self.auto_rollback:\n                raise ValueError(\"Production >10% requires feature flag AND auto-rollback\")\n\n        if self.max_duration_seconds > 600:\n            raise ValueError(\"Max duration cannot exceed 10 minutes without approval\")\n\n# Progressive blast radius expansion\ndef progressive_rollout() -> list[BlastRadiusConfig]:\n    return [\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MINIMAL,\n            environment=\"dev\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_dev\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.LOW,\n            environment=\"staging\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_staging\",\n            auto_rollback=True,\n            max_duration_seconds=600\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MEDIUM,\n            environment=\"production\",\n            target_percentage=1,\n            canary_users=[\"internal_team\"],\n            feature_flag=\"chaos_prod_canary\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        )\n    ]\n```\n\n## Safety Mechanisms\n\n```python\nimport asyncio\nfrom typing import Callable\n\nclass ChaosExperimentSafety:\n    def __init__(self, config: dict):\n        self.config = config\n        self.kill_switch_active = False\n        self.metrics = {}\n\n    async def run_with_safety(self, chaos_fn: Callable):\n        \"\"\"Execute chaos with automatic safety checks.\"\"\"\n        # Pre-flight checks\n        if not await self.verify_steady_state():\n            raise Exception(\"System not in steady state - aborting\")\n\n        # Set up rollback trigger\n        rollback_task = asyncio.create_task(self.monitor_for_rollback())\n        chaos_task = asyncio.create_task(chaos_fn())\n\n        try:\n            # Wait for either chaos completion or rollback trigger\n            done, pending = await asyncio.wait(\n                [chaos_task, rollback_task],\n                return_when=asyncio.FIRST_COMPLETED\n            )\n\n            if rollback_task in done:\n                # Rollback triggered - cancel chaos\n                chaos_task.cancel()\n                await self.rollback()\n\n        finally:\n            await self.ensure_system_recovery()\n\n    async def verify_steady_state(self) -> bool:\n        \"\"\"Check all steady state metrics are within threshold.\"\"\"\n        for metric in self.config['steady_state']['metrics']:\n            value = await self.query_metric(metric['query'])\n            if not self.within_threshold(value, metric['threshold']):\n                return False\n        return True\n\n    async def monitor_for_rollback(self):\n        \"\"\"Continuously monitor for rollback triggers.\"\"\"\n        start_time = asyncio.get_event_loop().time()\n\n        while True:\n            # Check duration limit\n            if asyncio.get_event_loop().time() - start_time > \\\n               self.config['blast_radius']['duration_seconds']:\n                return \"duration_exceeded\"\n\n            # Check manual kill switch\n            if self.kill_switch_active:\n                return \"manual_kill_switch\"\n\n            # Check error rate\n            error_rate = await self.query_metric(\"error_rate\")\n            if error_rate > float(self.config['blast_radius']['max_error_rate'].strip('%')):\n                return \"error_rate_exceeded\"\n\n            await asyncio.sleep(5)  # Check every 5 seconds\n```\n\n## Quick Reference\n\n| Phase | Key Actions | Time Limit |\n|-------|-------------|------------|\n| Design | Hypothesis, metrics, blast radius | 1 hour |\n| Review | Team review, safety check | 30 min |\n| Prepare | Setup monitoring, rollback | 1 hour |\n| Execute | Run experiment, monitor | 5-10 min |\n| Rollback | Restore steady state | < 30 sec |\n| Learn | Document findings, plan fixes | 2 hours |\n","skills/chaos-engineer/references/game-days.md":"# Game Day Planning & Execution\n\n## Game Day Planning Template\n\n```yaml\ngame_day:\n  name: \"Database Failover Drill\"\n  date: \"2025-01-15\"\n  time: \"10:00-12:00 PST\"\n  environment: \"staging\"  # Start in staging\n\n  objectives:\n    - \"Verify RDS failover to standby in under 2 minutes\"\n    - \"Validate application auto-reconnect logic\"\n    - \"Test monitoring and alerting effectiveness\"\n    - \"Practice incident response procedures\"\n\n  participants:\n    facilitator: \"chaos-engineer@company.com\"\n    observers:\n      - \"sre-team@company.com\"\n      - \"dev-team@company.com\"\n    responders:\n      - \"on-call-engineer@company.com\"\n      - \"database-admin@company.com\"\n    stakeholders:\n      - \"engineering-manager@company.com\"\n\n  scenarios:\n    - name: \"Primary database instance failure\"\n      duration_minutes: 30\n      steps:\n        - action: \"Force RDS instance reboot with failover\"\n          expected: \"Failover to standby in <2 min\"\n          success_criteria:\n            - \"Downtime < 2 minutes\"\n            - \"No data loss\"\n            - \"Alerts fired correctly\"\n\n    - name: \"Network partition to database\"\n      duration_minutes: 20\n      steps:\n        - action: \"Block network traffic to RDS security group\"\n          expected: \"Application switches to read replica\"\n          success_criteria:\n            - \"Read-only mode activated\"\n            - \"User-facing error messages clear\"\n\n  communication_plan:\n    announcement_channel: \"#game-day-announcements\"\n    war_room: \"Zoom link: https://...\"\n    status_updates_every: \"5 minutes\"\n    escalation_contacts:\n      - name: \"VP Engineering\"\n        phone: \"+1-555-0100\"\n        threshold: \"downtime > 5 minutes\"\n\n  rollback_plan:\n    automatic_rollback_triggers:\n      - \"production traffic affected\"\n      - \"customer complaints received\"\n      - \"error_rate > 10%\"\n    manual_rollback_command: \"aws rds reboot-db-instance --db-instance-identifier primary --force-failover\"\n    rollback_time_limit_seconds: 60\n\n  success_metrics:\n    - metric: \"RTO (Recovery Time Objective)\"\n      target: \"< 2 minutes\"\n      measurement: \"time between failure and full recovery\"\n    - metric: \"Alert accuracy\"\n      target: \"100%\"\n      measurement: \"all expected alerts fired\"\n    - metric: \"Team response time\"\n      target: \"< 5 minutes\"\n      measurement: \"time to acknowledge incident\"\n\n  post_mortem:\n    scheduled_for: \"2025-01-16 14:00\"\n    template: \"game-day-retro.md\"\n    required_attendees: \"all participants\"\n```\n\n## Game Day Runbook\n\n```markdown\n# Database Failover Game Day Runbook\n\n**Date**: January 15, 2025\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Pre-Game Checklist (T-30 min)\n\n- [ ] Verify all participants joined war room\n- [ ] Confirm monitoring dashboards accessible\n- [ ] Test rollback procedures work\n- [ ] Announce game day start in #engineering\n- [ ] Verify staging environment healthy\n- [ ] Set up screen recording for timeline\n- [ ] Prepare incident timeline spreadsheet\n\n## Timeline\n\n### 10:00 - Introduction (10 min)\n- Facilitator explains objectives\n- Review scenarios and success criteria\n- Confirm roles and communication channels\n- Remind everyone: this is a learning exercise\n\n### 10:10 - Scenario 1: Primary DB Failure (30 min)\n\n**T+0 (10:10)** - Inject failure\n```bash\naws rds reboot-db-instance \\\n  --db-instance-identifier staging-primary \\\n  --force-failover\n```\n\n**Expected Timeline**:\n- T+0: Reboot initiated\n- T+30s: Primary becomes unavailable\n- T+60s: DNS updated to standby\n- T+90s: Application reconnects\n- T+120s: Full recovery\n\n**Observer Tasks**:\n- [ ] Record exact time of failure injection\n- [ ] Monitor application error logs\n- [ ] Track alert notifications\n- [ ] Document team response actions\n- [ ] Screenshot dashboard states\n\n**Questions to Answer**:\n- How long until first alert?\n- Did application auto-reconnect?\n- Were customers impacted?\n- What manual interventions needed?\n\n### 10:40 - Debrief Scenario 1 (10 min)\n- What went well?\n- What could improve?\n- Any surprises?\n- Action items identified\n\n### 10:50 - Scenario 2: Network Partition (20 min)\n\n**T+0 (10:50)** - Inject failure\n```bash\n# Block database security group ingress\naws ec2 revoke-security-group-ingress \\\n  --group-id sg-xxxxx \\\n  --protocol tcp \\\n  --port 5432 \\\n  --cidr 10.0.0.0/16\n```\n\n**Expected Behavior**:\n- Connection timeouts occur\n- Circuit breaker opens\n- Read-only mode activates\n- Clear error messages shown\n\n**Observer Tasks**:\n- [ ] Monitor circuit breaker state\n- [ ] Verify read-replica failover\n- [ ] Check user-facing error messages\n- [ ] Track degraded service duration\n\n### 11:10 - Debrief Scenario 2 (10 min)\n\n### 11:20 - Scenario 3: Surprise! (20 min)\n\n**Facilitator Note**: Don't announce this scenario details beforehand.\nTest true incident response capability.\n\n**Hidden Scenario**: Combination failure\n1. Database connection pool leak\n2. Simultaneous cache invalidation\n\n```python\n# Connection leak simulator\nimport psycopg2\nconnections = []\nfor i in range(100):\n    conn = psycopg2.connect(DATABASE_URL)\n    connections.append(conn)\n    # Intentionally don't close\n```\n\n**Observer Tasks**:\n- [ ] How long to identify root cause?\n- [ ] Communication effectiveness\n- [ ] Cross-team coordination\n- [ ] Escalation decisions\n\n### 11:40 - Final Debrief & Wrap-up (20 min)\n\n**Debrief Questions**:\n1. What worked well?\n2. What didn't work?\n3. What surprised us?\n4. What are our top 3 action items?\n5. When should we run this again?\n\n## Post-Game Checklist\n\n- [ ] Restore all services to normal state\n- [ ] Verify no lingering issues\n- [ ] Collect all observer notes\n- [ ] Export metrics and dashboards\n- [ ] Schedule post-mortem meeting\n- [ ] Send thank-you to participants\n- [ ] Create action item tickets\n- [ ] Update runbooks based on learnings\n```\n\n## Game Day Observation Template\n\n```python\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom typing import List\n\n@dataclass\nclass GameDayObservation:\n    timestamp: datetime\n    observer: str\n    scenario: str\n    observation: str\n    category: str  # technical, process, communication, surprise\n    severity: str  # info, concern, critical\n    photo_url: str = \"\"\n\n@dataclass\nclass GameDayMetrics:\n    scenario_name: str\n    start_time: datetime\n    end_time: datetime\n\n    # Technical metrics\n    time_to_detect_seconds: float\n    time_to_respond_seconds: float\n    time_to_recover_seconds: float\n    error_rate_peak: float\n    alerts_fired: List[str] = field(default_factory=list)\n    alerts_missed: List[str] = field(default_factory=list)\n\n    # Team metrics\n    responders_involved: int\n    escalations_needed: int\n    communication_gaps: List[str] = field(default_factory=list)\n\n    # Success criteria\n    met_rto: bool = False\n    met_rpo: bool = False\n    zero_customer_impact: bool = False\n\n    def calculate_mttr(self) -> float:\n        \"\"\"Mean Time To Recovery\"\"\"\n        return (self.end_time - self.start_time).total_seconds()\n\n    def success_rate(self) -> float:\n        \"\"\"Percentage of success criteria met\"\"\"\n        criteria = [\n            self.met_rto,\n            self.met_rpo,\n            self.zero_customer_impact,\n            len(self.alerts_missed) == 0\n        ]\n        return sum(criteria) / len(criteria) * 100\n\n# Example usage\nmetrics = GameDayMetrics(\n    scenario_name=\"Database Failover\",\n    start_time=datetime(2025, 1, 15, 10, 10, 0),\n    end_time=datetime(2025, 1, 15, 10, 12, 30),\n    time_to_detect_seconds=15.0,\n    time_to_respond_seconds=45.0,\n    time_to_recover_seconds=150.0,\n    error_rate_peak=0.05,\n    alerts_fired=[\"DatabaseConnectionError\", \"HighLatency\"],\n    alerts_missed=[\"FailoverInitiated\"],\n    responders_involved=3,\n    escalations_needed=0,\n    met_rto=True,\n    met_rpo=True,\n    zero_customer_impact=True\n)\n\nprint(f\"MTTR: {metrics.calculate_mttr()}s\")\nprint(f\"Success Rate: {metrics.success_rate()}%\")\n```\n\n## Surprise Scenarios Library\n\n```yaml\n# Keep these secret until game day!\nsurprise_scenarios:\n  - name: \"Cascading Failure\"\n    description: \"Primary failure triggers secondary issue\"\n    injection:\n      - \"Database failover (expected)\"\n      - \"Cache eviction due to new primary IP (surprise!)\"\n    learning_goals:\n      - \"Do we understand our dependencies?\"\n      - \"Can we handle multiple simultaneous issues?\"\n\n  - name: \"Monitoring Blind Spot\"\n    description: \"Failure that doesn't trigger alerts\"\n    injection:\n      - \"Gradual connection pool leak\"\n      - \"No immediate alerts fire\"\n    learning_goals:\n      - \"How do we discover issues without alerts?\"\n      - \"Do we have adequate monitoring coverage?\"\n\n  - name: \"Documentation Failure\"\n    description: \"Runbook is outdated or incorrect\"\n    setup:\n      - \"Modify runbook to have incorrect commands\"\n      - \"Or remove runbook entirely\"\n    learning_goals:\n      - \"Can team problem-solve without docs?\"\n      - \"How quickly can we update documentation?\"\n\n  - name: \"Key Person Unavailable\"\n    description: \"Subject matter expert is unreachable\"\n    setup:\n      - \"Ask SME to not respond for 15 minutes\"\n    learning_goals:\n      - \"Is knowledge properly distributed?\"\n      - \"Can team succeed without specific person?\"\n\n  - name: \"Partial Degradation\"\n    description: \"Service works but slowly\"\n    injection:\n      - \"Add 5 second latency instead of complete failure\"\n    learning_goals:\n      - \"Do we detect performance degradation?\"\n      - \"What are our latency SLOs?\"\n```\n\n## Post-Game Report Template\n\n```markdown\n# Game Day Report: Database Failover\n\n**Date**: January 15, 2025\n**Participants**: 12\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Executive Summary\n\nConducted database failover game day to test RDS high availability and\napplication resilience. Successfully failed over database in 2.5 minutes\n(target: 2 min). Discovered 3 critical gaps in monitoring and 2 process\nimprovements needed.\n\n## Metrics\n\n| Metric | Target | Actual | Status |\n|--------|--------|--------|--------|\n| Time to Detect | < 30s | 15s | PASS |\n| Time to Respond | < 5min | 4min 20s | PASS |\n| Time to Recover | < 2min | 2min 30s | FAIL |\n| Alert Accuracy | 100% | 66% | FAIL |\n| Zero Customer Impact | Yes | Yes | PASS |\n\n## What Went Well\n\n1. Team responded quickly (4m 20s vs 5m target)\n2. Runbooks were accurate and helpful\n3. Communication was clear and frequent\n4. No customer impact during any scenario\n5. Application auto-reconnect worked perfectly\n\n## What Didn't Go Well\n\n1. Missing alert for failover initiation\n2. Took 30s longer than target to recover\n3. Connection pool exhaustion not detected\n4. Dashboard didn't show replica lag clearly\n5. Escalation contacts list was outdated\n\n## Surprises\n\n1. Cache invalidation cascaded from DB failover (unexpected)\n2. Read replica had 45s replication lag we didn't know about\n3. Application retried too aggressively during failover\n4. Team found a workaround we hadn't documented\n\n## Action Items\n\n| Action | Owner | Due Date | Priority |\n|--------|-------|----------|----------|\n| Add alert for RDS failover events | @sre-team | Jan 20 | P0 |\n| Update dashboard with replica lag | @platform | Jan 22 | P1 |\n| Document cache invalidation behavior | @dev-team | Jan 25 | P1 |\n| Add connection pool monitoring | @sre-team | Jan 27 | P0 |\n| Update escalation contact list | @manager | Jan 18 | P2 |\n| Tune application retry backoff | @dev-team | Feb 1 | P1 |\n\n## Lessons Learned\n\n1. **Monitoring Gaps**: We had blind spots in replica monitoring\n2. **Cascading Effects**: DB changes affect cache in non-obvious ways\n3. **Team Knowledge**: Cross-training is working well\n4. **Documentation**: Runbooks saved time, keep them updated\n\n## Next Game Day\n\n**Proposed Date**: March 15, 2025\n**Scenario**: Multi-region failover\n**Scope**: Production (with safeguards)\n\n## Appendix\n\n- Full timeline spreadsheet: [link]\n- Screen recordings: [link]\n- Metrics dashboard export: [link]\n- Raw observation notes: [link]\n```\n\n## Quick Reference\n\n| Phase | Duration | Key Activities |\n|-------|----------|----------------|\n| Planning | 2 weeks | Define scenarios, invite participants |\n| Pre-game | 30 min | Setup, verify environment, brief team |\n| Execution | 2 hours | Run scenarios, observe, document |\n| Debrief | 30 min | Immediate learnings, quick wins |\n| Post-mortem | 1 week later | Detailed analysis, action items |\n| Follow-up | 1 month | Verify improvements, plan next game day |\n","skills/chaos-engineer/references/infrastructure-chaos.md":"# Infrastructure Chaos Engineering\n\n## Network Latency Injection\n\n```python\n# Using toxiproxy for network chaos\nimport requests\nfrom typing import Literal\n\nclass ToxiproxyClient:\n    def __init__(self, host: str = \"localhost:8474\"):\n        self.base_url = f\"http://{host}\"\n\n    def create_proxy(self, name: str, listen: str, upstream: str):\n        \"\"\"Create proxy to inject failures.\"\"\"\n        response = requests.post(f\"{self.base_url}/proxies\", json={\n            \"name\": name,\n            \"listen\": listen,\n            \"upstream\": upstream,\n            \"enabled\": True\n        })\n        return response.json()\n\n    def add_latency(self, proxy: str, latency_ms: int, jitter_ms: int = 0):\n        \"\"\"Add latency toxic.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"latency\",\n                \"type\": \"latency\",\n                \"attributes\": {\n                    \"latency\": latency_ms,\n                    \"jitter\": jitter_ms\n                }\n            }\n        )\n\n    def add_bandwidth_limit(self, proxy: str, rate_kb: int):\n        \"\"\"Limit bandwidth.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"bandwidth\",\n                \"type\": \"bandwidth\",\n                \"attributes\": {\"rate\": rate_kb}\n            }\n        )\n\n    def add_timeout(self, proxy: str, timeout_ms: int):\n        \"\"\"Add connection timeout.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"timeout\",\n                \"type\": \"timeout\",\n                \"attributes\": {\"timeout\": timeout_ms}\n            }\n        )\n\n# Example usage\ntoxiproxy = ToxiproxyClient()\n\n# Create proxy to database\ntoxiproxy.create_proxy(\n    name=\"postgres\",\n    listen=\"0.0.0.0:5433\",\n    upstream=\"postgres:5432\"\n)\n\n# Inject 200ms latency with 50ms jitter\ntoxiproxy.add_latency(\"postgres\", latency_ms=200, jitter_ms=50)\n```\n\n## AWS Zone Failure Simulation\n\n```python\nimport boto3\nfrom datetime import datetime, timedelta\n\nclass AWSChaosSimulator:\n    def __init__(self, region: str):\n        self.ec2 = boto3.client('ec2', region_name=region)\n        self.asg = boto3.client('autoscaling', region_name=region)\n        self.elb = boto3.client('elbv2', region_name=region)\n\n    def simulate_az_failure(\n        self,\n        availability_zone: str,\n        asg_name: str,\n        duration_minutes: int = 10\n    ):\n        \"\"\"\n        Simulate AZ failure by terminating instances in specific AZ.\n        Auto Scaling Group will launch replacements in other AZs.\n        \"\"\"\n        # Find instances in target AZ\n        instances = self.ec2.describe_instances(Filters=[\n            {'Name': 'tag:aws:autoscaling:groupName', 'Values': [asg_name]},\n            {'Name': 'availability-zone', 'Values': [availability_zone]},\n            {'Name': 'instance-state-name', 'Values': ['running']}\n        ])\n\n        instance_ids = [\n            i['InstanceId']\n            for r in instances['Reservations']\n            for i in r['Instances']\n        ]\n\n        if not instance_ids:\n            return {\"status\": \"no_instances\", \"instances\": []}\n\n        # Suspend AZ-specific scaling activities\n        self.asg.suspend_processes(\n            AutoScalingGroupName=asg_name,\n            ScalingProcesses=['AZRebalance']\n        )\n\n        # Terminate instances to simulate AZ failure\n        self.ec2.terminate_instances(InstanceIds=instance_ids)\n\n        return {\n            \"status\": \"simulated\",\n            \"availability_zone\": availability_zone,\n            \"terminated_instances\": instance_ids,\n            \"recovery_time\": datetime.now() + timedelta(minutes=duration_minutes)\n        }\n\n    def drain_az_from_load_balancer(\n        self,\n        target_group_arn: str,\n        availability_zone: str\n    ):\n        \"\"\"Remove AZ from load balancer to simulate zone failure.\"\"\"\n        # Get current target health\n        health = self.elb.describe_target_health(\n            TargetGroupArn=target_group_arn\n        )\n\n        # Find targets in AZ\n        targets_to_deregister = []\n        for target in health['TargetHealthDescriptions']:\n            # Get instance details\n            instance = self.ec2.describe_instances(\n                InstanceIds=[target['Target']['Id']]\n            )\n            if instance['Reservations'][0]['Instances'][0]['Placement']['AvailabilityZone'] == availability_zone:\n                targets_to_deregister.append(target['Target'])\n\n        # Deregister targets\n        if targets_to_deregister:\n            self.elb.deregister_targets(\n                TargetGroupArn=target_group_arn,\n                Targets=targets_to_deregister\n            )\n\n        return {\n            \"deregistered_targets\": len(targets_to_deregister),\n            \"availability_zone\": availability_zone\n        }\n```\n\n## Server Resource Exhaustion\n\n```bash\n#!/bin/bash\n# CPU stress test using stress-ng\n\n# Install stress-ng\nsudo apt-get install -y stress-ng\n\n# Stress CPU - use 80% of available cores for 5 minutes\nstress-ng --cpu $(nproc --all) --cpu-load 80 --timeout 5m\n\n# Memory stress - consume 70% of available memory\nTOTAL_MEM_MB=$(free -m | awk 'NR==2{print $2}')\nSTRESS_MEM_MB=$((TOTAL_MEM_MB * 70 / 100))\nstress-ng --vm 1 --vm-bytes ${STRESS_MEM_MB}M --timeout 5m\n\n# Disk I/O stress - 4 workers doing sequential writes\nstress-ng --hdd 4 --hdd-bytes 1G --timeout 5m\n\n# Network bandwidth saturation\n# Using iperf3 to saturate network\niperf3 -c target-server -t 300 -P 10  # 10 parallel streams for 5 minutes\n```\n\n## Docker Container Chaos with Pumba\n\n```bash\n#!/bin/bash\n# Pumba - chaos testing for Docker\n\n# Kill random container every 30 seconds\npumba --interval 30s kill --signal SIGKILL \"re2:^myapp\"\n\n# Pause container for 15 seconds, then resume\npumba pause --duration 15s myapp-container\n\n# Add network delay to container\npumba netem \\\n  --duration 5m \\\n  --interface eth0 \\\n  delay \\\n    --time 300 \\\n    --jitter 50 \\\n  myapp-container\n\n# Packet loss - drop 20% of packets\npumba netem \\\n  --duration 5m \\\n  loss \\\n    --percent 20 \\\n  myapp-container\n\n# Limit bandwidth to 1Mbps\npumba netem \\\n  --duration 5m \\\n  rate \\\n    --rate 1mbit \\\n  myapp-container\n\n# Stop all containers matching pattern for 2 minutes\npumba stop --duration 2m \"re2:^production-.*\"\n```\n\n## DNS Failure Simulation\n\n```python\n# Using dnsmasq or editing /etc/hosts for DNS chaos\n\nimport subprocess\nimport time\nfrom contextlib import contextmanager\n\nclass DNSChaos:\n    @staticmethod\n    @contextmanager\n    def block_domain(domain: str, duration_seconds: int = 60):\n        \"\"\"Block DNS resolution for domain by pointing to localhost.\"\"\"\n        try:\n            # Add entry to /etc/hosts\n            subprocess.run([\n                'sudo', 'sh', '-c',\n                f'echo \"127.0.0.1 {domain}\" >> /etc/hosts'\n            ], check=True)\n\n            print(f\"Blocked DNS for {domain}\")\n            yield\n\n        finally:\n            # Wait for duration\n            time.sleep(duration_seconds)\n\n            # Remove entry from /etc/hosts\n            subprocess.run([\n                'sudo', 'sed', '-i',\n                f'/127.0.0.1 {domain}/d',\n                '/etc/hosts'\n            ], check=True)\n\n            print(f\"Restored DNS for {domain}\")\n\n    @staticmethod\n    def add_dns_latency(domain: str, latency_ms: int):\n        \"\"\"Add latency to DNS queries using dnsmasq.\"\"\"\n        config = f\"\"\"\n        # Add to /etc/dnsmasq.conf\n        address=/{domain}/127.0.0.1\n        min-cache-ttl=0\n\n        # Restart dnsmasq with delay\n        \"\"\"\n        return config\n\n# Usage\nwith DNSChaos.block_domain('api.external-service.com', duration_seconds=120):\n    # Run tests while DNS is blocked\n    print(\"DNS blocked - testing fallback behavior\")\n```\n\n## Certificate Expiry Simulation\n\n```python\nfrom datetime import datetime, timedelta\nfrom cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom cryptography.hazmat.primitives import hashes, serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\n\ndef create_expired_certificate(\n    common_name: str,\n    expired_days_ago: int = 1\n) -> tuple[bytes, bytes]:\n    \"\"\"\n    Create an expired TLS certificate for chaos testing.\n    Returns (certificate_pem, private_key_pem)\n    \"\"\"\n    # Generate private key\n    private_key = rsa.generate_private_key(\n        public_exponent=65537,\n        key_size=2048\n    )\n\n    # Certificate valid from 365 days ago to `expired_days_ago` ago\n    not_valid_before = datetime.utcnow() - timedelta(days=365)\n    not_valid_after = datetime.utcnow() - timedelta(days=expired_days_ago)\n\n    subject = issuer = x509.Name([\n        x509.NameAttribute(NameOID.COMMON_NAME, common_name)\n    ])\n\n    cert = x509.CertificateBuilder().subject_name(\n        subject\n    ).issuer_name(\n        issuer\n    ).public_key(\n        private_key.public_key()\n    ).serial_number(\n        x509.random_serial_number()\n    ).not_valid_before(\n        not_valid_before\n    ).not_valid_after(\n        not_valid_after\n    ).sign(private_key, hashes.SHA256())\n\n    # Serialize to PEM\n    cert_pem = cert.public_bytes(serialization.Encoding.PEM)\n    key_pem = private_key.private_bytes(\n        encoding=serialization.Encoding.PEM,\n        format=serialization.PrivateFormat.PKCS8,\n        encryption_algorithm=serialization.NoEncryption()\n    )\n\n    return cert_pem, key_pem\n```\n\n## Quick Reference\n\n| Failure Type | Tool | Command/Method |\n|--------------|------|----------------|\n| Network latency | toxiproxy | `add_latency(proxy, ms)` |\n| Packet loss | toxiproxy/pumba | `loss --percent 20` |\n| AZ failure | AWS API | `simulate_az_failure(az, asg)` |\n| CPU stress | stress-ng | `--cpu N --cpu-load 80` |\n| Memory exhaustion | stress-ng | `--vm 1 --vm-bytes XG` |\n| Container kill | pumba | `kill --signal SIGKILL` |\n| DNS failure | /etc/hosts | Block domain resolution |\n| Cert expiry | cryptography | Generate expired cert |\n","skills/chaos-engineer/references/kubernetes-chaos.md":"# Kubernetes Chaos Engineering\n\n## Litmus Chaos - ChaosEngine\n\n```yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: nginx-chaos\n  namespace: default\nspec:\n  # Application information\n  appinfo:\n    appns: 'default'\n    applabel: 'app=nginx'\n    appkind: 'deployment'\n\n  # Chaos service account\n  chaosServiceAccount: litmus-admin\n\n  # Experiments to run\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            # Total chaos duration\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n\n            # Chaos interval (delete pod every X seconds)\n            - name: CHAOS_INTERVAL\n              value: '10'\n\n            # Force delete pods\n            - name: FORCE\n              value: 'true'\n\n            # Number of pods to delete\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n    - name: pod-network-latency\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: NETWORK_LATENCY\n              value: '2000'  # 2 second latency\n            - name: JITTER\n              value: '200'   # 200ms jitter\n            - name: CONTAINER_RUNTIME\n              value: 'containerd'\n\n    - name: pod-cpu-hog\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: CPU_CORES\n              value: '2'\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n  # Monitor application during chaos\n  monitoring: true\n\n  # Job cleanup policy\n  jobCleanUpPolicy: 'delete'\n```\n\n## Chaos Mesh Experiments\n\n```yaml\n# Network partition between services\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: partition-frontend-backend\n  namespace: chaos-testing\nspec:\n  action: partition\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'frontend'\n  direction: to\n  target:\n    mode: all\n    selector:\n      namespaces:\n        - production\n      labelSelectors:\n        'app': 'backend'\n  duration: '5m'\n\n---\n# Pod failure - kill random pods\napiVersion: chaos-mesh.org/v1alpha1\nkind: PodChaos\nmetadata:\n  name: pod-failure\n  namespace: chaos-testing\nspec:\n  action: pod-failure\n  mode: one  # one, all, fixed, fixed-percent, random-max-percent\n  duration: '30s'\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'payment-service'\n  scheduler:\n    cron: '@every 10m'  # Run every 10 minutes\n\n---\n# Network bandwidth limitation\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: bandwidth-limit\nspec:\n  action: bandwidth\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'tier': 'backend'\n  bandwidth:\n    rate: '1mbps'\n    limit: 20000\n    buffer: 10000\n  duration: '5m'\n\n---\n# Disk I/O stress\napiVersion: chaos-mesh.org/v1alpha1\nkind: IOChaos\nmetadata:\n  name: io-latency\nspec:\n  action: latency\n  mode: one\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'database'\n  volumePath: /var/lib/postgresql/data\n  path: /var/lib/postgresql/data/**/*\n  delay: '100ms'\n  percent: 50  # 50% of I/O operations affected\n  duration: '5m'\n\n---\n# DNS chaos - random DNS errors\napiVersion: chaos-mesh.org/v1alpha1\nkind: DNSChaos\nmetadata:\n  name: dns-random-error\nspec:\n  action: random\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'api-gateway'\n  patterns:\n    - external-api.example.com\n    - *.third-party-service.com\n  duration: '3m'\n```\n\n## Node Drain Simulation\n\n```python\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport time\n\nclass K8sNodeChaos:\n    def __init__(self):\n        config.load_kube_config()\n        self.core_v1 = client.CoreV1Api()\n        self.apps_v1 = client.AppsV1Api()\n\n    def cordon_node(self, node_name: str):\n        \"\"\"Mark node as unschedulable.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": True\n            }\n        }\n        try:\n            self.core_v1.patch_node(node_name, body)\n            print(f\"Node {node_name} cordoned\")\n        except ApiException as e:\n            print(f\"Failed to cordon node: {e}\")\n\n    def drain_node(\n        self,\n        node_name: str,\n        grace_period_seconds: int = 30,\n        delete_local_data: bool = True\n    ):\n        \"\"\"\n        Drain node by evicting all pods.\n        Simulates node failure or maintenance.\n        \"\"\"\n        # First, cordon the node\n        self.cordon_node(node_name)\n\n        # Get all pods on the node\n        field_selector = f\"spec.nodeName={node_name}\"\n        pods = self.core_v1.list_pod_for_all_namespaces(\n            field_selector=field_selector\n        )\n\n        # Evict each pod\n        for pod in pods.items:\n            # Skip DaemonSet pods and mirror pods\n            if pod.metadata.owner_references:\n                for owner in pod.metadata.owner_references:\n                    if owner.kind in ['DaemonSet', 'Node']:\n                        continue\n\n            # Create eviction\n            eviction = client.V1Eviction(\n                metadata=client.V1ObjectMeta(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace\n                ),\n                delete_options=client.V1DeleteOptions(\n                    grace_period_seconds=grace_period_seconds\n                )\n            )\n\n            try:\n                self.core_v1.create_namespaced_pod_eviction(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace,\n                    body=eviction\n                )\n                print(f\"Evicted pod {pod.metadata.name}\")\n            except ApiException as e:\n                if e.status == 429:  # Too many requests\n                    print(f\"Pod {pod.metadata.name} protected by PDB\")\n                else:\n                    print(f\"Failed to evict {pod.metadata.name}: {e}\")\n\n        return {\"node\": node_name, \"status\": \"drained\"}\n\n    def uncordon_node(self, node_name: str):\n        \"\"\"Mark node as schedulable again.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": False\n            }\n        }\n        self.core_v1.patch_node(node_name, body)\n        print(f\"Node {node_name} uncordoned\")\n\n    def simulate_node_failure(\n        self,\n        node_name: str,\n        duration_seconds: int = 300\n    ):\n        \"\"\"\n        Simulate complete node failure.\n        Drain node, wait, then restore.\n        \"\"\"\n        print(f\"Simulating failure of node {node_name}\")\n\n        # Drain the node\n        self.drain_node(node_name)\n\n        # Wait for duration\n        print(f\"Node failed for {duration_seconds} seconds\")\n        time.sleep(duration_seconds)\n\n        # Restore node\n        self.uncordon_node(node_name)\n        print(\"Node restored\")\n```\n\n## Pod Autoscaling Chaos\n\n```yaml\n# Test HPA behavior under load\napiVersion: chaos-mesh.org/v1alpha1\nkind: StressChaos\nmetadata:\n  name: stress-hpa-trigger\nspec:\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'web-server'\n  stressors:\n    cpu:\n      workers: 2\n      load: 80  # 80% CPU load\n  duration: '10m'\n\n---\n# Verify HPA scaling response\napiVersion: v1\nkind: Pod\nmetadata:\n  name: chaos-verification\nspec:\n  containers:\n  - name: verifier\n    image: bitnami/kubectl:latest\n    command:\n      - /bin/bash\n      - -c\n      - |\n        # Monitor HPA scaling\n        while true; do\n          echo \"=== HPA Status ===\"\n          kubectl get hpa web-server -o json | \\\n            jq '.status | {current: .currentReplicas, desired: .desiredReplicas, cpu: .currentCPUUtilizationPercentage}'\n\n          echo \"=== Pod Count ===\"\n          kubectl get pods -l app=web-server --no-headers | wc -l\n\n          sleep 10\n        done\n```\n\n## Custom Resource Chaos\n\n```python\n# Python script to test custom CRD resilience\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport random\nimport time\n\ndef chaos_delete_custom_resources(\n    group: str,\n    version: str,\n    plural: str,\n    namespace: str,\n    percentage: int = 30\n):\n    \"\"\"\n    Randomly delete custom resources to test operator resilience.\n\n    Args:\n        group: API group (e.g., 'app.example.com')\n        version: API version (e.g., 'v1')\n        plural: Resource plural name (e.g., 'myapps')\n        namespace: Namespace to target\n        percentage: Percentage of resources to delete (0-100)\n    \"\"\"\n    config.load_kube_config()\n    custom_api = client.CustomObjectsApi()\n\n    try:\n        # List all custom resources\n        resources = custom_api.list_namespaced_custom_object(\n            group=group,\n            version=version,\n            namespace=namespace,\n            plural=plural\n        )\n\n        items = resources.get('items', [])\n        if not items:\n            print(\"No resources found\")\n            return\n\n        # Calculate number to delete\n        count_to_delete = max(1, int(len(items) * percentage / 100))\n\n        # Randomly select resources\n        to_delete = random.sample(items, count_to_delete)\n\n        print(f\"Deleting {count_to_delete} of {len(items)} {plural}\")\n\n        # Delete selected resources\n        for resource in to_delete:\n            name = resource['metadata']['name']\n            try:\n                custom_api.delete_namespaced_custom_object(\n                    group=group,\n                    version=version,\n                    namespace=namespace,\n                    plural=plural,\n                    name=name,\n                    body=client.V1DeleteOptions()\n                )\n                print(f\"Deleted {plural}/{name}\")\n                time.sleep(1)  # Rate limit deletions\n            except ApiException as e:\n                print(f\"Failed to delete {name}: {e}\")\n\n    except ApiException as e:\n        print(f\"Error listing resources: {e}\")\n\n# Example: Delete 30% of MyApp custom resources\nchaos_delete_custom_resources(\n    group='app.example.com',\n    version='v1',\n    plural='myapps',\n    namespace='production',\n    percentage=30\n)\n```\n\n## Quick Reference\n\n| Chaos Type | Tool | YAML/Command |\n|------------|------|--------------|\n| Pod delete | Litmus | `pod-delete` experiment |\n| Network latency | Chaos Mesh | `NetworkChaos` with action: delay |\n| Node drain | kubectl/API | `kubectl drain <node>` |\n| CPU stress | Chaos Mesh | `StressChaos` with cpu stressor |\n| DNS failure | Chaos Mesh | `DNSChaos` random/error action |\n| I/O latency | Chaos Mesh | `IOChaos` with latency action |\n| Network partition | Chaos Mesh | `NetworkChaos` partition |\n| Pod failure | Chaos Mesh | `PodChaos` pod-failure |\n","skills/cli-developer/SKILL.md":"---\nname: cli-developer\ndescription: Use when building CLI tools, implementing argument parsing, or adding interactive prompts. Invoke for parsing flags and subcommands, displaying progress bars and spinners, generating bash/zsh/fish completion scripts, CLI design, shell completions, and cross-platform terminal applications using commander, click, typer, or cobra.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: devops\n  triggers: CLI, command-line, terminal app, argument parsing, shell completion, interactive prompt, progress bar, commander, click, typer, cobra\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: devops-engineer\n---\n\n# CLI Developer\n\n## Core Workflow\n\n1. **Analyze UX** — Identify user workflows, command hierarchy, common tasks. Validate by listing all commands and their expected `--help` output before writing code.\n2. **Design commands** — Plan subcommands, flags, arguments, configuration. Confirm flag naming is consistent and no existing signatures are broken.\n3. **Implement** — Build with the appropriate CLI framework for the language (see Reference Guide below). After wiring up commands, run `<cli> --help` to verify help text renders correctly and `<cli> --version` to confirm version output.\n4. **Polish** — Add completions, help text, error messages, progress indicators. Verify TTY detection for color output and graceful SIGINT handling.\n5. **Test** — Run cross-platform smoke tests; benchmark startup time (target: <50ms).\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Design Patterns | `references/design-patterns.md` | Subcommands, flags, config, architecture |\n| Node.js CLIs | `references/node-cli.md` | commander, yargs, inquirer, chalk |\n| Python CLIs | `references/python-cli.md` | click, typer, argparse, rich |\n| Go CLIs | `references/go-cli.md` | cobra, viper, bubbletea |\n| UX Patterns | `references/ux-patterns.md` | Progress bars, colors, help text |\n\n## Quick-Start Example\n\n### Node.js (commander)\n\n```js\n#!/usr/bin/env node\n// npm install commander\nconst { program } = require('commander');\n\nprogram\n  .name('mytool')\n  .description('Example CLI')\n  .version('1.0.0');\n\nprogram\n  .command('greet <name>')\n  .description('Greet a user')\n  .option('-l, --loud', 'uppercase the greeting')\n  .action((name, opts) => {\n    const msg = `Hello, ${name}!`;\n    console.log(opts.loud ? msg.toUpperCase() : msg);\n  });\n\nprogram.parse();\n```\n\nFor Python (click/typer) and Go (cobra) quick-start examples, see `references/python-cli.md` and `references/go-cli.md`.\n\n## Constraints\n\n### MUST DO\n- Keep startup time under 50ms\n- Provide clear, actionable error messages\n- Support `--help` and `--version` flags\n- Use consistent flag naming conventions\n- Handle SIGINT (Ctrl+C) gracefully\n- Validate user input early\n- Support both interactive and non-interactive modes\n- Test on Windows, macOS, and Linux\n\n### MUST NOT DO\n\n- **Block on synchronous I/O unnecessarily** — use async reads or stream processing instead.\n- **Print to stdout when output will be piped** — write logs/diagnostics to stderr.\n- **Use colors when output is not a TTY** — detect before applying color:\n  ```js\n  // Node.js\n  const useColor = process.stdout.isTTY;\n  ```\n  ```python\n  # Python\n  import sys\n  use_color = sys.stdout.isatty()\n  ```\n  ```go\n  // Go\n  import \"golang.org/x/term\"\n  useColor := term.IsTerminal(int(os.Stdout.Fd()))\n  ```\n- **Break existing command signatures** — treat flag/subcommand renames as breaking changes.\n- **Require interactive input in CI/CD environments** — always provide non-interactive fallbacks via flags or env vars.\n- **Hardcode paths or platform-specific logic** — use `os.homedir()` / `os.UserHomeDir()` / `Path.home()` instead.\n- **Ship without shell completions** — all three frameworks above have built-in completion generation.\n\n## Output Templates\n\nWhen implementing CLI features, provide:\n1. Command structure (main entry point, subcommands)\n2. Configuration handling (files, env vars, flags)\n3. Core implementation with error handling\n4. Shell completion scripts if applicable\n5. Brief explanation of UX decisions\n\n## Knowledge Reference\n\nCLI frameworks (commander, yargs, oclif, click, typer, argparse, cobra, viper), terminal UI (chalk, inquirer, rich, bubbletea), testing (snapshot testing, E2E), distribution (npm, pip, homebrew, releases), performance optimization\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/devops/cli-developer/)\n","skills/cli-developer/references/design-patterns.md":"# CLI Design Patterns\n\n## Command Hierarchy\n\n```\nmycli                           # Root command\n├── init [options]              # Simple command\n├── config\n│   ├── get <key>              # Nested subcommand\n│   ├── set <key> <value>\n│   └── list\n├── deploy [environment]        # Command with args\n│   ├── --dry-run              # Flag\n│   ├── --force\n│   └── --config <file>        # Option with value\n└── plugins\n    ├── install <name>\n    ├── list\n    └── remove <name>\n```\n\n## Flag Conventions\n\n```bash\n# Boolean flags (presence = true)\nmycli deploy --force --dry-run\n\n# Short + long forms\nmycli -v --verbose\nmycli -c config.yml --config config.yml\n\n# Required vs optional\nmycli deploy <env>              # Positional (required)\nmycli deploy --env production   # Flag (optional)\n\n# Multiple values\nmycli install pkg1 pkg2 pkg3    # Variadic args\nmycli --exclude node_modules --exclude .git\n```\n\n## Configuration Layers\n\nPriority order (highest to lowest):\n\n1. **Command-line flags** - Explicit user intent\n2. **Environment variables** - Runtime context\n3. **Config files (project)** - `.myclirc`, `mycli.config.js`\n4. **Config files (user)** - `~/.myclirc`, `~/.config/mycli/config.yml`\n5. **Config files (system)** - `/etc/mycli/config.yml`\n6. **Defaults** - Hard-coded sensible defaults\n\n```javascript\n// Example config resolution\nconst config = {\n  ...systemDefaults,\n  ...loadSystemConfig(),\n  ...loadUserConfig(),\n  ...loadProjectConfig(),\n  ...loadEnvVars(),\n  ...parseCliFlags(),\n};\n```\n\n## Exit Codes\n\nStandard POSIX exit codes:\n\n```javascript\nconst EXIT_CODES = {\n  SUCCESS: 0,\n  GENERAL_ERROR: 1,\n  MISUSE: 2,              // Invalid arguments\n  PERMISSION_DENIED: 77,\n  NOT_FOUND: 127,\n  SIGINT: 130,            // Ctrl+C\n};\n```\n\n## Plugin Architecture\n\n```\nmycli/\n├── core/                      # Core functionality\n├── plugins/\n│   ├── aws/                  # Plugin: AWS integration\n│   │   ├── package.json\n│   │   └── index.js\n│   └── github/               # Plugin: GitHub integration\n│       ├── package.json\n│       └── index.js\n└── plugin-loader.js          # Discovery & loading\n```\n\nPlugin discovery:\n1. Check `~/.mycli/plugins/`\n2. Check `node_modules/mycli-plugin-*`\n3. Check `MYCLI_PLUGIN_PATH` env var\n\n## Error Handling Patterns\n\n```javascript\n// Good: Actionable error messages\nError: Config file not found at /path/to/config.yml\n\nTried locations:\n  • ./mycli.config.yml\n  • ~/.myclirc\n  • /etc/mycli/config.yml\n\nRun 'mycli init' to create a config file, or use --config to specify location.\n\n// Bad: Unhelpful errors\nError: ENOENT\n```\n\n## Interactive vs Non-Interactive\n\n```javascript\n// Detect if running in CI/CD\nconst isCI = process.env.CI === 'true' || !process.stdout.isTTY;\n\nif (isCI) {\n  // Non-interactive: fail fast with clear errors\n  if (!options.environment) {\n    throw new Error('--environment required in non-interactive mode');\n  }\n} else {\n  // Interactive: prompt user\n  const environment = await prompt({\n    type: 'select',\n    message: 'Select environment:',\n    choices: ['development', 'staging', 'production'],\n  });\n}\n```\n\n## State Management\n\n```\n~/.mycli/\n├── config.yml           # User configuration\n├── cache/               # Cached data\n│   ├── plugins.json\n│   └── api-responses/\n├── credentials.json     # Sensitive data (600 perms)\n└── state.json          # Session state\n```\n\n## Performance Patterns\n\n```javascript\n// Lazy loading: Don't load unused dependencies\nif (command === 'deploy') {\n  const deploy = require('./commands/deploy'); // Load on demand\n  await deploy.run();\n}\n\n// Caching: Avoid repeated API calls\nconst cache = new Cache('~/.mycli/cache', { ttl: 3600 });\nlet plugins = await cache.get('plugins');\nif (!plugins) {\n  plugins = await fetchPlugins();\n  await cache.set('plugins', plugins);\n}\n\n// Async operations: Don't block unnecessarily\nawait Promise.all([\n  validateConfig(),\n  checkForUpdates(),\n  loadPlugins(),\n]);\n```\n\n## Versioning & Updates\n\n```javascript\n// Check for updates (non-blocking)\ncheckForUpdates().then(update => {\n  if (update.available) {\n    console.log(`Update available: ${update.version}`);\n    console.log(`Run: npm install -g mycli@latest`);\n  }\n}).catch(() => {\n  // Silently fail - don't interrupt user workflow\n});\n\n// Version compatibility\nconst MIN_NODE_VERSION = '18.0.0';\nif (!semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`)) {\n  console.error(`mycli requires Node.js ${MIN_NODE_VERSION} or higher`);\n  process.exit(1);\n}\n```\n\n## Help Text Design\n\n```\nUSAGE\n  mycli deploy [environment] [options]\n\nARGUMENTS\n  environment  Target environment (development|staging|production)\n\nOPTIONS\n  -c, --config <file>  Path to config file\n  -f, --force          Skip confirmation prompts\n  -d, --dry-run        Preview changes without executing\n  -v, --verbose        Show detailed output\n\nEXAMPLES\n  # Deploy to production\n  mycli deploy production\n\n  # Preview staging deployment\n  mycli deploy staging --dry-run\n\n  # Use custom config\n  mycli deploy --config ./custom.yml\n\nLearn more: https://docs.mycli.dev/deploy\n```\n","skills/cli-developer/references/go-cli.md":"# Go CLI Development\n\n## Cobra (Recommended)\n\nPowerful CLI framework used by kubectl, hugo, docker.\n\n```go\n// cmd/root.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"os\"\n    \"github.com/spf13/cobra\"\n    \"github.com/spf13/viper\"\n)\n\nvar (\n    cfgFile string\n    verbose bool\n)\n\nvar rootCmd = &cobra.Command{\n    Use:   \"mycli\",\n    Short: \"My awesome CLI tool\",\n    Long: `A longer description of your CLI application`,\n    Version: \"1.0.0\",\n}\n\nfunc Execute() {\n    if err := rootCmd.Execute(); err != nil {\n        fmt.Fprintln(os.Stderr, err)\n        os.Exit(1)\n    }\n}\n\nfunc init() {\n    cobra.OnInitialize(initConfig)\n\n    rootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file\")\n    rootCmd.PersistentFlags().BoolVarP(&verbose, \"verbose\", \"v\", false, \"verbose output\")\n\n    viper.BindPFlag(\"verbose\", rootCmd.PersistentFlags().Lookup(\"verbose\"))\n}\n\nfunc initConfig() {\n    if cfgFile != \"\" {\n        viper.SetConfigFile(cfgFile)\n    } else {\n        home, err := os.UserHomeDir()\n        cobra.CheckErr(err)\n\n        viper.AddConfigPath(home)\n        viper.AddConfigPath(\".\")\n        viper.SetConfigType(\"yaml\")\n        viper.SetConfigName(\".mycli\")\n    }\n\n    viper.AutomaticEnv()\n\n    if err := viper.ReadInConfig(); err == nil {\n        fmt.Fprintln(os.Stderr, \"Using config file:\", viper.ConfigFileUsed())\n    }\n}\n\n// cmd/init.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/cobra\"\n)\n\nvar (\n    template string\n    force    bool\n)\n\nvar initCmd = &cobra.Command{\n    Use:   \"init [name]\",\n    Short: \"Initialize a new project\",\n    Args:  cobra.ExactArgs(1),\n    RunE: func(cmd *cobra.Command, args []string) error {\n        name := args[0]\n        return initProject(name, template, force)\n    },\n}\n\nfunc init() {\n    rootCmd.AddCommand(initCmd)\n\n    initCmd.Flags().StringVarP(&template, \"template\", \"t\", \"default\", \"Project template\")\n    initCmd.Flags().BoolVarP(&force, \"force\", \"f\", false, \"Overwrite existing\")\n}\n\nfunc initProject(name, template string, force bool) error {\n    fmt.Printf(\"Creating %s from %s\\n\", name, template)\n    return nil\n}\n\n// cmd/deploy.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/cobra\"\n)\n\nvar (\n    dryRun bool\n)\n\nvar deployCmd = &cobra.Command{\n    Use:   \"deploy [environment]\",\n    Short: \"Deploy to environment\",\n    Args:  cobra.ExactArgs(1),\n    ValidArgs: []string{\"dev\", \"staging\", \"prod\"},\n    RunE: func(cmd *cobra.Command, args []string) error {\n        env := args[0]\n        return deploy(env, dryRun)\n    },\n}\n\nfunc init() {\n    rootCmd.AddCommand(deployCmd)\n    deployCmd.Flags().BoolVar(&dryRun, \"dry-run\", false, \"Preview only\")\n}\n\nfunc deploy(env string, dryRun bool) error {\n    if dryRun {\n        fmt.Printf(\"Would deploy to: %s\\n\", env)\n    } else {\n        fmt.Printf(\"Deploying to %s...\\n\", env)\n    }\n    return nil\n}\n\n// main.go\npackage main\n\nimport \"mycli/cmd\"\n\nfunc main() {\n    cmd.Execute()\n}\n```\n\n## Viper (Configuration)\n\nConfiguration management with multiple sources.\n\n```go\npackage config\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/viper\"\n)\n\ntype Config struct {\n    Environment string `mapstructure:\"environment\"`\n    Timeout     int    `mapstructure:\"timeout\"`\n    Verbose     bool   `mapstructure:\"verbose\"`\n    API         APIConfig `mapstructure:\"api\"`\n}\n\ntype APIConfig struct {\n    Endpoint string `mapstructure:\"endpoint\"`\n    Token    string `mapstructure:\"token\"`\n}\n\nfunc Load() (*Config, error) {\n    // Set defaults\n    viper.SetDefault(\"environment\", \"development\")\n    viper.SetDefault(\"timeout\", 30)\n    viper.SetDefault(\"verbose\", false)\n\n    // Config file locations\n    viper.SetConfigName(\"config\")\n    viper.SetConfigType(\"yaml\")\n    viper.AddConfigPath(\"/etc/mycli/\")\n    viper.AddConfigPath(\"$HOME/.config/mycli\")\n    viper.AddConfigPath(\".\")\n\n    // Environment variables\n    viper.SetEnvPrefix(\"MYCLI\")\n    viper.AutomaticEnv()\n\n    // Read config\n    if err := viper.ReadInConfig(); err != nil {\n        if _, ok := err.(viper.ConfigFileNotFoundError); !ok {\n            return nil, fmt.Errorf(\"failed to read config: %w\", err)\n        }\n    }\n\n    // Unmarshal into struct\n    var cfg Config\n    if err := viper.Unmarshal(&cfg); err != nil {\n        return nil, fmt.Errorf(\"failed to unmarshal config: %w\", err)\n    }\n\n    return &cfg, nil\n}\n```\n\n## Bubble Tea (Interactive TUI)\n\nModern terminal UI framework for interactive CLIs.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n\n    tea \"github.com/charmbracelet/bubbletea\"\n    \"github.com/charmbracelet/lipgloss\"\n)\n\n// Model\ntype model struct {\n    choices  []string\n    cursor   int\n    selected map[int]struct{}\n}\n\nfunc initialModel() model {\n    return model{\n        choices:  []string{\"TypeScript\", \"ESLint\", \"Prettier\", \"Jest\"},\n        selected: make(map[int]struct{}),\n    }\n}\n\n// Init\nfunc (m model) Init() tea.Cmd {\n    return nil\n}\n\n// Update\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n    case tea.KeyMsg:\n        switch msg.String() {\n        case \"ctrl+c\", \"q\":\n            return m, tea.Quit\n\n        case \"up\", \"k\":\n            if m.cursor > 0 {\n                m.cursor--\n            }\n\n        case \"down\", \"j\":\n            if m.cursor < len(m.choices)-1 {\n                m.cursor++\n            }\n\n        case \" \":\n            _, ok := m.selected[m.cursor]\n            if ok {\n                delete(m.selected, m.cursor)\n            } else {\n                m.selected[m.cursor] = struct{}{}\n            }\n\n        case \"enter\":\n            return m, tea.Quit\n        }\n    }\n\n    return m, nil\n}\n\n// View\nfunc (m model) View() string {\n    s := \"Select features:\\n\\n\"\n\n    for i, choice := range m.choices {\n        cursor := \" \"\n        if m.cursor == i {\n            cursor = \">\"\n        }\n\n        checked := \" \"\n        if _, ok := m.selected[i]; ok {\n            checked = \"x\"\n        }\n\n        s += fmt.Sprintf(\"%s [%s] %s\\n\", cursor, checked, choice)\n    }\n\n    s += \"\\nPress space to select, enter to confirm, q to quit.\\n\"\n\n    return s\n}\n\nfunc main() {\n    p := tea.NewProgram(initialModel())\n    if _, err := p.Run(); err != nil {\n        fmt.Printf(\"Error: %v\", err)\n        os.Exit(1)\n    }\n}\n```\n\n## Progress Indicators\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/schollz/progressbar/v3\"\n)\n\nfunc main() {\n    // Simple progress bar\n    bar := progressbar.Default(100, \"Downloading\")\n    for i := 0; i < 100; i++ {\n        bar.Add(1)\n        time.Sleep(40 * time.Millisecond)\n    }\n\n    // Custom progress bar\n    bar = progressbar.NewOptions(100,\n        progressbar.OptionEnableColorCodes(true),\n        progressbar.OptionShowBytes(true),\n        progressbar.OptionSetWidth(15),\n        progressbar.OptionSetDescription(\"[cyan][1/3][reset] Downloading...\"),\n        progressbar.OptionSetTheme(progressbar.Theme{\n            Saucer:        \"[green]=[reset]\",\n            SaucerHead:    \"[green]>[reset]\",\n            SaucerPadding: \" \",\n            BarStart:      \"[\",\n            BarEnd:        \"]\",\n        }),\n    )\n\n    for i := 0; i < 100; i++ {\n        bar.Add(1)\n        time.Sleep(40 * time.Millisecond)\n    }\n}\n```\n\n## Spinner\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/briandowns/spinner\"\n)\n\nfunc main() {\n    s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)\n    s.Suffix = \" Installing dependencies...\"\n    s.Start()\n\n    time.Sleep(4 * time.Second)\n\n    s.UpdateCharSet(spinner.CharSets[9])\n    s.Suffix = \" Processing...\"\n    time.Sleep(2 * time.Second)\n\n    s.Stop()\n    fmt.Println(\"✓ Done!\")\n}\n```\n\n## Colored Output\n\n```go\npackage main\n\nimport (\n    \"github.com/fatih/color\"\n)\n\nfunc main() {\n    // Basic colors\n    color.Blue(\"Info: Starting deployment...\")\n    color.Green(\"Success: Deployment complete!\")\n    color.Yellow(\"Warning: Deprecated flag used\")\n    color.Red(\"Error: Deployment failed\")\n\n    // Custom styles\n    success := color.New(color.FgGreen, color.Bold).PrintlnFunc()\n    error := color.New(color.FgRed, color.Bold).PrintlnFunc()\n\n    success(\"✓ Build successful\")\n    error(\"✗ Build failed\")\n\n    // Printf-style\n    color.Cyan(\"Processing %d files...\\n\", 42)\n\n    // Disable colors for CI\n    if os.Getenv(\"CI\") != \"\" {\n        color.NoColor = true\n    }\n}\n```\n\n## Error Handling\n\n```go\npackage main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"os\"\n    \"syscall\"\n\n    \"github.com/spf13/cobra\"\n)\n\nvar deployCmd = &cobra.Command{\n    Use:   \"deploy\",\n    Short: \"Deploy application\",\n    RunE: func(cmd *cobra.Command, args []string) error {\n        if err := deploy(); err != nil {\n            return handleError(err)\n        }\n        return nil\n    },\n}\n\nfunc handleError(err error) error {\n    var exitCode int\n\n    switch {\n    case errors.Is(err, os.ErrPermission):\n        fmt.Fprintln(os.Stderr, \"Permission denied\")\n        fmt.Fprintln(os.Stderr, \"Try running with sudo or check file permissions\")\n        exitCode = 77\n\n    case errors.Is(err, os.ErrNotExist):\n        fmt.Fprintf(os.Stderr, \"File not found: %v\\n\", err)\n        exitCode = 127\n\n    default:\n        fmt.Fprintf(os.Stderr, \"Deployment failed: %v\\n\", err)\n        if os.Getenv(\"DEBUG\") != \"\" {\n            fmt.Fprintf(os.Stderr, \"%+v\\n\", err)\n        }\n        exitCode = 1\n    }\n\n    os.Exit(exitCode)\n    return nil\n}\n\n// Handle SIGINT (Ctrl+C)\nfunc main() {\n    // Setup signal handling\n    c := make(chan os.Signal, 1)\n    signal.Notify(c, os.Interrupt, syscall.SIGTERM)\n\n    go func() {\n        <-c\n        fmt.Println(\"\\nOperation cancelled\")\n        os.Exit(130)\n    }()\n\n    cmd.Execute()\n}\n```\n\n## Testing\n\n```go\npackage cmd\n\nimport (\n    \"bytes\"\n    \"testing\"\n\n    \"github.com/spf13/cobra\"\n    \"github.com/stretchr/testify/assert\"\n)\n\nfunc TestInitCommand(t *testing.T) {\n    cmd := &cobra.Command{Use: \"test\"}\n    cmd.AddCommand(initCmd)\n\n    b := bytes.NewBufferString(\"\")\n    cmd.SetOut(b)\n    cmd.SetArgs([]string{\"init\", \"my-project\"})\n\n    err := cmd.Execute()\n    assert.NoError(t, err)\n    assert.Contains(t, b.String(), \"Creating my-project\")\n}\n\nfunc TestInitWithTemplate(t *testing.T) {\n    cmd := &cobra.Command{Use: \"test\"}\n    cmd.AddCommand(initCmd)\n\n    b := bytes.NewBufferString(\"\")\n    cmd.SetOut(b)\n    cmd.SetArgs([]string{\"init\", \"my-project\", \"--template\", \"react\"})\n\n    err := cmd.Execute()\n    assert.NoError(t, err)\n    assert.Contains(t, b.String(), \"react\")\n}\n```\n\n## Build & Distribution\n\n```makefile\n# Makefile\nVERSION := $(shell git describe --tags --always --dirty)\nLDFLAGS := -ldflags \"-X main.version=$(VERSION)\"\n\n.PHONY: build\nbuild:\n\tgo build $(LDFLAGS) -o bin/mycli main.go\n\n.PHONY: install\ninstall:\n\tgo install $(LDFLAGS)\n\n.PHONY: test\ntest:\n\tgo test -v ./...\n\n.PHONY: release\nrelease:\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-linux-amd64\n\tGOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-darwin-amd64\n\tGOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/mycli-darwin-arm64\n\tGOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-windows-amd64.exe\n```\n","skills/cli-developer/references/node-cli.md":"# Node.js CLI Development\n\n## Commander.js (Recommended)\n\nModern, elegant CLI framework with TypeScript support.\n\n```javascript\n#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { version } from './package.json';\n\nconst program = new Command();\n\nprogram\n  .name('mycli')\n  .description('My awesome CLI tool')\n  .version(version);\n\n// Simple command\nprogram\n  .command('init')\n  .description('Initialize a new project')\n  .option('-t, --template <type>', 'Project template', 'default')\n  .option('-f, --force', 'Overwrite existing files')\n  .action(async (options) => {\n    console.log(`Initializing with template: ${options.template}`);\n  });\n\n// Command with arguments\nprogram\n  .command('deploy <environment>')\n  .description('Deploy to environment')\n  .option('--dry-run', 'Preview without executing')\n  .action(async (environment, options) => {\n    if (options.dryRun) {\n      console.log(`Would deploy to: ${environment}`);\n    } else {\n      await deploy(environment);\n    }\n  });\n\n// Nested subcommands\nconst config = program.command('config').description('Manage configuration');\n\nconfig\n  .command('get <key>')\n  .description('Get config value')\n  .action((key) => console.log(getConfig(key)));\n\nconfig\n  .command('set <key> <value>')\n  .description('Set config value')\n  .action((key, value) => setConfig(key, value));\n\nprogram.parse();\n```\n\n## Yargs (Alternative)\n\nPowerful argument parsing with middleware support.\n\n```javascript\n#!/usr/bin/env node\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\n\nyargs(hideBin(process.argv))\n  .command(\n    'deploy <env>',\n    'Deploy to environment',\n    (yargs) => {\n      return yargs\n        .positional('env', {\n          describe: 'Environment name',\n          choices: ['dev', 'staging', 'prod'],\n        })\n        .option('force', {\n          alias: 'f',\n          type: 'boolean',\n          description: 'Force deployment',\n        });\n    },\n    async (argv) => {\n      await deploy(argv.env, { force: argv.force });\n    }\n  )\n  .middleware([(argv) => {\n    // Validate before all commands\n    if (!isConfigValid()) {\n      throw new Error('Invalid config');\n    }\n  }])\n  .demandCommand()\n  .help()\n  .parse();\n```\n\n## Interactive Prompts (Inquirer)\n\nBeautiful interactive prompts for user input.\n\n```javascript\nimport inquirer from 'inquirer';\n\n// Text input\nconst { name } = await inquirer.prompt([\n  {\n    type: 'input',\n    name: 'name',\n    message: 'Project name:',\n    default: 'my-project',\n    validate: (input) => input.length > 0 || 'Name required',\n  },\n]);\n\n// Select from list\nconst { environment } = await inquirer.prompt([\n  {\n    type: 'list',\n    name: 'environment',\n    message: 'Select environment:',\n    choices: ['development', 'staging', 'production'],\n    default: 'development',\n  },\n]);\n\n// Checkbox (multi-select)\nconst { features } = await inquirer.prompt([\n  {\n    type: 'checkbox',\n    name: 'features',\n    message: 'Select features:',\n    choices: [\n      { name: 'TypeScript', checked: true },\n      { name: 'ESLint', checked: true },\n      { name: 'Prettier', checked: true },\n      { name: 'Jest', checked: false },\n    ],\n  },\n]);\n\n// Confirmation\nconst { confirmed } = await inquirer.prompt([\n  {\n    type: 'confirm',\n    name: 'confirmed',\n    message: 'Deploy to production?',\n    default: false,\n  },\n]);\n\n// Password\nconst { password } = await inquirer.prompt([\n  {\n    type: 'password',\n    name: 'password',\n    message: 'Enter password:',\n    mask: '*',\n  },\n]);\n```\n\n## Terminal Output (Chalk)\n\nColorful terminal output with proper TTY detection.\n\n```javascript\nimport chalk from 'chalk';\n\n// Basic colors\nconsole.log(chalk.blue('Info: ') + 'Starting deployment...');\nconsole.log(chalk.green('Success: ') + 'Deployment complete');\nconsole.log(chalk.yellow('Warning: ') + 'Deprecated flag used');\nconsole.log(chalk.red('Error: ') + 'Deployment failed');\n\n// Styles\nconsole.log(chalk.bold.underline('Important'));\nconsole.log(chalk.dim('Less important'));\n\n// Templates\nconst success = chalk.green.bold;\nconst error = chalk.red.bold;\nconsole.log(success('✓') + ' Build successful');\nconsole.log(error('✗') + ' Build failed');\n\n// Disable colors for CI\nconst log = {\n  info: (msg) => console.log(chalk.blue('ℹ'), msg),\n  success: (msg) => console.log(chalk.green('✔'), msg),\n  warn: (msg) => console.log(chalk.yellow('⚠'), msg),\n  error: (msg) => console.log(chalk.red('✖'), msg),\n};\n\n// Auto-detects TTY and CI environments\n```\n\n## Progress Indicators (Ora)\n\nElegant terminal spinners and progress indicators.\n\n```javascript\nimport ora from 'ora';\n\n// Simple spinner\nconst spinner = ora('Loading...').start();\nawait doWork();\nspinner.succeed('Done!');\n\n// Update text\nconst spinner = ora('Starting...').start();\nspinner.text = 'Processing...';\nawait process();\nspinner.text = 'Finalizing...';\nawait finalize();\nspinner.succeed('Complete!');\n\n// Different states\nspinner.start('Installing dependencies...');\n// ... work\nspinner.succeed('Dependencies installed');\n// or\nspinner.fail('Installation failed');\n// or\nspinner.warn('Some packages skipped');\n// or\nspinner.info('Using cached packages');\n\n// Multiple spinners\nconst spinners = {\n  api: ora('Deploying API...').start(),\n  web: ora('Deploying web app...').start(),\n  db: ora('Running migrations...').start(),\n};\n\nawait Promise.all([\n  deployApi().then(() => spinners.api.succeed()),\n  deployWeb().then(() => spinners.web.succeed()),\n  runMigrations().then(() => spinners.db.succeed()),\n]);\n```\n\n## Progress Bars (cli-progress)\n\n```javascript\nimport cliProgress from 'cli-progress';\n\n// Single progress bar\nconst bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\nbar.start(100, 0);\n\nfor (let i = 0; i <= 100; i++) {\n  await processItem(i);\n  bar.update(i);\n}\n\nbar.stop();\n\n// Multi-progress\nconst multibar = new cliProgress.MultiBar({\n  clearOnComplete: false,\n  hideCursor: true,\n});\n\nconst bar1 = multibar.create(100, 0, { task: 'API' });\nconst bar2 = multibar.create(100, 0, { task: 'Web' });\n\nawait Promise.all([\n  processApi(bar1),\n  processWeb(bar2),\n]);\n\nmultibar.stop();\n```\n\n## File System Helpers\n\n```javascript\nimport fs from 'fs-extra';\nimport { globby } from 'globby';\nimport path from 'path';\n\n// Copy with template\nawait fs.copy('templates/app', targetDir, {\n  filter: (src) => !src.includes('node_modules'),\n});\n\n// Read/write JSON\nconst config = await fs.readJson('config.json');\nawait fs.writeJson('output.json', data, { spaces: 2 });\n\n// Ensure directory exists\nawait fs.ensureDir('dist/assets');\n\n// Find files\nconst files = await globby(['src/**/*.ts', '!src/**/*.test.ts']);\n```\n\n## Error Handling\n\n```javascript\nimport { Command } from 'commander';\n\nprogram\n  .command('deploy')\n  .action(async () => {\n    try {\n      await deploy();\n    } catch (error) {\n      if (error.code === 'EACCES') {\n        console.error(chalk.red('Permission denied'));\n        console.error('Try running with sudo or check file permissions');\n        process.exit(77);\n      } else if (error.code === 'ENOENT') {\n        console.error(chalk.red('File not found:'), error.path);\n        process.exit(127);\n      } else {\n        console.error(chalk.red('Deployment failed:'), error.message);\n        if (process.env.DEBUG) {\n          console.error(error.stack);\n        }\n        process.exit(1);\n      }\n    }\n  });\n\n// Handle SIGINT (Ctrl+C)\nprocess.on('SIGINT', () => {\n  console.log('\\nOperation cancelled');\n  process.exit(130);\n});\n```\n\n## Package.json Setup\n\n```json\n{\n  \"name\": \"mycli\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"bin\": {\n    \"mycli\": \"./bin/cli.js\"\n  },\n  \"files\": [\n    \"bin/\",\n    \"lib/\",\n    \"templates/\"\n  ],\n  \"engines\": {\n    \"node\": \">=18.0.0\"\n  },\n  \"dependencies\": {\n    \"commander\": \"^11.0.0\",\n    \"inquirer\": \"^9.0.0\",\n    \"chalk\": \"^5.0.0\",\n    \"ora\": \"^7.0.0\"\n  }\n}\n```\n\n## Testing CLIs\n\n```javascript\nimport { execaCommand } from 'execa';\nimport { describe, it, expect } from 'vitest';\n\ndescribe('mycli', () => {\n  it('shows version', async () => {\n    const { stdout } = await execaCommand('node bin/cli.js --version');\n    expect(stdout).toMatch(/\\d+\\.\\d+\\.\\d+/);\n  });\n\n  it('shows help', async () => {\n    const { stdout } = await execaCommand('node bin/cli.js --help');\n    expect(stdout).toContain('Usage:');\n  });\n\n  it('handles invalid command', async () => {\n    await expect(\n      execaCommand('node bin/cli.js invalid')\n    ).rejects.toThrow();\n  });\n});\n```\n","skills/cli-developer/references/python-cli.md":"# Python CLI Development\n\n## Typer (Recommended - Modern)\n\nFastAPI-style CLI framework with automatic help generation.\n\n```python\n#!/usr/bin/env python3\nimport typer\nfrom typing import Optional\nfrom enum import Enum\n\napp = typer.Typer()\n\nclass Environment(str, Enum):\n    dev = \"development\"\n    staging = \"staging\"\n    prod = \"production\"\n\n@app.command()\ndef init(\n    name: str = typer.Argument(..., help=\"Project name\"),\n    template: str = typer.Option(\"default\", help=\"Project template\"),\n    force: bool = typer.Option(False, \"--force\", \"-f\", help=\"Overwrite existing\"),\n):\n    \"\"\"Initialize a new project\"\"\"\n    typer.echo(f\"Creating {name} from {template}\")\n    if force:\n        typer.echo(\"Force mode enabled\")\n\n@app.command()\ndef deploy(\n    environment: Environment = typer.Argument(..., help=\"Target environment\"),\n    dry_run: bool = typer.Option(False, \"--dry-run\", help=\"Preview only\"),\n    config: Optional[typer.FileText] = typer.Option(None, help=\"Config file\"),\n):\n    \"\"\"Deploy to environment\"\"\"\n    if dry_run:\n        typer.echo(f\"Would deploy to: {environment.value}\")\n    else:\n        typer.echo(f\"Deploying to {environment.value}...\")\n\n# Nested commands\nconfig_app = typer.Typer()\napp.add_typer(config_app, name=\"config\", help=\"Manage configuration\")\n\n@config_app.command(\"get\")\ndef config_get(key: str):\n    \"\"\"Get config value\"\"\"\n    typer.echo(f\"Value: {get_config(key)}\")\n\n@config_app.command(\"set\")\ndef config_set(key: str, value: str):\n    \"\"\"Set config value\"\"\"\n    set_config(key, value)\n    typer.echo(f\"Set {key} = {value}\")\n\nif __name__ == \"__main__\":\n    app()\n```\n\n## Click (Widely Used)\n\nPowerful, composable CLI framework.\n\n```python\nimport click\n\n@click.group()\n@click.version_option()\ndef cli():\n    \"\"\"My awesome CLI tool\"\"\"\n    pass\n\n@cli.command()\n@click.argument('name')\n@click.option('--template', default='default', help='Project template')\n@click.option('--force', '-f', is_flag=True, help='Overwrite existing')\ndef init(name, template, force):\n    \"\"\"Initialize a new project\"\"\"\n    click.echo(f\"Creating {name} from {template}\")\n\n@cli.command()\n@click.argument('environment', type=click.Choice(['dev', 'staging', 'prod']))\n@click.option('--dry-run', is_flag=True, help='Preview only')\n@click.option('--config', type=click.File('r'), help='Config file')\ndef deploy(environment, dry_run, config):\n    \"\"\"Deploy to environment\"\"\"\n    if dry_run:\n        click.secho(f\"Would deploy to: {environment}\", fg='yellow')\n    else:\n        click.secho(f\"Deploying to {environment}...\", fg='green')\n\n# Nested groups\n@cli.group()\ndef config():\n    \"\"\"Manage configuration\"\"\"\n    pass\n\n@config.command('get')\n@click.argument('key')\ndef config_get(key):\n    \"\"\"Get config value\"\"\"\n    click.echo(get_config(key))\n\n@config.command('set')\n@click.argument('key')\n@click.argument('value')\ndef config_set(key, value):\n    \"\"\"Set config value\"\"\"\n    set_config(key, value)\n\nif __name__ == '__main__':\n    cli()\n```\n\n## Rich Terminal Output\n\nBeautiful terminal formatting and progress indicators.\n\n```python\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.progress import Progress, SpinnerColumn, TextColumn\nfrom rich.panel import Panel\nfrom rich.syntax import Syntax\nfrom rich import print as rprint\n\nconsole = Console()\n\n# Styled output\nconsole.print(\"[bold blue]Info:[/] Starting deployment...\")\nconsole.print(\"[bold green]Success:[/] Deployment complete!\")\nconsole.print(\"[bold yellow]Warning:[/] Deprecated flag used\")\nconsole.print(\"[bold red]Error:[/] Deployment failed\")\n\n# Tables\ntable = Table(title=\"Deployments\")\ntable.add_column(\"Environment\", style=\"cyan\")\ntable.add_column(\"Status\", style=\"magenta\")\ntable.add_column(\"Time\", style=\"green\")\n\ntable.add_row(\"Production\", \"✓ Success\", \"2m 34s\")\ntable.add_row(\"Staging\", \"✗ Failed\", \"1m 12s\")\nconsole.print(table)\n\n# Panels\nconsole.print(Panel.fit(\n    \"Deploy to production?\",\n    title=\"Confirmation\",\n    border_style=\"red\"\n))\n\n# Syntax highlighting\ncode = '''\ndef deploy(env: str):\n    print(f\"Deploying to {env}\")\n'''\nconsole.print(Syntax(code, \"python\", theme=\"monokai\"))\n\n# Progress bars\nwith Progress() as progress:\n    task = progress.add_task(\"[cyan]Deploying...\", total=100)\n    for i in range(100):\n        do_work()\n        progress.update(task, advance=1)\n\n# Spinners\nwith Progress(\n    SpinnerColumn(),\n    TextColumn(\"[progress.description]{task.description}\"),\n) as progress:\n    task = progress.add_task(\"Installing dependencies...\")\n    install_dependencies()\n```\n\n## Interactive Prompts (questionary)\n\n```python\nimport questionary\n\n# Text input\nname = questionary.text(\n    \"Project name:\",\n    default=\"my-project\",\n    validate=lambda x: len(x) > 0 or \"Name required\"\n).ask()\n\n# Select from list\nenvironment = questionary.select(\n    \"Select environment:\",\n    choices=[\"development\", \"staging\", \"production\"],\n    default=\"development\"\n).ask()\n\n# Checkbox (multi-select)\nfeatures = questionary.checkbox(\n    \"Select features:\",\n    choices=[\n        questionary.Choice(\"TypeScript\", checked=True),\n        questionary.Choice(\"ESLint\", checked=True),\n        questionary.Choice(\"Prettier\", checked=True),\n        questionary.Choice(\"Jest\", checked=False),\n    ]\n).ask()\n\n# Confirmation\nconfirmed = questionary.confirm(\n    \"Deploy to production?\",\n    default=False\n).ask()\n\nif confirmed:\n    deploy()\n\n# Password\npassword = questionary.password(\"Enter password:\").ask()\n```\n\n## Argparse (Standard Library)\n\nBuilt-in argument parsing (verbose but no dependencies).\n\n```python\nimport argparse\nimport sys\n\ndef main():\n    parser = argparse.ArgumentParser(\n        prog='mycli',\n        description='My awesome CLI tool',\n    )\n    parser.add_argument('--version', action='version', version='1.0.0')\n\n    subparsers = parser.add_subparsers(dest='command', required=True)\n\n    # Init command\n    init_parser = subparsers.add_parser('init', help='Initialize project')\n    init_parser.add_argument('name', help='Project name')\n    init_parser.add_argument('--template', default='default', help='Template')\n    init_parser.add_argument('-f', '--force', action='store_true')\n\n    # Deploy command\n    deploy_parser = subparsers.add_parser('deploy', help='Deploy')\n    deploy_parser.add_argument(\n        'environment',\n        choices=['dev', 'staging', 'prod'],\n        help='Target environment'\n    )\n    deploy_parser.add_argument('--dry-run', action='store_true')\n    deploy_parser.add_argument('--config', type=argparse.FileType('r'))\n\n    args = parser.parse_args()\n\n    if args.command == 'init':\n        init(args.name, args.template, args.force)\n    elif args.command == 'deploy':\n        deploy(args.environment, args.dry_run, args.config)\n\nif __name__ == '__main__':\n    main()\n```\n\n## Error Handling\n\n```python\nimport typer\nimport sys\nfrom pathlib import Path\n\napp = typer.Typer()\n\n@app.command()\ndef deploy():\n    try:\n        perform_deploy()\n    except PermissionError as e:\n        typer.secho(\"Permission denied\", fg=typer.colors.RED, err=True)\n        typer.echo(\"Try running with sudo or check file permissions\")\n        raise typer.Exit(code=77)\n    except FileNotFoundError as e:\n        typer.secho(f\"File not found: {e.filename}\", fg=typer.colors.RED, err=True)\n        raise typer.Exit(code=127)\n    except Exception as e:\n        typer.secho(f\"Deployment failed: {e}\", fg=typer.colors.RED, err=True)\n        if os.getenv('DEBUG'):\n            import traceback\n            traceback.print_exc()\n        raise typer.Exit(code=1)\n\n# Handle KeyboardInterrupt (Ctrl+C)\ndef main():\n    try:\n        app()\n    except KeyboardInterrupt:\n        typer.echo(\"\\nOperation cancelled\")\n        sys.exit(130)\n\nif __name__ == \"__main__\":\n    main()\n```\n\n## Configuration Management\n\n```python\nfrom pathlib import Path\nfrom typing import Any\nimport json\nimport os\n\nclass Config:\n    def __init__(self):\n        self.config_paths = [\n            Path(\"/etc/mycli/config.json\"),          # System\n            Path.home() / \".config\" / \"mycli\" / \"config.json\",  # User\n            Path.cwd() / \"mycli.json\",               # Project\n        ]\n\n    def load(self) -> dict[str, Any]:\n        config = self._defaults()\n\n        # Load from files (lowest to highest priority)\n        for path in self.config_paths:\n            if path.exists():\n                with path.open() as f:\n                    config.update(json.load(f))\n\n        # Override with environment variables\n        for key in config.keys():\n            env_var = f\"MYCLI_{key.upper()}\"\n            if env_var in os.environ:\n                config[key] = os.environ[env_var]\n\n        return config\n\n    def _defaults(self) -> dict[str, Any]:\n        return {\n            \"environment\": \"development\",\n            \"verbose\": False,\n            \"timeout\": 30,\n        }\n```\n\n## Setup.py / pyproject.toml\n\n```toml\n# pyproject.toml\n[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"mycli\"\nversion = \"1.0.0\"\ndescription = \"My awesome CLI tool\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"typer[all]>=0.9.0\",\n    \"rich>=13.0.0\",\n    \"questionary>=2.0.0\",\n]\n\n[project.scripts]\nmycli = \"mycli.cli:main\"\n\n[project.optional-dependencies]\ndev = [\n    \"pytest>=7.0.0\",\n    \"pytest-cov>=4.0.0\",\n]\n```\n\n## Testing CLIs\n\n```python\nfrom typer.testing import CliRunner\nfrom mycli.cli import app\n\nrunner = CliRunner()\n\ndef test_version():\n    result = runner.invoke(app, [\"--version\"])\n    assert result.exit_code == 0\n    assert \"1.0.0\" in result.stdout\n\ndef test_init():\n    result = runner.invoke(app, [\"init\", \"my-project\"])\n    assert result.exit_code == 0\n    assert \"Creating my-project\" in result.stdout\n\ndef test_init_with_template():\n    result = runner.invoke(app, [\"init\", \"my-project\", \"--template\", \"react\"])\n    assert result.exit_code == 0\n    assert \"react\" in result.stdout\n\ndef test_invalid_command():\n    result = runner.invoke(app, [\"invalid\"])\n    assert result.exit_code != 0\n```\n\n## Progress Bars (tqdm)\n\n```python\nfrom tqdm import tqdm\nimport time\n\n# Simple progress bar\nfor i in tqdm(range(100), desc=\"Processing\"):\n    process_item(i)\n\n# Custom format\nwith tqdm(total=100, desc=\"Downloading\", unit=\"MB\") as pbar:\n    for chunk in download_chunks():\n        pbar.update(len(chunk))\n\n# Multiple progress bars\nfrom tqdm import trange\n\nfor epoch in trange(10, desc=\"Epochs\"):\n    for batch in trange(100, desc=\"Batches\", leave=False):\n        train_batch(batch)\n```\n","skills/cli-developer/references/ux-patterns.md":"# CLI UX Patterns\n\n## Progress Indicators\n\n### When to Use What\n\n```\nDeterminate (known total):\n  [████████████░░░░░░░░] 60% (3/5 files)\n  Use: File operations, downloads, batch processing\n\nIndeterminate (unknown duration):\n  ⠋ Loading...\n  Use: API calls, database queries, waiting for external services\n\nMulti-step:\n  ✓ Dependencies installed\n  ⠋ Building application...\n  ⏳ Running tests...\n  Use: Multi-phase operations (build, deploy, etc.)\n```\n\n### Progress Bar Best Practices\n\n```\nGood:\n[████████████░░░░░░░░] 60% | 120/200 MB | 2.4 MB/s | ETA: 33s\n↑ Visual     ↑ Percent  ↑ Progress  ↑ Rate     ↑ Time\n\nComponents:\n- Visual bar (20-40 chars)\n- Percentage (when known)\n- Current/total (with units)\n- Speed/rate (when applicable)\n- ETA (estimated time remaining)\n\nBad:\nProcessing... (no feedback)\n60% (no context)\n[████████████████████████████████████████] (too wide)\n```\n\n### Spinner Styles\n\n```\n⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏   Dots (elegant, low-key)\n⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷        Blocks (bold, attention)\n◐ ◓ ◑ ◒                  Circle (classic)\n▖ ▘ ▝ ▗                  Corners (minimal)\n⠁ ⠂ ⠄ ⡀ ⢀ ⠠ ⠐ ⠈        Line (subtle)\n\nChoose based on:\n- Terminal compatibility (stick to ASCII for Windows)\n- Branding (match your tool's personality)\n- Context (subtle for background, bold for main task)\n```\n\n## Color Usage\n\n### Semantic Colors\n\n```\nRed:     Errors, failures, destructive actions\nYellow:  Warnings, deprecations, non-critical issues\nGreen:   Success, completion, positive feedback\nBlue:    Information, hints, neutral messages\nCyan:    Commands, code, technical details\nMagenta: Highlights, special items\nGray:    Less important, metadata, timestamps\n\nExamples:\n✓ Success: Deployment complete\n✗ Error: File not found\n⚠ Warning: Deprecated flag --old-flag\nℹ Info: Using cache from ~/.mycli/cache\n```\n\n### When to Disable Colors\n\n```javascript\n// Detect non-TTY output (piped to file, etc.)\nconst noColor = !process.stdout.isTTY ||\n                process.env.NO_COLOR ||\n                process.env.CI === 'true';\n\nif (noColor) {\n  // Disable colors\n}\n\n// Support NO_COLOR standard\n// https://no-color.org/\n```\n\n### Color Accessibility\n\n```\n- Don't rely on color alone (use symbols too)\n- Provide high contrast (test with various terminals)\n- Support color blindness (red/green alternatives)\n\nGood:\n✓ Build successful (green)\n✗ Build failed (red)\n↑ Symbols work without color\n\nBad:\nSuccess (only color, no symbol)\nFailed (only color, no symbol)\n```\n\n## Help Text Design\n\n### Command Help Structure\n\n```\nUSAGE\n  mycli <command> [options]\n\nCOMMANDS\n  init         Initialize a new project\n  deploy       Deploy to environment\n  config       Manage configuration\n  plugins      Manage plugins\n\nOPTIONS\n  -h, --help     Show help\n  -v, --version  Show version\n  --config FILE  Config file path\n\nRun 'mycli <command> --help' for more information on a command.\n\nEXAMPLES\n  # Initialize a new project\n  mycli init my-app\n\n  # Deploy to production\n  mycli deploy production --dry-run\n\nLearn more: https://docs.mycli.dev\n```\n\n### Subcommand Help\n\n```\nUSAGE\n  mycli deploy <environment> [options]\n\nARGUMENTS\n  environment    Target environment (required)\n                 Values: development, staging, production\n\nOPTIONS\n  -c, --config <file>    Config file path\n                         Default: ./mycli.config.yml\n\n  -f, --force            Skip confirmation prompts\n                         Use with caution in production\n\n  -d, --dry-run          Preview changes without executing\n                         Shows what would happen\n\n  -v, --verbose          Show detailed output\n                         Includes debug information\n\nEXAMPLES\n  # Deploy to production (with confirmation)\n  mycli deploy production\n\n  # Preview staging deployment\n  mycli deploy staging --dry-run\n\n  # Use custom config\n  mycli deploy production --config ./prod.yml\n\n  # Force deploy without prompts\n  mycli deploy production --force\n\nFor more information, visit https://docs.mycli.dev/deploy\n```\n\n## Error Messages\n\n### Good Error Messages\n\n```\nPattern: [Context] → [Problem] → [Solution]\n\nExample 1: File not found\n✗ Error: Config file not found\n\nSearched locations:\n  • ./mycli.config.yml\n  • ~/.config/mycli/config.yml\n  • /etc/mycli/config.yml\n\nSolutions:\n  • Run 'mycli init' to create a config file\n  • Use --config to specify a different location\n  • Check file permissions\n\nExample 2: Validation error\n✗ Error: Invalid environment 'prod'\n\nExpected one of:\n  • development\n  • staging\n  • production\n\nDid you mean 'production'?\n\nExample 3: Permission error\n✗ Error: Permission denied writing to /etc/mycli/config.yml\n\nThis operation requires elevated permissions.\n\nTry:\n  • Run with sudo: sudo mycli config set key value\n  • Use user config: mycli config set --user key value\n  • Check file permissions: ls -la /etc/mycli/config.yml\n```\n\n### Error Message Guidelines\n\n```\nDO:\n✓ Be specific (\"Port 3000 already in use\" not \"Port unavailable\")\n✓ Show context (\"in file config.yml, line 42\")\n✓ Suggest solutions (\"Try running 'mycli fix'\")\n✓ Use plain language (\"File not found\" not \"ENOENT\")\n\nDON'T:\n✗ Show stack traces to users (save for --debug)\n✗ Use jargon (\"EACCES: permission denied\")\n✗ Leave users stuck (\"Invalid input\" with no explanation)\n✗ Be vague (\"Something went wrong\")\n```\n\n## Interactive Prompts\n\n### Prompt Types\n\n```\nText Input:\n  Project name: my-awesome-app\n  ↑ Clear label\n\nSelect (Single Choice):\n  ? Select environment: (Use arrow keys)\n  ❯ development\n    staging\n    production\n\nCheckbox (Multiple Choice):\n  ? Select features: (Press space to select, enter to confirm)\n  ◉ TypeScript\n  ◯ ESLint\n  ◉ Prettier\n  ◯ Jest\n\nConfirmation:\n  ? Deploy to production? (y/N)\n  ↑ Default is No (safer)\n\nPassword:\n  ? Enter password: ********\n  ↑ Masked input\n```\n\n### Prompt Guidelines\n\n```\nDO:\n✓ Show keyboard hints (\"Use arrow keys\", \"Press space\")\n✓ Provide sensible defaults (pre-select common choices)\n✓ Allow skipping with Ctrl+C\n✓ Validate input immediately\n✓ Show preview/summary before final action\n\nDON'T:\n✗ Require interaction in CI/CD environments\n✗ Ask obvious questions (confirm every action)\n✗ Hide what will happen next\n✗ Make users repeat information\n```\n\n## Output Formatting\n\n### Tables\n\n```\nGood:\n┌─────────────┬──────────┬──────────┐\n│ Environment │ Status   │ Updated  │\n├─────────────┼──────────┼──────────┤\n│ production  │ ✓ Active │ 2h ago   │\n│ staging     │ ✓ Active │ 5m ago   │\n│ development │ ✗ Down   │ 1d ago   │\n└─────────────┴──────────┴──────────┘\n\nMinimal (for scripting):\nEnvironment  Status  Updated\nproduction   Active  2h ago\nstaging      Active  5m ago\ndevelopment  Down    1d ago\n\nJSON (for programmatic use):\n[\n  {\"env\": \"production\", \"status\": \"active\", \"updated\": \"2h ago\"},\n  {\"env\": \"staging\", \"status\": \"active\", \"updated\": \"5m ago\"}\n]\n```\n\n### Lists\n\n```\nBulleted:\nFeatures:\n  • TypeScript support\n  • Hot reload\n  • Auto-formatting\n\nNumbered:\nSteps to deploy:\n  1. Build application\n  2. Run tests\n  3. Deploy to server\n  4. Verify deployment\n\nTree:\nmy-app/\n├── src/\n│   ├── components/\n│   └── utils/\n├── tests/\n└── package.json\n```\n\n## Status Messages\n\n### Real-time Updates\n\n```\nMulti-step process:\n✓ Dependencies installed (2.3s)\n✓ Application built (8.1s)\n⠋ Running tests... (current)\n⏳ Deploying... (pending)\n⏳ Verifying... (pending)\n\nUpdates:\n⠋ Installing dependencies...\n  → npm install\n✓ Dependencies installed (2.3s)\n\n⠋ Building application...\n  → webpack build\n✓ Application built (8.1s)\n  → Output: dist/ (2.4 MB)\n```\n\n### Summary/Completion\n\n```\n✓ Deployment complete!\n\nSummary:\n  Environment:  production\n  Version:      v1.2.3\n  Duration:     2m 34s\n  Deployed:     2023-12-14 10:30:45 UTC\n\nNext steps:\n  • View logs: mycli logs production\n  • Monitor:   mycli status production\n  • Rollback:  mycli rollback production\n\nURL: https://app.example.com\n```\n\n## Debugging & Verbose Mode\n\n```\nNormal mode (default):\n✓ Deployed to production (2m 34s)\n\nVerbose mode (--verbose):\n[10:30:12] Starting deployment...\n[10:30:13] Loading config from ./mycli.config.yml\n[10:30:14] Connecting to production server...\n[10:30:15] Uploading files (124 files, 2.4 MB)...\n[10:30:28] Running post-deploy hooks...\n[10:32:46] ✓ Deployment complete\n\nDebug mode (--debug):\n[DEBUG] Config loaded: {env: 'production', ...}\n[DEBUG] SSH connection established: user@host\n[DEBUG] Executing: rsync -avz ./dist/ user@host:/var/www/\n[DEBUG] Output: sending incremental file list...\n[DEBUG] Exit code: 0\n✓ Deployed to production (2m 34s)\n\nUsage:\n# Normal: concise output\nmycli deploy production\n\n# Verbose: detailed steps\nmycli deploy production --verbose\n\n# Debug: everything including internals\nDEBUG=* mycli deploy production\n```\n\n## Man Page Format\n\n```\nNAME\n    mycli-deploy - Deploy application to environment\n\nSYNOPSIS\n    mycli deploy <environment> [options]\n\nDESCRIPTION\n    Deploy your application to the specified environment.\n    Supports development, staging, and production environments.\n\nOPTIONS\n    -c, --config <file>\n        Path to configuration file\n        Default: ./mycli.config.yml\n\n    -f, --force\n        Skip all confirmation prompts\n        Use with caution in production\n\n    -d, --dry-run\n        Preview deployment without executing\n        Shows what would be deployed\n\nEXAMPLES\n    Deploy to production:\n        mycli deploy production\n\n    Preview staging deployment:\n        mycli deploy staging --dry-run\n\nSEE ALSO\n    mycli-init(1), mycli-config(1), mycli-rollback(1)\n```\n","skills/cloud-architect/SKILL.md":"---\nname: cloud-architect\ndescription: Designs cloud architectures, creates migration plans, generates cost optimization recommendations, and produces disaster recovery strategies across AWS, Azure, and GCP. Use when designing cloud architectures, planning migrations, or optimizing multi-cloud deployments. Invoke for Well-Architected Framework, cost optimization, disaster recovery, landing zones, security architecture, serverless design.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: infrastructure\n  triggers: AWS, Azure, GCP, Google Cloud, cloud migration, cloud architecture, multi-cloud, cloud cost, Well-Architected, landing zone, cloud security, disaster recovery, cloud native, serverless architecture\n  role: architect\n  scope: infrastructure\n  output-format: architecture\n  related-skills: devops-engineer, kubernetes-specialist, terraform-engineer, security-reviewer, microservices-architect, monitoring-expert\n---\n\n# Cloud Architect\n\n## Core Workflow\n\n1. **Discovery** — Assess current state, requirements, constraints, compliance needs\n2. **Design** — Select services, design topology, plan data architecture\n3. **Security** — Implement zero-trust, identity federation, encryption\n4. **Cost Model** — Right-size resources, reserved capacity, auto-scaling\n5. **Migration** — Apply 6Rs framework, define waves, validate connectivity before cutover\n6. **Operate** — Set up monitoring, automation, continuous optimization\n\n### Workflow Validation Checkpoints\n\n**After Design:** Confirm every component has a redundancy strategy and no single points of failure exist in the topology.\n\n**Before Migration cutover:** Validate VPC peering or connectivity is fully established:\n```bash\n# AWS: confirm peering connection is Active before proceeding\naws ec2 describe-vpc-peering-connections \\\n  --filters \"Name=status-code,Values=active\"\n\n# Azure: confirm VNet peering state\naz network vnet peering list \\\n  --resource-group myRG --vnet-name myVNet \\\n  --query \"[].{Name:name,State:peeringState}\"\n```\n\n**After Migration:** Verify application health and routing:\n```bash\n# AWS: check target group health in ALB\naws elbv2 describe-target-health \\\n  --target-group-arn arn:aws:elasticloadbalancing:...\n```\n\n**After DR test:** Confirm RTO/RPO targets were met; document actual recovery times.\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| AWS Services | `references/aws.md` | EC2, S3, Lambda, RDS, Well-Architected Framework |\n| Azure Services | `references/azure.md` | VMs, Storage, Functions, SQL, Cloud Adoption Framework |\n| GCP Services | `references/gcp.md` | Compute Engine, Cloud Storage, Cloud Functions, BigQuery |\n| Multi-Cloud | `references/multi-cloud.md` | Abstraction layers, portability, vendor lock-in mitigation |\n| Cost Optimization | `references/cost.md` | Reserved instances, spot, right-sizing, FinOps practices |\n\n## Constraints\n\n### MUST DO\n- Design for high availability (99.9%+)\n- Implement security by design (zero-trust)\n- Use infrastructure as code (Terraform, CloudFormation)\n- Enable cost allocation tags and monitoring\n- Plan disaster recovery with defined RTO/RPO\n- Implement multi-region for critical workloads\n- Use managed services when possible\n- Document architectural decisions\n\n### MUST NOT DO\n- Store credentials in code or public repos\n- Skip encryption (at rest and in transit)\n- Create single points of failure\n- Ignore cost optimization opportunities\n- Deploy without proper monitoring\n- Use overly complex architectures\n- Ignore compliance requirements\n- Skip disaster recovery testing\n\n## Common Patterns with Examples\n\n### Least-Privilege IAM (Zero-Trust)\n\nRather than broad policies, scope permissions to specific resources and actions:\n\n```bash\n# AWS: create a scoped role for an application\naws iam create-role \\\n  --role-name AppRole \\\n  --assume-role-policy-document file://trust-policy.json\n\naws iam put-role-policy \\\n  --role-name AppRole \\\n  --policy-name AppInlinePolicy \\\n  --policy-document '{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\"],\n      \"Resource\": \"arn:aws:s3:::my-app-bucket/*\"\n    }]\n  }'\n```\n\n```hcl\n# Terraform equivalent\nresource \"aws_iam_role\" \"app_role\" {\n  name               = \"AppRole\"\n  assume_role_policy = data.aws_iam_policy_document.trust.json\n}\n\nresource \"aws_iam_role_policy\" \"app_policy\" {\n  role = aws_iam_role.app_role.id\n  policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [{\n      Effect   = \"Allow\"\n      Action   = [\"s3:GetObject\", \"s3:PutObject\"]\n      Resource = \"${aws_s3_bucket.app.arn}/*\"\n    }]\n  })\n}\n```\n\n### VPC with Public/Private Subnets (Terraform)\n\n```hcl\nresource \"aws_vpc\" \"main\" {\n  cidr_block           = \"10.0.0.0/16\"\n  enable_dns_hostnames = true\n  tags = { Name = \"main\", CostCenter = var.cost_center }\n}\n\nresource \"aws_subnet\" \"private\" {\n  count             = 2\n  vpc_id            = aws_vpc.main.id\n  cidr_block        = cidrsubnet(\"10.0.0.0/16\", 8, count.index)\n  availability_zone = data.aws_availability_zones.available.names[count.index]\n}\n\nresource \"aws_subnet\" \"public\" {\n  count                   = 2\n  vpc_id                  = aws_vpc.main.id\n  cidr_block              = cidrsubnet(\"10.0.0.0/16\", 8, count.index + 10)\n  availability_zone       = data.aws_availability_zones.available.names[count.index]\n  map_public_ip_on_launch = true\n}\n```\n\n### Auto-Scaling Group (Terraform)\n\n```hcl\nresource \"aws_autoscaling_group\" \"app\" {\n  desired_capacity    = 2\n  min_size            = 1\n  max_size            = 10\n  vpc_zone_identifier = aws_subnet.private[*].id\n\n  launch_template {\n    id      = aws_launch_template.app.id\n    version = \"$Latest\"\n  }\n\n  tag {\n    key                 = \"CostCenter\"\n    value               = var.cost_center\n    propagate_at_launch = true\n  }\n}\n\nresource \"aws_autoscaling_policy\" \"cpu_target\" {\n  autoscaling_group_name = aws_autoscaling_group.app.name\n  policy_type            = \"TargetTrackingScaling\"\n  target_tracking_configuration {\n    predefined_metric_specification {\n      predefined_metric_type = \"ASGAverageCPUUtilization\"\n    }\n    target_value = 60.0\n  }\n}\n```\n\n### Cost Analysis CLI\n\n```bash\n# AWS: identify top cost drivers for the last 30 days\naws ce get-cost-and-usage \\\n  --time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \\\n  --granularity MONTHLY \\\n  --metrics \"UnblendedCost\" \\\n  --group-by Type=DIMENSION,Key=SERVICE \\\n  --query 'ResultsByTime[0].Groups[*].{Service:Keys[0],Cost:Metrics.UnblendedCost.Amount}' \\\n  --output table\n\n# Azure: review spend by resource group\naz consumption usage list \\\n  --start-date $(date -d '30 days ago' +%Y-%m-%d) \\\n  --end-date $(date +%Y-%m-%d) \\\n  --query \"[].{ResourceGroup:resourceGroup,Cost:pretaxCost,Currency:currency}\" \\\n  --output table\n```\n\n## Output Templates\n\nWhen designing cloud architecture, provide:\n1. Architecture diagram with services and data flow\n2. Service selection rationale (compute, storage, database, networking)\n3. Security architecture (IAM, network segmentation, encryption)\n4. Cost estimation and optimization strategy\n5. Deployment approach and rollback plan\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/infrastructure/cloud-architect/)\n","skills/cloud-architect/references/aws.md":"# AWS Architecture Reference\n\nComprehensive guide for AWS services, patterns, and Well-Architected Framework implementation.\n\n## Well-Architected Framework\n\n### Six Pillars\n\n1. **Operational Excellence**\n   - Infrastructure as Code (CloudFormation, CDK, Terraform)\n   - Continuous integration/deployment\n   - Observability (CloudWatch, X-Ray)\n   - Runbooks and playbooks\n   - Game days and failure injection\n\n2. **Security**\n   - Identity and Access Management (IAM)\n   - Detective controls (GuardDuty, Security Hub)\n   - Infrastructure protection (VPC, security groups, NACLs)\n   - Data protection (KMS, encryption at rest/transit)\n   - Incident response automation\n\n3. **Reliability**\n   - Multi-AZ deployments\n   - Auto Scaling groups\n   - Route 53 health checks and failover\n   - Backup and restore (AWS Backup)\n   - Chaos engineering (AWS FIS)\n\n4. **Performance Efficiency**\n   - Right-sizing with Compute Optimizer\n   - Caching strategies (CloudFront, ElastiCache)\n   - Database optimization (RDS Performance Insights)\n   - Serverless architectures\n   - Global content delivery\n\n5. **Cost Optimization**\n   - Reserved Instances and Savings Plans\n   - Spot Instances for fault-tolerant workloads\n   - S3 Intelligent-Tiering and lifecycle policies\n   - Right-sizing recommendations\n   - Cost allocation tags and budgets\n\n6. **Sustainability**\n   - Region selection for renewable energy\n   - Serverless to minimize idle resources\n   - Efficient data storage patterns\n   - Resource utilization optimization\n\n## Core Services Architecture\n\n### Compute\n\n**EC2 (Elastic Compute Cloud)**\n- Instance families: General (t3, m5), Compute (c5), Memory (r5), GPU (p3, g4)\n- Auto Scaling: Target tracking, step scaling, scheduled scaling\n- Placement groups: Cluster, partition, spread\n- Best practices: Use latest generation, right-size, enable detailed monitoring\n\n**Lambda**\n- Invocation models: Synchronous, asynchronous, event source mapping\n- Concurrency: Reserved, provisioned, burst limits\n- Layers for shared dependencies\n- Best practices: Keep functions small, use environment variables, set timeouts\n\n**ECS/EKS (Container Services)**\n- ECS: Fargate for serverless, EC2 for control\n- EKS: Managed Kubernetes with AWS integration\n- Service mesh: App Mesh for observability\n- Best practices: Use Fargate for simplicity, EKS for portability\n\n**Elastic Beanstalk**\n- Managed platform for web apps\n- Auto-scaling and load balancing included\n- Support for multiple languages and Docker\n\n### Storage\n\n**S3 (Simple Storage Service)**\n- Storage classes: Standard, IA, One Zone-IA, Glacier, Deep Archive\n- Lifecycle policies for automatic tiering\n- Versioning and MFA delete for protection\n- Cross-region replication for DR\n- Best practices: Enable versioning, use lifecycle policies, block public access\n\n**EBS (Elastic Block Store)**\n- Volume types: gp3 (general), io2 (IOPS), st1 (throughput), sc1 (cold)\n- Snapshots to S3 for backup\n- Encryption by default\n- Best practices: Use gp3 for most workloads, enable encryption\n\n**EFS (Elastic File System)**\n- NFSv4 file system for shared access\n- Performance modes: General purpose, Max I/O\n- Throughput modes: Bursting, provisioned\n- Best practices: Use lifecycle management, enable encryption\n\n**FSx**\n- FSx for Windows File Server (SMB)\n- FSx for Lustre (HPC workloads)\n- FSx for NetApp ONTAP\n- FSx for OpenZFS\n\n### Database\n\n**RDS (Relational Database Service)**\n- Engines: MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, Aurora\n- Multi-AZ for high availability\n- Read replicas for scalability\n- Automated backups and point-in-time recovery\n- Best practices: Use Aurora for performance, enable Multi-AZ, use read replicas\n\n**Aurora**\n- MySQL and PostgreSQL compatible\n- 5x MySQL, 3x PostgreSQL performance\n- Global databases for cross-region DR\n- Serverless v2 for variable workloads\n- Best practices: Use Aurora Serverless for unpredictable workloads\n\n**DynamoDB**\n- NoSQL key-value and document database\n- On-demand or provisioned capacity\n- Global tables for multi-region replication\n- DynamoDB Streams for change data capture\n- Best practices: Use on-demand for unpredictable traffic, implement GSI carefully\n\n**ElastiCache**\n- Redis or Memcached in-memory caching\n- Cluster mode for Redis scalability\n- Best practices: Use for session storage, API caching\n\n### Networking\n\n**VPC (Virtual Private Cloud)**\n- CIDR planning: Avoid overlaps, plan for growth\n- Subnets: Public (IGW), private (NAT), isolated (no internet)\n- Route tables and routing decisions\n- Security groups (stateful) and NACLs (stateless)\n- Best practices: Use /16 for VPC, /24 for subnets, plan IP space\n\n**Route 53**\n- DNS service with health checks\n- Routing policies: Simple, weighted, latency, failover, geolocation\n- Best practices: Use alias records, enable DNSSEC\n\n**CloudFront**\n- Global CDN with edge locations\n- Origin types: S3, ALB, custom origins\n- Lambda@Edge for request/response manipulation\n- Best practices: Enable compression, use field-level encryption\n\n**VPN and Direct Connect**\n- Site-to-Site VPN for encrypted tunnels\n- Direct Connect for dedicated bandwidth\n- Transit Gateway for hub-and-spoke topology\n- Best practices: Use Direct Connect for high bandwidth, Transit Gateway for complex routing\n\n**API Gateway**\n- REST APIs, HTTP APIs, WebSocket APIs\n- Throttling and quotas\n- Integration with Lambda, HTTP endpoints, AWS services\n- Best practices: Use HTTP APIs for lower cost, implement caching\n\n### Security\n\n**IAM (Identity and Access Management)**\n- Principle of least privilege\n- Roles for applications, not access keys\n- MFA for privileged users\n- Service Control Policies (SCPs) for organization-wide controls\n- Best practices: Use roles, enable MFA, rotate credentials\n\n**KMS (Key Management Service)**\n- Customer managed keys (CMKs)\n- Automatic key rotation\n- Envelope encryption pattern\n- Best practices: Enable automatic rotation, use grants for temporary access\n\n**Secrets Manager**\n- Automatic rotation for RDS credentials\n- Versioning and rollback\n- Best practices: Rotate secrets regularly, use VPC endpoints\n\n**Security Hub**\n- Centralized security findings\n- CIS AWS Foundations Benchmark\n- Integration with GuardDuty, Inspector, Macie\n\n**GuardDuty**\n- Threat detection using ML\n- Monitors CloudTrail, VPC Flow Logs, DNS logs\n\n## Architecture Patterns\n\n### High Availability\n\n**Multi-AZ Pattern**\n```\n- Application Load Balancer across 3 AZs\n- Auto Scaling group with instances in each AZ\n- RDS Multi-AZ for database\n- S3 for static assets (11 9's durability)\n```\n\n**Multi-Region Pattern**\n```\n- Route 53 with health checks and failover\n- CloudFront for global distribution\n- Aurora Global Database for <1s RPO\n- S3 Cross-Region Replication\n```\n\n### Serverless Architecture\n\n**API-Driven Pattern**\n```\nAPI Gateway -> Lambda -> DynamoDB\n              |\n              v\n          EventBridge -> Lambda (async processing)\n```\n\n**Event-Driven Pattern**\n```\nS3 Event -> Lambda -> Process -> SNS\n                                  |\n                                  v\n                              Multiple subscribers\n```\n\n### Microservices on AWS\n\n**Container-Based**\n```\nALB -> ECS Fargate (multiple services)\n    |\n    v\nService Discovery (Cloud Map)\n    |\n    v\nRDS/DynamoDB per service\n```\n\n**Service Mesh**\n```\nApp Mesh for traffic management\nX-Ray for distributed tracing\nCloudWatch Container Insights\n```\n\n### Data Lake Architecture\n\n```\nData Sources -> Kinesis Data Streams\n                      |\n                      v\n              Kinesis Firehose\n                      |\n                      v\n                S3 (raw bucket)\n                      |\n                      v\n        Glue ETL or Lambda processing\n                      |\n                      v\n            S3 (processed bucket)\n                      |\n                      v\n          Athena/Redshift Spectrum\n                      |\n                      v\n              QuickSight dashboards\n```\n\n## Migration Strategies (6Rs)\n\n1. **Rehost (Lift-and-Shift)**\n   - AWS Application Migration Service (MGN)\n   - Minimal changes, quick migration\n   - Use for legacy apps with compliance constraints\n\n2. **Replatform (Lift-Tinker-and-Shift)**\n   - Migrate to RDS instead of self-managed databases\n   - Use Elastic Beanstalk instead of custom app servers\n   - Small optimizations during migration\n\n3. **Repurchase (Drop-and-Shop)**\n   - Move to SaaS (e.g., Salesforce, Workday)\n   - Reduce maintenance burden\n\n4. **Refactor/Re-architect**\n   - Modernize to serverless or containers\n   - Highest effort, highest benefit\n   - Use for competitive advantage applications\n\n5. **Retire**\n   - Decommission unused applications\n   - Reduce attack surface and costs\n\n6. **Retain**\n   - Keep on-premises temporarily\n   - Migrate later or keep for regulatory reasons\n\n## Landing Zone Design\n\n**AWS Control Tower**\n- Multi-account strategy (AWS Organizations)\n- Account factory for standardization\n- Guardrails for governance (SCPs)\n- Centralized logging (CloudTrail, Config)\n\n**Account Structure**\n```\nRoot\n├── Security OU\n│   ├── Log Archive Account\n│   └── Security Tooling Account\n├── Infrastructure OU\n│   ├── Network Account (Transit Gateway, VPN)\n│   └── Shared Services Account\n└── Workloads OU\n    ├── Production Account\n    ├── Staging Account\n    └── Development Account\n```\n\n**Network Design**\n```\nTransit Gateway (hub)\n    |\n    ├── Production VPC\n    ├── Staging VPC\n    ├── Development VPC\n    └── On-premises (Direct Connect/VPN)\n```\n\n## Cost Optimization Strategies\n\n**Compute Savings**\n- Compute Savings Plans (up to 66% savings)\n- EC2 Reserved Instances (1-year or 3-year)\n- Spot Instances for batch/fault-tolerant workloads\n- Lambda: Reduce memory if possible, use reserved concurrency\n\n**Storage Savings**\n- S3 Intelligent-Tiering for unpredictable access\n- Lifecycle policies to Glacier/Deep Archive\n- EBS gp3 instead of gp2 (20% cheaper, better performance)\n- Delete unused snapshots and volumes\n\n**Database Savings**\n- Aurora Serverless v2 for variable workloads\n- RDS Reserved Instances\n- DynamoDB on-demand for unpredictable workloads\n- Read replicas in same region to reduce cross-AZ data transfer\n\n**Monitoring and Alerting**\n- AWS Cost Explorer for analysis\n- AWS Budgets for alerts\n- Cost Anomaly Detection\n- Trusted Advisor for recommendations\n\n## Disaster Recovery\n\n**RPO and RTO Targets**\n- Backup and Restore: Hours RPO/RTO (lowest cost)\n- Pilot Light: Minutes RPO, hours RTO\n- Warm Standby: Seconds RPO, minutes RTO\n- Multi-Site Active/Active: Near-zero RPO/RTO (highest cost)\n\n**Implementation**\n- AWS Backup for centralized backup management\n- Aurora Global Database for cross-region replication\n- S3 Cross-Region Replication\n- Route 53 health checks and failover routing\n- Regular DR testing with CloudFormation/Terraform\n\n## Monitoring and Observability\n\n**CloudWatch**\n- Metrics: Standard (5 min) and detailed (1 min)\n- Alarms with SNS notifications\n- Logs Insights for log analysis\n- Dashboards for visualization\n\n**X-Ray**\n- Distributed tracing for microservices\n- Service map visualization\n- Trace annotations and metadata\n\n**AWS Config**\n- Resource inventory and change tracking\n- Compliance rules evaluation\n- Relationship tracking between resources\n","skills/cloud-architect/references/azure.md":"# Azure Architecture Reference\n\nComprehensive guide for Azure services, patterns, and Cloud Adoption Framework implementation.\n\n## Cloud Adoption Framework\n\n### Framework Phases\n\n1. **Strategy**\n   - Define business justification\n   - Expected business outcomes\n   - Business case development\n   - First project prioritization\n\n2. **Plan**\n   - Digital estate assessment\n   - Initial organization alignment\n   - Skills readiness plan\n   - Cloud adoption plan\n\n3. **Ready**\n   - Azure landing zone setup\n   - Azure setup guide\n   - Migration readiness\n   - Best practices validation\n\n4. **Adopt (Migrate + Innovate)**\n   - Migration: Assess, migrate, optimize\n   - Innovate: Build cloud-native solutions\n   - Best practices and patterns\n\n5. **Govern**\n   - Methodology for governance\n   - Governance benchmark\n   - Initial governance foundation\n   - Mature governance evolution\n\n6. **Manage**\n   - Business commitments\n   - Operations baseline\n   - Platform and workload specialization\n\n## Azure Well-Architected Framework\n\n### Five Pillars\n\n1. **Cost Optimization**\n   - Azure Cost Management and Billing\n   - Reserved instances and Savings Plans\n   - Azure Hybrid Benefit\n   - Auto-scaling and right-sizing\n\n2. **Operational Excellence**\n   - Infrastructure as Code (ARM, Bicep, Terraform)\n   - Azure DevOps and GitHub Actions\n   - Azure Monitor and Application Insights\n   - Deployment slots and blue-green deployments\n\n3. **Performance Efficiency**\n   - Azure CDN and Front Door\n   - Auto-scaling (VMSS, App Service)\n   - Caching (Redis, CDN)\n   - Performance diagnostics\n\n4. **Reliability**\n   - Availability Zones and regions\n   - Azure Site Recovery\n   - Load Balancer and Traffic Manager\n   - Backup and disaster recovery\n\n5. **Security**\n   - Azure AD (Entra ID)\n   - Network Security Groups and Firewalls\n   - Azure Key Vault\n   - Microsoft Defender for Cloud\n\n## Core Services Architecture\n\n### Compute\n\n**Virtual Machines**\n- VM sizes: General (D-series), Compute (F-series), Memory (E-series), GPU (N-series)\n- Availability Sets (99.95% SLA)\n- Availability Zones (99.99% SLA)\n- VM Scale Sets for auto-scaling\n- Best practices: Use managed disks, enable accelerated networking, use proximity placement groups\n\n**App Service**\n- Web Apps, API Apps, Mobile Apps\n- Deployment slots for staging\n- Auto-scaling based on metrics or schedule\n- Supports .NET, Java, Node.js, Python, PHP, Ruby\n- Best practices: Use deployment slots, enable auto-scaling, use App Service Plan efficiently\n\n**Azure Functions**\n- Consumption Plan (serverless)\n- Premium Plan (VNet integration, no cold start)\n- Dedicated Plan (App Service Plan)\n- Durable Functions for orchestration\n- Best practices: Keep functions small, use Premium for production, implement retry policies\n\n**Azure Kubernetes Service (AKS)**\n- Managed Kubernetes control plane\n- Azure CNI or kubenet networking\n- Azure AD integration\n- Virtual nodes (Azure Container Instances)\n- Best practices: Use system node pools, enable autoscaling, implement network policies\n\n**Container Instances**\n- Serverless containers\n- Fast startup without infrastructure management\n- Best for batch jobs and burstable workloads\n\n**Azure Batch**\n- Large-scale parallel and HPC workloads\n- Auto-scaling compute nodes\n- Task scheduling and dependencies\n\n### Storage\n\n**Blob Storage**\n- Storage tiers: Hot, Cool, Archive\n- Access tiers: Premium, Standard\n- Lifecycle management policies\n- Immutable storage for compliance\n- Best practices: Use lifecycle policies, enable soft delete, implement versioning\n\n**Azure Files**\n- SMB and NFS file shares\n- Integration with Azure File Sync\n- Premium tier for high performance\n- Best practices: Use Premium for databases, implement snapshots\n\n**Disk Storage**\n- Managed Disks: Premium SSD, Standard SSD, Standard HDD, Ultra Disk\n- Disk encryption with Azure Disk Encryption\n- Snapshots and incremental backups\n- Best practices: Use Premium SSD for production, enable encryption\n\n**Data Lake Storage Gen2**\n- Hierarchical namespace for big data\n- Built on Blob Storage\n- Integration with Azure Synapse and Databricks\n- Best practices: Enable hierarchical namespace, use lifecycle policies\n\n**Azure NetApp Files**\n- Enterprise-grade NFS and SMB shares\n- High performance and low latency\n- Snapshots and data protection\n\n### Database\n\n**Azure SQL Database**\n- Serverless and provisioned compute\n- Hyperscale for up to 100TB\n- Elastic pools for multiple databases\n- Auto-tuning and intelligent insights\n- Best practices: Use serverless for dev/test, enable geo-replication\n\n**Azure SQL Managed Instance**\n- Near 100% compatibility with SQL Server\n- VNet integration for isolation\n- Native virtual network implementation\n- Best practices: Use for lift-and-shift migrations\n\n**Cosmos DB**\n- Multi-model NoSQL database\n- Global distribution with multi-master\n- Consistency levels: Strong, Bounded staleness, Session, Consistent prefix, Eventual\n- APIs: SQL, MongoDB, Cassandra, Gremlin, Table\n- Best practices: Choose appropriate consistency, partition key design critical\n\n**Azure Database for PostgreSQL/MySQL/MariaDB**\n- Flexible Server (newer) vs Single Server (legacy)\n- High availability with zone redundancy\n- Read replicas for scaling\n- Best practices: Use Flexible Server, enable HA, implement connection pooling\n\n**Azure Cache for Redis**\n- In-memory caching\n- Clustering for scalability\n- Geo-replication for disaster recovery\n- Best practices: Use Premium tier for production, enable persistence\n\n### Networking\n\n**Virtual Network (VNet)**\n- CIDR planning (avoid overlaps)\n- Subnets with Network Security Groups\n- Service endpoints and Private Link\n- VNet peering for connectivity\n- Best practices: Plan IP address space, use NSGs, implement Private Link\n\n**Azure Load Balancer**\n- Layer 4 load balancing\n- Standard SKU (zone-redundant, SLA)\n- Health probes and distribution algorithms\n- Best practices: Use Standard SKU, configure health probes\n\n**Application Gateway**\n- Layer 7 load balancing\n- WAF (Web Application Firewall)\n- URL-based routing and SSL termination\n- Best practices: Enable WAF, use autoscaling\n\n**Azure Front Door**\n- Global load balancing and CDN\n- WAF at edge\n- Anycast for low latency\n- Best practices: Use for global applications, enable caching\n\n**VPN Gateway and ExpressRoute**\n- Site-to-Site VPN for encrypted connectivity\n- ExpressRoute for private, dedicated connection\n- Virtual WAN for global transit network\n- Best practices: Use ExpressRoute for production, implement redundancy\n\n**Azure Firewall**\n- Managed firewall service\n- Application and network rules\n- Threat intelligence\n- Best practices: Use in hub-spoke topology, enable DNS proxy\n\n**Azure Private Link**\n- Private connectivity to Azure services\n- No public internet exposure\n- Available for PaaS services\n- Best practices: Use for all PaaS services in production\n\n### Security and Identity\n\n**Azure Active Directory (Microsoft Entra ID)**\n- Identity and access management\n- Conditional Access policies\n- Multi-factor authentication\n- B2B and B2C scenarios\n- Best practices: Enable MFA, use Conditional Access, implement PIM\n\n**Azure Key Vault**\n- Secrets, keys, and certificates management\n- Hardware Security Module (HSM) backed\n- Soft delete and purge protection\n- Best practices: Enable soft delete, use RBAC, implement Private Link\n\n**Microsoft Defender for Cloud**\n- Security posture management\n- Threat protection for hybrid workloads\n- Regulatory compliance dashboard\n- Just-in-time VM access\n- Best practices: Enable enhanced security, implement recommendations\n\n**Azure Policy**\n- Governance and compliance at scale\n- Built-in and custom policies\n- Deny, audit, append effects\n- Best practices: Assign at management group level, test before enforce\n\n**Azure Sentinel**\n- Cloud-native SIEM and SOAR\n- AI-powered threat detection\n- Integration with Microsoft 365, third-party tools\n- Best practices: Enable data connectors, create custom analytics rules\n\n## Architecture Patterns\n\n### High Availability\n\n**Zone-Redundant Pattern**\n```\nAzure Front Door (global)\n    |\n    v\nApplication Gateway (zone-redundant)\n    |\n    v\nVM Scale Set (across availability zones)\n    |\n    v\nAzure SQL Database (zone-redundant)\n```\n\n**Multi-Region Pattern**\n```\nAzure Traffic Manager (DNS-based routing)\n    |\n    ├── Region 1: App Service + SQL Database (primary)\n    └── Region 2: App Service + SQL Database (geo-replica)\n```\n\n### Hub-Spoke Topology\n\n```\nHub VNet\n├── Azure Firewall\n├── VPN Gateway\n└── Shared Services\n    |\n    ├── Spoke VNet 1 (Production)\n    ├── Spoke VNet 2 (Development)\n    └── Spoke VNet 3 (DMZ)\n```\n\n### Serverless Architecture\n\n**Event-Driven Pattern**\n```\nEvent Grid -> Azure Functions -> Cosmos DB\n                    |\n                    v\n              Service Bus -> Functions (processing)\n```\n\n**API-First Pattern**\n```\nAPI Management\n    |\n    ├── Function App 1 (auth)\n    ├── Function App 2 (business logic)\n    └── Function App 3 (data access)\n```\n\n### Microservices on Azure\n\n**AKS-Based**\n```\nAzure Front Door\n    |\n    v\nApplication Gateway + WAF\n    |\n    v\nAKS (multiple microservices)\n    |\n    ├── Cosmos DB (microservice A)\n    ├── SQL Database (microservice B)\n    └── Service Bus (async communication)\n```\n\n**Container Apps Pattern**\n```\nAzure Container Apps\n├── Dapr for state management\n├── KEDA for event-driven scaling\n└── Azure Monitor for observability\n```\n\n### Data Platform\n\n```\nData Sources\n    |\n    v\nEvent Hubs / IoT Hub\n    |\n    v\nStream Analytics (real-time processing)\n    |\n    v\nData Lake Storage Gen2\n    |\n    v\nAzure Synapse Analytics\n    |\n    v\nPower BI (visualization)\n```\n\n## Landing Zone Design\n\n### Enterprise-Scale Landing Zone\n\n**Management Group Hierarchy**\n```\nTenant Root Group\n├── Platform\n│   ├── Management (monitoring, automation)\n│   ├── Connectivity (hub networks, VPN)\n│   └── Identity (domain controllers)\n└── Landing Zones\n    ├── Corp (internal workloads)\n    └── Online (internet-facing workloads)\n```\n\n**Network Topology**\n```\nHub VNet (Connectivity subscription)\n├── Azure Firewall\n├── VPN Gateway\n├── ExpressRoute Gateway\n└── Bastion\n\nSpoke VNets (Workload subscriptions)\n├── Production VNet\n├── Staging VNet\n└── Development VNet\n```\n\n**Governance**\n- Azure Policy for compliance\n- Management groups for hierarchy\n- RBAC assignments at appropriate scope\n- Resource tags for cost allocation\n- Azure Blueprints for repeatable deployments\n\n## Migration Strategies\n\n### Azure Migrate\n\n1. **Assess**\n   - Discovery with Azure Migrate appliance\n   - Dependency analysis\n   - Performance-based sizing\n   - Cost estimation\n\n2. **Migrate**\n   - Azure Migrate: Server Migration (agentless)\n   - Database Migration Service\n   - App Service Migration Assistant\n   - Data Box for large data transfers\n\n3. **Optimize**\n   - Right-sizing recommendations\n   - Reserved instances\n   - Azure Hybrid Benefit\n\n### Migration Patterns\n\n**Rehost**: Azure Migrate for VMs\n**Replatform**: App Service, Azure SQL Database\n**Refactor**: Container Apps, AKS, Functions\n**Rebuild**: Azure-native services (Cosmos DB, Cognitive Services)\n\n## Cost Optimization\n\n### Compute Savings\n- Azure Reserved Instances (1-year or 3-year, up to 72% savings)\n- Azure Savings Plans for Compute (up to 65% savings)\n- Spot VMs for fault-tolerant workloads (up to 90% savings)\n- Azure Hybrid Benefit (use existing Windows Server/SQL licenses)\n- Auto-shutdown for dev/test VMs\n\n### Storage Savings\n- Blob Storage lifecycle policies (Hot -> Cool -> Archive)\n- Azure Files: Standard tier for general use\n- Managed Disks: Standard SSD instead of Premium if possible\n- Delete unused snapshots and disks\n\n### Database Savings\n- Serverless tier for Azure SQL Database\n- Reserved capacity for Cosmos DB\n- DTU model vs vCore (choose based on workload)\n- Pause Azure Synapse when not in use\n\n### Monitoring\n- Azure Cost Management + Billing\n- Cost alerts and budgets\n- Azure Advisor recommendations\n- Resource tagging for cost allocation\n\n## Disaster Recovery\n\n### Azure Site Recovery\n\n**VM Replication**\n- Azure to Azure replication\n- On-premises to Azure (VMware, Hyper-V, physical)\n- RPO: 30 seconds to a few minutes\n- Automated failover and failback\n\n**Recovery Plans**\n- Multi-tier application recovery\n- Customizable scripts and manual actions\n- Integration with Azure Automation\n\n### Backup Strategies\n\n**Azure Backup**\n- VM backups (application-consistent)\n- SQL Server and SAP HANA in Azure VMs\n- Azure Files backup\n- Cross-region restore\n\n**Database Backup**\n- SQL Database: Automated backups (7-35 days)\n- Cosmos DB: Continuous backup (30 days)\n- Long-term retention policies\n\n### High Availability\n\n**RTO/RPO Targets**\n- Active-Active: Multi-region with Traffic Manager (near-zero)\n- Active-Passive: Geo-replication with failover (minutes)\n- Backup and Restore: Azure Backup (hours)\n\n## Monitoring and Observability\n\n### Azure Monitor\n\n**Components**\n- Metrics: Time-series data (1-minute resolution)\n- Logs: Log Analytics workspace for queries (KQL)\n- Alerts: Metric, log, and activity log alerts\n- Dashboards: Custom visualizations\n\n**Application Insights**\n- APM for web applications\n- Distributed tracing\n- Live Metrics Stream\n- Smart detection and anomaly detection\n- Best practices: Instrument all applications, set up availability tests\n\n### Log Analytics\n\n**KQL Queries**\n```kusto\n// Performance analysis\nPerf\n| where CounterName == \"% Processor Time\"\n| summarize avg(CounterValue) by bin(TimeGenerated, 5m), Computer\n| render timechart\n\n// Failed requests\nrequests\n| where success == false\n| summarize count() by resultCode, bin(timestamp, 1h)\n```\n\n**Workbooks**\n- Interactive reports\n- Parameterized queries\n- Combining metrics and logs\n\n## Identity and Access\n\n### Azure AD Best Practices\n\n- Enable MFA for all users\n- Use Conditional Access policies\n- Implement Privileged Identity Management (PIM)\n- Regular access reviews\n- Break-glass accounts\n\n### RBAC Design\n\n**Built-in Roles**\n- Owner: Full access including RBAC\n- Contributor: Full access except RBAC\n- Reader: Read-only access\n- Custom roles for specific needs\n\n**Scope Hierarchy**\n```\nManagement Group (highest)\n    |\nSubscription\n    |\nResource Group\n    |\nResource (lowest)\n```\n\nBest practices: Assign at highest appropriate scope, use groups not individual users, apply least privilege\n","skills/cloud-architect/references/cost.md":"# Cloud Cost Optimization Reference\n\nComprehensive guide for cloud cost optimization including reserved instances, spot/preemptible, right-sizing, and FinOps practices.\n\n## FinOps Framework\n\n### FinOps Principles\n\n1. **Teams need to collaborate** - Finance, engineering, and business work together\n2. **Everyone takes ownership** - Decentralized cost responsibility\n3. **A centralized team drives FinOps** - Center of excellence for best practices\n4. **Reports should be accessible and timely** - Real-time visibility\n5. **Decisions are driven by business value** - Cost per business outcome\n6. **Take advantage of variable cost model** - Scale up and down as needed\n\n### FinOps Lifecycle\n\n```\n      Inform\n         |\n    +---------+\n    |         |\n    v         v\n Optimize --> Operate\n    ^         |\n    |         |\n    +---------+\n```\n\n**Inform Phase**\n- Visibility into cloud spend\n- Allocation and showback\n- Benchmarking and forecasting\n\n**Optimize Phase**\n- Rate optimization (RIs, savings plans)\n- Usage optimization (right-sizing)\n- Architectural optimization\n\n**Operate Phase**\n- Continuous improvement\n- Automation and governance\n- Anomaly detection\n\n## Compute Cost Optimization\n\n### Reserved Instances / Savings Plans\n\n**AWS Savings Plans**\n\n| Type | Flexibility | Savings |\n|------|-------------|---------|\n| Compute Savings Plans | Any EC2, Fargate, Lambda | Up to 66% |\n| EC2 Instance Savings Plans | Specific instance family, region | Up to 72% |\n| Reserved Instances | Specific instance type, AZ | Up to 72% |\n\n**Commitment Strategy**\n```\nBaseline (always-on): 1-year or 3-year Savings Plans\nVariable (predictable): Scheduled Reserved Instances\nSpiky (unpredictable): On-Demand + Spot\n```\n\n**Azure Reservations**\n```\n# Azure CLI - Purchase reservation\naz reservations reservation-order purchase \\\n  --sku Standard_D2s_v3 \\\n  --term P1Y \\\n  --billing-scope /subscriptions/{subscription-id} \\\n  --quantity 10 \\\n  --applied-scope-type Shared\n```\n\n**GCP Committed Use Discounts**\n- Resource-based: Specific vCPUs and memory\n- Spend-based: Dollar commitment for flexibility\n- 1-year (37% discount) or 3-year (55% discount)\n\n### Spot/Preemptible Instances\n\n**When to Use Spot**\n- Batch processing and analytics\n- CI/CD build agents\n- Stateless web servers (with auto-scaling)\n- Machine learning training\n- Development and testing environments\n\n**AWS Spot Best Practices**\n```yaml\n# EC2 Auto Scaling with Spot\nMixedInstancesPolicy:\n  InstancesDistribution:\n    OnDemandBaseCapacity: 2\n    OnDemandPercentageAboveBaseCapacity: 20\n    SpotAllocationStrategy: capacity-optimized\n  LaunchTemplate:\n    Overrides:\n      - InstanceType: m5.large\n      - InstanceType: m5a.large\n      - InstanceType: m4.large\n      - InstanceType: r5.large\n```\n\n**Spot Interruption Handling**\n```python\n# Check for spot termination notice (AWS)\nimport requests\n\ndef check_spot_termination():\n    try:\n        response = requests.get(\n            \"http://169.254.169.254/latest/meta-data/spot/termination-time\",\n            timeout=2\n        )\n        if response.status_code == 200:\n            # 2-minute warning - gracefully shutdown\n            graceful_shutdown()\n    except requests.exceptions.RequestException:\n        pass  # Not being terminated\n```\n\n**GCP Preemptible/Spot VMs**\n```hcl\n# Terraform - GCP Spot VM\nresource \"google_compute_instance\" \"spot\" {\n  name         = \"spot-instance\"\n  machine_type = \"n2-standard-4\"\n\n  scheduling {\n    preemptible                 = true\n    automatic_restart           = false\n    provisioning_model          = \"SPOT\"\n    instance_termination_action = \"STOP\"\n  }\n}\n```\n\n### Right-Sizing\n\n**Analysis Process**\n1. Collect metrics (CPU, memory, network, disk I/O)\n2. Identify idle or underutilized resources\n3. Recommend appropriate instance size\n4. Implement changes during maintenance windows\n5. Monitor and iterate\n\n**AWS Compute Optimizer**\n```bash\n# Enable Compute Optimizer\naws compute-optimizer update-enrollment-status \\\n  --status Active \\\n  --include-member-accounts\n\n# Get recommendations\naws compute-optimizer get-ec2-instance-recommendations \\\n  --filters name=Finding,values=OVER_PROVISIONED\n```\n\n**Right-Sizing Thresholds**\n| Metric | Underutilized | Optimal | Overutilized |\n|--------|---------------|---------|--------------|\n| CPU | <20% avg | 40-60% avg | >80% avg |\n| Memory | <30% avg | 50-70% avg | >85% avg |\n| Network | <10% capacity | Variable | >80% capacity |\n\n**Azure Advisor Recommendations**\n```bash\n# Get cost recommendations\naz advisor recommendation list \\\n  --category Cost \\\n  --query \"[?impact=='High']\"\n```\n\n## Storage Cost Optimization\n\n### Object Storage Tiering\n\n**AWS S3 Storage Classes**\n```\nS3 Standard\n    |\n    | (30 days)\n    v\nS3 Standard-IA\n    |\n    | (90 days)\n    v\nS3 Glacier Instant Retrieval\n    |\n    | (180 days)\n    v\nS3 Glacier Deep Archive\n```\n\n**Lifecycle Policy Example**\n```json\n{\n  \"Rules\": [\n    {\n      \"ID\": \"OptimizeCosts\",\n      \"Status\": \"Enabled\",\n      \"Filter\": { \"Prefix\": \"logs/\" },\n      \"Transitions\": [\n        { \"Days\": 30, \"StorageClass\": \"STANDARD_IA\" },\n        { \"Days\": 90, \"StorageClass\": \"GLACIER\" },\n        { \"Days\": 365, \"StorageClass\": \"DEEP_ARCHIVE\" }\n      ],\n      \"Expiration\": { \"Days\": 730 }\n    }\n  ]\n}\n```\n\n**S3 Intelligent-Tiering**\n- Automatic tiering based on access patterns\n- No retrieval fees\n- Small monitoring fee per object\n- Best for unpredictable access patterns\n\n### Block Storage Optimization\n\n**EBS Volume Selection**\n| Type | Use Case | $/GB/month |\n|------|----------|------------|\n| gp3 | General purpose | $0.08 |\n| gp2 | Legacy (migrate to gp3) | $0.10 |\n| io2 | High IOPS databases | $0.125+ |\n| st1 | Throughput (big data) | $0.045 |\n| sc1 | Cold archives | $0.015 |\n\n**gp3 Migration (20% savings)**\n```bash\n# Modify EBS volume from gp2 to gp3\naws ec2 modify-volume \\\n  --volume-id vol-12345678 \\\n  --volume-type gp3 \\\n  --iops 3000 \\\n  --throughput 125\n```\n\n### Database Storage\n\n**Aurora Storage Optimization**\n- Pay only for storage used (auto-scaling)\n- No pre-provisioning required\n- 10GB increments up to 128TB\n\n**DynamoDB Capacity Modes**\n| Mode | Best For | Pricing |\n|------|----------|---------|\n| On-Demand | Unpredictable traffic | Pay per request |\n| Provisioned | Steady traffic | Pay per capacity unit |\n| Provisioned + Auto Scaling | Variable but predictable | Lower cost than on-demand |\n\n## Network Cost Optimization\n\n### Data Transfer Costs\n\n**AWS Data Transfer Pricing**\n```\nInbound: Free\nSame AZ: Free\nCross-AZ: $0.01/GB each direction\nSame Region (via public IP): $0.01/GB\nCross-Region: $0.02/GB\nInternet Egress: $0.09/GB (first 10TB)\n```\n\n**Optimization Strategies**\n1. Keep traffic within same AZ when possible\n2. Use VPC endpoints for AWS services\n3. Use CloudFront for cacheable content\n4. Compress data before transfer\n5. Use regional rather than global services\n\n**VPC Endpoints (Avoid NAT Gateway)**\n```hcl\n# Gateway endpoint (free for S3, DynamoDB)\nresource \"aws_vpc_endpoint\" \"s3\" {\n  vpc_id       = aws_vpc.main.id\n  service_name = \"com.amazonaws.us-east-1.s3\"\n}\n\n# Interface endpoint (cheaper than NAT for specific services)\nresource \"aws_vpc_endpoint\" \"ecr\" {\n  vpc_id            = aws_vpc.main.id\n  service_name      = \"com.amazonaws.us-east-1.ecr.api\"\n  vpc_endpoint_type = \"Interface\"\n}\n```\n\n### CDN Optimization\n\n**CloudFront Cost Savings**\n- Lower data transfer rates than direct from origin\n- Cache hit ratio optimization (target >90%)\n- Use Origin Shield to reduce origin load\n- Compress objects (Gzip/Brotli)\n\n```yaml\n# CloudFront cache optimization\nCacheBehaviors:\n  - PathPattern: \"/static/*\"\n    CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6  # CachingOptimized\n    Compress: true\n    TTL:\n      DefaultTTL: 86400\n      MaxTTL: 31536000\n```\n\n## Serverless Cost Optimization\n\n### Lambda Optimization\n\n**Memory/CPU Tuning**\n```python\n# Use AWS Lambda Power Tuning\n# Finds optimal memory for cost vs performance\n\n# Results example:\n# 128MB:  $0.000021 per invocation, 3200ms duration\n# 256MB:  $0.000025 per invocation, 1600ms duration\n# 512MB:  $0.000031 per invocation, 800ms duration\n# 1024MB: $0.000042 per invocation, 450ms duration\n# Optimal: 512MB (best cost-performance balance)\n```\n\n**Cost Reduction Strategies**\n1. Right-size memory allocation\n2. Minimize cold starts (provisioned concurrency for critical paths)\n3. Use ARM64 (Graviton2) - 20% cheaper\n4. Optimize package size for faster cold starts\n5. Use Lambda Layers for shared dependencies\n\n**Graviton2 Migration**\n```yaml\n# SAM template with ARM64\nResources:\n  MyFunction:\n    Type: AWS::Serverless::Function\n    Properties:\n      Runtime: python3.11\n      Architectures:\n        - arm64  # 20% cost savings\n```\n\n### Container Optimization\n\n**Fargate Pricing Optimization**\n```\n# Fargate Spot: Up to 70% discount\n# Use for fault-tolerant workloads\n\nECS Service:\n  CapacityProviderStrategy:\n    - CapacityProvider: FARGATE_SPOT\n      Weight: 4\n    - CapacityProvider: FARGATE\n      Weight: 1\n      Base: 2  # Minimum on-demand tasks\n```\n\n**Right-Size Container Resources**\n```yaml\n# Analyze actual usage with Container Insights\nresources:\n  requests:\n    memory: \"256Mi\"  # Based on p95 usage + 20% buffer\n    cpu: \"100m\"      # Based on p95 usage + 20% buffer\n  limits:\n    memory: \"512Mi\"  # 2x requests for burst\n    cpu: \"500m\"\n```\n\n## Cost Allocation and Tagging\n\n### Tagging Strategy\n\n**Required Tags**\n```yaml\n# Terraform - enforce tags\nvariable \"required_tags\" {\n  default = {\n    environment  = \"prod\"\n    cost-center  = \"engineering\"\n    owner        = \"platform-team\"\n    project      = \"api-gateway\"\n    managed-by   = \"terraform\"\n  }\n}\n\nresource \"aws_instance\" \"example\" {\n  ami           = data.aws_ami.latest.id\n  instance_type = \"t3.medium\"\n  tags          = var.required_tags\n}\n```\n\n**Tag Enforcement**\n```json\n// AWS SCP - Deny untagged resources\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyUntaggedEC2\",\n      \"Effect\": \"Deny\",\n      \"Action\": \"ec2:RunInstances\",\n      \"Resource\": \"arn:aws:ec2:*:*:instance/*\",\n      \"Condition\": {\n        \"Null\": {\n          \"aws:RequestTag/cost-center\": \"true\"\n        }\n      }\n    }\n  ]\n}\n```\n\n### Cost Allocation Reports\n\n**AWS Cost and Usage Report**\n```bash\n# Enable detailed billing reports\naws cur put-report-definition \\\n  --report-definition '{\n    \"ReportName\": \"detailed-cost-report\",\n    \"TimeUnit\": \"HOURLY\",\n    \"Format\": \"Parquet\",\n    \"Compression\": \"Parquet\",\n    \"S3Bucket\": \"my-billing-bucket\",\n    \"S3Region\": \"us-east-1\",\n    \"AdditionalArtifacts\": [\"ATHENA\"]\n  }'\n```\n\n**Athena Queries for Analysis**\n```sql\n-- Cost by service and tag\nSELECT\n  line_item_product_code as service,\n  resource_tags_user_cost_center as cost_center,\n  SUM(line_item_unblended_cost) as cost\nFROM cost_report\nWHERE month = '2024-01'\nGROUP BY 1, 2\nORDER BY 3 DESC;\n\n-- Unused Reserved Instances\nSELECT\n  reservation_reservation_a_r_n,\n  reservation_unused_quantity,\n  reservation_unused_normalized_unit_quantity\nFROM cost_report\nWHERE reservation_unused_quantity > 0;\n```\n\n## Automation and Governance\n\n### Automated Cost Controls\n\n**AWS Budgets with Actions**\n```yaml\n# CloudFormation - Budget with auto-stop\nResources:\n  MonthlyCostBudget:\n    Type: AWS::Budgets::Budget\n    Properties:\n      Budget:\n        BudgetName: monthly-cost-limit\n        BudgetLimit:\n          Amount: 10000\n          Unit: USD\n        TimeUnit: MONTHLY\n        BudgetType: COST\n      NotificationsWithSubscribers:\n        - Notification:\n            NotificationType: ACTUAL\n            ComparisonOperator: GREATER_THAN\n            Threshold: 80\n          Subscribers:\n            - SubscriptionType: EMAIL\n              Address: finance@company.com\n```\n\n**Scheduled Scaling (Dev/Test)**\n```yaml\n# Stop non-prod resources nights/weekends\nResources:\n  ScaleDownSchedule:\n    Type: AWS::AutoScaling::ScheduledAction\n    Properties:\n      AutoScalingGroupName: !Ref DevASG\n      DesiredCapacity: 0\n      Recurrence: \"0 20 * * MON-FRI\"  # 8 PM weekdays\n\n  ScaleUpSchedule:\n    Type: AWS::AutoScaling::ScheduledAction\n    Properties:\n      AutoScalingGroupName: !Ref DevASG\n      DesiredCapacity: 3\n      Recurrence: \"0 8 * * MON-FRI\"   # 8 AM weekdays\n```\n\n### Cost Anomaly Detection\n\n**AWS Cost Anomaly Detection**\n```bash\n# Create anomaly monitor\naws ce create-anomaly-monitor \\\n  --anomaly-monitor '{\n    \"MonitorName\": \"ServiceMonitor\",\n    \"MonitorType\": \"DIMENSIONAL\",\n    \"MonitorDimension\": \"SERVICE\"\n  }'\n\n# Create anomaly subscription\naws ce create-anomaly-subscription \\\n  --anomaly-subscription '{\n    \"SubscriptionName\": \"CostAlerts\",\n    \"MonitorArnList\": [\"arn:aws:ce::123456789:anomalymonitor/abc123\"],\n    \"Subscribers\": [{\"Type\": \"EMAIL\", \"Address\": \"alerts@company.com\"}],\n    \"Threshold\": 100\n  }'\n```\n\n## Cost Metrics and KPIs\n\n### Key Metrics\n\n| Metric | Formula | Target |\n|--------|---------|--------|\n| Unit Cost | Total Cost / Business Metric | Decreasing |\n| Coverage | Reserved Hours / Total Hours | >70% |\n| Utilization | Used Reserved Hours / Purchased | >80% |\n| Waste | Idle Resource Cost / Total Cost | <10% |\n| Forecast Accuracy | Actual / Forecasted | 90-110% |\n\n### Dashboard Example\n\n```sql\n-- Cost efficiency dashboard metrics\nWITH metrics AS (\n  SELECT\n    date_trunc('month', usage_date) as month,\n    SUM(cost) as total_cost,\n    SUM(CASE WHEN reservation_arn IS NOT NULL THEN cost END) as reserved_cost,\n    COUNT(DISTINCT user_id) as active_users\n  FROM cloud_costs\n  GROUP BY 1\n)\nSELECT\n  month,\n  total_cost,\n  reserved_cost / total_cost as reservation_coverage,\n  total_cost / active_users as cost_per_user\nFROM metrics;\n```\n\n## Quick Wins Checklist\n\n**Immediate Savings (This Week)**\n- [ ] Delete unused EBS volumes and snapshots\n- [ ] Terminate stopped EC2 instances not needed\n- [ ] Remove unused Elastic IPs\n- [ ] Delete unused load balancers\n- [ ] Review and delete old AMIs\n\n**Short-Term (This Month)**\n- [ ] Right-size underutilized instances\n- [ ] Migrate gp2 volumes to gp3\n- [ ] Implement S3 lifecycle policies\n- [ ] Enable S3 Intelligent-Tiering\n- [ ] Schedule dev/test environments\n\n**Medium-Term (This Quarter)**\n- [ ] Purchase Savings Plans for baseline\n- [ ] Implement Spot for fault-tolerant workloads\n- [ ] Set up cost allocation tags\n- [ ] Enable Cost Anomaly Detection\n- [ ] Establish FinOps practices\n","skills/cloud-architect/references/gcp.md":"# GCP Architecture Reference\n\nComprehensive guide for Google Cloud Platform services, patterns, and architecture framework.\n\n## Google Cloud Architecture Framework\n\n### Five Pillars\n\n1. **Operational Excellence**\n   - Infrastructure as Code (Deployment Manager, Terraform)\n   - CI/CD with Cloud Build\n   - Monitoring with Cloud Monitoring (Stackdriver)\n   - SRE principles and SLOs\n   - Incident management\n\n2. **Security, Privacy, and Compliance**\n   - Identity and Access Management (Cloud IAM)\n   - VPC Service Controls for data perimeter\n   - Binary Authorization for containers\n   - Data encryption (default at rest and in transit)\n   - Security Command Center\n\n3. **Reliability**\n   - Multi-zone and multi-region deployments\n   - Load balancing and autoscaling\n   - Disaster recovery planning\n   - Chaos engineering practices\n   - SLIs, SLOs, and error budgets\n\n4. **Cost Optimization**\n   - Committed Use Discounts\n   - Sustained Use Discounts (automatic)\n   - Preemptible VMs and Spot VMs\n   - Recommender for right-sizing\n   - Active Assist for optimization\n\n5. **Performance Optimization**\n   - Cloud CDN and Media CDN\n   - Caching strategies (Memorystore)\n   - Database performance tuning\n   - Network optimization (Premium vs Standard tier)\n   - Regional and zonal resource placement\n\n## Core Services Architecture\n\n### Compute\n\n**Compute Engine**\n- Machine types: E2 (cost-optimized), N2 (balanced), C2 (compute-optimized), M2 (memory-optimized)\n- Custom machine types for specific needs\n- Preemptible VMs (up to 80% discount, max 24 hours)\n- Spot VMs (similar to preemptible, better availability)\n- Instance groups: Managed (with autoscaling), unmanaged\n- Best practices: Use latest generation, committed use discounts, Spot for batch jobs\n\n**Cloud Run**\n- Fully managed serverless container platform\n- Auto-scaling to zero\n- Pay per request\n- CPU allocated only during request handling\n- Best practices: Stateless containers, optimize cold starts, use Cloud Run jobs for batch\n\n**Cloud Functions**\n- Event-driven serverless functions\n- 1st gen: HTTP and background functions\n- 2nd gen: Built on Cloud Run, better performance\n- Event sources: Pub/Sub, Cloud Storage, Firestore, HTTP\n- Best practices: Use 2nd gen, minimize cold starts, implement retry logic\n\n**Google Kubernetes Engine (GKE)**\n- Managed Kubernetes with GCP integration\n- Autopilot mode: Fully managed, per-pod pricing\n- Standard mode: More control, node management\n- Workload Identity for secure service access\n- Binary Authorization for deployment policies\n- Best practices: Use Autopilot for simplicity, enable Workload Identity, implement network policies\n\n**App Engine**\n- Fully managed platform (PaaS)\n- Standard environment (sandboxed, auto-scaling)\n- Flexible environment (Docker containers, custom runtimes)\n- Traffic splitting for canary deployments\n- Best practices: Use Standard for web apps, Flexible for custom dependencies\n\n### Storage\n\n**Cloud Storage**\n- Storage classes: Standard, Nearline (30-day), Coldline (90-day), Archive (365-day)\n- Object lifecycle management\n- Object versioning and retention policies\n- Autoclass for automatic tier transitions\n- Requester pays for data transfer\n- Best practices: Use Autoclass, enable versioning, implement lifecycle policies\n\n**Persistent Disk**\n- Types: Standard (HDD), Balanced SSD, SSD, Extreme\n- Zonal and regional persistent disks\n- Snapshots for backup (incremental)\n- Disk resize without downtime\n- Best practices: Use Balanced SSD for most workloads, enable snapshots\n\n**Filestore**\n- Managed NFS file storage\n- Tiers: Basic (1-63.9 TB), Enterprise (1-10 TB, better performance)\n- Backup to Cloud Storage\n- Best practices: Use Enterprise for production, implement backups\n\n**Cloud Storage for Firebase**\n- Object storage for mobile and web apps\n- Client SDKs for direct upload/download\n- Security rules for access control\n\n### Database\n\n**Cloud SQL**\n- Managed MySQL, PostgreSQL, SQL Server\n- High availability configuration (regional)\n- Read replicas for scaling\n- Automated backups and point-in-time recovery\n- Best practices: Enable HA, use read replicas, implement connection pooling with Cloud SQL Proxy\n\n**Cloud Spanner**\n- Globally distributed relational database\n- Horizontal scalability with strong consistency\n- Multi-region for 99.999% availability\n- TrueTime for global consistency\n- Best practices: Design proper schema splits, use commit timestamps, optimize hotspots\n\n**Firestore (Native mode)**\n- NoSQL document database\n- Real-time synchronization\n- Offline support for mobile\n- ACID transactions\n- Best practices: Design document structure carefully, use collection group queries wisely\n\n**Bigtable**\n- NoSQL wide-column database\n- Petabyte-scale with single-digit millisecond latency\n- HBase API compatible\n- Linear scalability by adding nodes\n- Best practices: Design row keys to avoid hotspots, use replication for HA\n\n**Memorystore**\n- Managed Redis and Memcached\n- Standard tier (HA with replica) and Basic tier\n- Best practices: Use Standard tier for production, implement connection pooling\n\n**BigQuery**\n- Serverless data warehouse\n- SQL analytics on petabyte-scale data\n- Column-oriented storage\n- Automatic caching and optimization\n- Best practices: Partition and cluster tables, use approximate functions, control costs with quotas\n\n### Networking\n\n**VPC (Virtual Private Cloud)**\n- Global resource (subnets are regional)\n- Custom or auto mode networks\n- Firewall rules (stateful)\n- VPC peering and Shared VPC\n- Private Google Access for GCP services\n- Best practices: Use custom mode VPC, plan IP ranges, implement firewall rules\n\n**Cloud Load Balancing**\n- Global load balancing (HTTP(S), TCP/SSL Proxy, external TCP/UDP)\n- Regional load balancing (internal HTTP(S), internal TCP/UDP)\n- Anycast IP for global distribution\n- Backend services with health checks\n- Best practices: Use global for multi-region, enable CDN, configure health checks\n\n**Cloud CDN**\n- Global content delivery network\n- Cache invalidation and signed URLs\n- Integration with Cloud Storage and compute\n- Best practices: Enable compression, use cache-control headers\n\n**Cloud Interconnect and VPN**\n- Dedicated Interconnect (10 Gbps or 100 Gbps)\n- Partner Interconnect (50 Mbps to 50 Gbps)\n- Cloud VPN (HA VPN for 99.99% SLA)\n- Best practices: Use HA VPN for redundancy, Dedicated Interconnect for high bandwidth\n\n**Cloud Armor**\n- DDoS protection and WAF\n- Preconfigured and custom rules\n- Adaptive protection (ML-based)\n- Best practices: Enable for internet-facing services, use preconfigured rules\n\n**Private Service Connect**\n- Private connectivity to Google APIs and services\n- Service Directory for service discovery\n- Best practices: Use for all managed services in production\n\n### Serverless and Event-Driven\n\n**Pub/Sub**\n- Global message queue\n- At-least-once delivery\n- Push and pull subscriptions\n- Message ordering and filtering\n- Dead-letter topics\n- Best practices: Use message attributes for filtering, implement idempotent processing\n\n**Eventarc**\n- Event-driven architecture\n- Triggers for Cloud Run, Workflows, GKE\n- Sources: Audit Logs, Pub/Sub, custom events\n- Best practices: Use for decoupled architectures, implement event filtering\n\n**Cloud Scheduler**\n- Fully managed cron service\n- HTTP, Pub/Sub, and App Engine targets\n- Best practices: Use for periodic tasks, implement retry logic\n\n**Workflows**\n- Orchestrate and automate GCP and HTTP services\n- YAML-based workflow definition\n- Built-in error handling and retry\n- Best practices: Use for complex multi-step processes, implement compensating transactions\n\n### Security and Identity\n\n**Cloud IAM**\n- Resource hierarchy: Organization -> Folders -> Projects -> Resources\n- Roles: Primitive (Owner, Editor, Viewer), Predefined, Custom\n- Service accounts for applications\n- Workload Identity for GKE\n- Best practices: Use predefined roles, least privilege, service accounts for apps\n\n**Cloud Key Management (KMS)**\n- Encryption key management\n- Customer-managed encryption keys (CMEK)\n- Hardware Security Module (HSM) backed\n- Automatic key rotation\n- Best practices: Enable automatic rotation, use separate keys per environment\n\n**Secret Manager**\n- Store API keys, passwords, certificates\n- Versioning and access control\n- Automatic rotation integration\n- Best practices: Rotate secrets regularly, use IAM for access control\n\n**Security Command Center**\n- Centralized security and risk management\n- Asset discovery and vulnerability scanning\n- Threat detection and compliance monitoring\n- Best practices: Enable all detectors, review findings regularly\n\n**VPC Service Controls**\n- Create security perimeters around GCP resources\n- Prevent data exfiltration\n- Best practices: Use for sensitive data, implement access levels\n\n### AI and Machine Learning\n\n**Vertex AI**\n- Unified ML platform\n- AutoML for custom models\n- Pre-trained models (Vision, Natural Language, etc.)\n- MLOps with pipelines\n- Best practices: Use AutoML for quick start, implement feature store\n\n**BigQuery ML**\n- Create and execute ML models using SQL\n- Model types: Linear regression, logistic regression, clustering, etc.\n- Integration with Vertex AI\n- Best practices: Use for simple models, leverage BigQuery's scale\n\n## Architecture Patterns\n\n### High Availability\n\n**Multi-Zone Pattern**\n```\nGlobal HTTP(S) Load Balancer\n    |\n    v\nManaged Instance Group (multi-zone)\n    |\n    v\nCloud SQL (regional, HA configuration)\n    |\n    v\nCloud Storage (multi-region)\n```\n\n**Multi-Region Pattern**\n```\nGlobal HTTP(S) Load Balancer\n    |\n    ├── Backend Service Region 1 (Cloud Run)\n    └── Backend Service Region 2 (Cloud Run)\n         |\n         v\n    Cloud Spanner (multi-region)\n```\n\n### Serverless Architecture\n\n**Event-Driven Pattern**\n```\nCloud Storage upload event\n    |\n    v\nPub/Sub topic\n    |\n    v\nCloud Functions (image processing)\n    |\n    v\nFirestore (metadata storage)\n```\n\n**API-First Pattern**\n```\nCloud Endpoints or API Gateway\n    |\n    v\nCloud Run (multiple services)\n    |\n    ├── Cloud SQL (transactional data)\n    └── Firestore (user data)\n```\n\n### Microservices on GKE\n\n**GKE with Service Mesh**\n```\nGlobal Load Balancer\n    |\n    v\nGKE Ingress\n    |\n    v\nAnthos Service Mesh (Istio)\n    |\n    v\nMicroservices (Cloud Spanner, Firestore, Memorystore)\n```\n\n### Data Analytics Platform\n\n```\nData Sources\n    |\n    v\nPub/Sub (streaming)\n    |\n    v\nDataflow (Apache Beam)\n    |\n    v\nBigQuery (data warehouse)\n    |\n    v\nLooker or Data Studio (visualization)\n```\n\n**Batch Processing**\n```\nCloud Storage (raw data)\n    |\n    v\nDataproc (Apache Spark)\n    |\n    v\nBigQuery (analytics)\n```\n\n## Landing Zone Design\n\n### Resource Hierarchy\n\n```\nOrganization\n├── Folders (by environment or team)\n│   ├── Production Folder\n│   │   ├── Project A\n│   │   └── Project B\n│   ├── Staging Folder\n│   └── Development Folder\n└── Shared Services Folder\n    ├── Networking Project (Shared VPC host)\n    ├── Security Project (KMS, Secret Manager)\n    └── Logging Project (centralized logs)\n```\n\n### Network Design\n\n**Shared VPC Pattern**\n```\nHost Project (networking team)\n├── Shared VPC\n│   ├── Subnet Production (region A)\n│   ├── Subnet Staging (region A)\n│   └── Subnet Development (region B)\n\nService Projects (application teams)\n├── Production Project (uses Production subnet)\n├── Staging Project (uses Staging subnet)\n└── Development Project (uses Development subnet)\n```\n\n**Hub-and-Spoke with VPN**\n```\nOn-premises Network\n    |\n    v\nCloud VPN / Interconnect\n    |\n    v\nHub VPC (shared services)\n    |\n    ├── Spoke VPC 1 (production workloads)\n    ├── Spoke VPC 2 (development workloads)\n    └── Spoke VPC 3 (analytics workloads)\n```\n\n### Governance\n\n**Organization Policies**\n- Restrict public IP assignment\n- Enforce uniform bucket-level access\n- Restrict VM external IP\n- Define allowed resource locations\n\n**IAM Strategy**\n- Use Google Groups for role assignments\n- Separate duties (network admin, security admin, etc.)\n- Service accounts per application\n- Workload Identity for GKE workloads\n\n**Logging and Monitoring**\n```\nAll Projects\n    |\n    v\nLog Router\n    |\n    ├── Cloud Logging (default sink)\n    ├── BigQuery (long-term analysis)\n    ├── Cloud Storage (archive)\n    └── Pub/Sub (real-time processing)\n```\n\n## Migration Strategies\n\n### Migrate to Virtual Machines\n\n**Tools**\n- Migrate to Virtual Machines (formerly Migrate for Compute Engine)\n- Supports VMware, AWS, Azure, physical servers\n- Agentless or agent-based migration\n- Waves and test clones\n\n**Process**\n1. Assess: Fit assessment and TCO analysis\n2. Plan: Group VMs, define migration waves\n3. Deploy: Set up infrastructure (VPC, firewall rules)\n4. Migrate: Test migration, cutover, validation\n5. Optimize: Right-sizing, committed use discounts\n\n### Database Migration\n\n**Database Migration Service**\n- Minimal downtime migrations\n- Supports MySQL, PostgreSQL, SQL Server, Oracle\n- Continuous replication for cutover flexibility\n\n**Transfer Appliance**\n- Physical device for large data transfers\n- Up to 1 PB capacity\n- Offline data transfer\n\n## Cost Optimization\n\n### Compute Savings\n\n**Committed Use Discounts**\n- 1-year or 3-year commitments\n- Up to 57% savings for VMs\n- Resource-based or spend-based\n\n**Sustained Use Discounts**\n- Automatic discounts for running VMs >25% of month\n- Up to 30% savings\n- No commitment required\n\n**Preemptible and Spot VMs**\n- Up to 80% discount\n- Can be terminated by GCP\n- Best for batch processing, fault-tolerant workloads\n\n**Recommender**\n- VM rightsizing recommendations\n- Idle resource identification\n- Committed use discount recommendations\n\n### Storage Savings\n\n**Cloud Storage**\n- Autoclass for automatic tier transitions\n- Lifecycle policies (delete or transition)\n- Nearline (30+ days), Coldline (90+ days), Archive (365+ days)\n- Requester pays for data transfer\n\n**Persistent Disk**\n- Delete orphaned disks\n- Use balanced SSD instead of SSD when possible\n- Resize disks to match actual usage\n\n### BigQuery Savings\n\n**On-Demand Pricing**\n- $5 per TB processed\n- Use partitioning and clustering\n- Query cache for free repeated queries\n\n**Flat-Rate Pricing**\n- Predictable costs for heavy users\n- Autoscaling slots available\n- Flex slots for short-term commitments\n\n**Best Practices**\n- Use approximate aggregation functions (APPROX_COUNT_DISTINCT)\n- Avoid SELECT *, specify columns\n- Use materialized views for common queries\n- Set up cost controls with custom quotas\n\n### Monitoring Costs\n\n**Cloud Billing**\n- Budgets and alerts\n- Cost breakdown by project, service, SKU\n- Export to BigQuery for analysis\n- Recommendations from Active Assist\n\n## Disaster Recovery\n\n### Backup Strategies\n\n**VM Backups**\n- Persistent disk snapshots (incremental)\n- Machine images (include metadata and config)\n- Cross-region snapshot copy\n- Snapshot schedules for automation\n\n**Database Backups**\n- Cloud SQL: Automated backups (7-365 days retention)\n- Cloud Spanner: Backups on demand or scheduled\n- Firestore: Automated daily exports\n- Bigtable: Backups to Cloud Storage\n\n### High Availability\n\n**RTO/RPO Matrix**\n\n| Pattern | RPO | RTO | Cost |\n|---------|-----|-----|------|\n| Active-Active Multi-Region | Seconds | Seconds | High |\n| Active-Passive with Replication | Minutes | Minutes | Medium |\n| Warm Standby | Minutes | 10-30 min | Medium |\n| Backup and Restore | Hours | Hours | Low |\n\n**Cloud SQL HA**\n- Regional configuration with synchronous replication\n- Automatic failover\n- 99.95% SLA (vs 99.5% for single zone)\n\n**Cloud Spanner**\n- Multi-region configuration\n- 99.999% availability SLA\n- Synchronous replication across regions\n\n### Disaster Recovery Testing\n\n- Regular DR drills (quarterly recommended)\n- Document runbooks\n- Test restoration procedures\n- Measure actual RTO/RPO vs targets\n\n## Monitoring and Observability\n\n### Cloud Monitoring (formerly Stackdriver)\n\n**Metrics**\n- System metrics (CPU, memory, disk, network)\n- Custom metrics via Cloud Monitoring API\n- Metric scopes for multi-project monitoring\n- Uptime checks for availability\n\n**Dashboards and Charts**\n- Predefined dashboards for GCP services\n- Custom dashboards with filters and grouping\n- SLO monitoring with error budgets\n\n### Cloud Logging\n\n**Log Types**\n- Admin Activity logs (always enabled, no charge)\n- Data Access logs (must be enabled)\n- System Event logs\n- Access Transparency logs (for Google access)\n\n**Log Sinks**\n- Route logs to BigQuery, Cloud Storage, Pub/Sub\n- Aggregated sinks at organization/folder level\n- Exclusion filters to reduce costs\n\n### Cloud Trace\n\n**Distributed Tracing**\n- Automatic instrumentation for App Engine, Cloud Run, GKE\n- Manual instrumentation with client libraries\n- Latency analysis and performance insights\n- Integration with Zipkin\n\n### Cloud Profiler\n\n**Continuous Profiling**\n- CPU and memory profiling\n- Low overhead (< 0.5% CPU)\n- Flame graphs for visualization\n- Supported languages: Java, Go, Python, Node.js\n\n### Error Reporting\n\n**Aggregated Error Tracking**\n- Automatic error grouping\n- Stack trace analysis\n- Integration with Cloud Logging\n- Notifications for new errors\n","skills/cloud-architect/references/multi-cloud.md":"# Multi-Cloud Architecture Reference\n\nComprehensive guide for multi-cloud strategies, abstraction layers, portability patterns, and vendor lock-in mitigation.\n\n## Multi-Cloud Strategy\n\n### When to Use Multi-Cloud\n\n**Valid Use Cases**\n- Regulatory compliance requiring data residency in specific regions\n- Best-of-breed service selection (BigQuery for analytics, AWS for ML)\n- Acquisition integration (different clouds in merged organizations)\n- Disaster recovery with cloud provider as failure domain\n- Negotiating leverage with cloud vendors\n\n**Poor Reasons for Multi-Cloud**\n- \"Avoiding vendor lock-in\" without specific exit scenario\n- Assuming portability is free (it has significant costs)\n- Political decisions without technical justification\n- Spreading workloads arbitrarily across providers\n\n### Multi-Cloud Patterns\n\n**Active-Active**\n```\nUsers -> Global Load Balancer\n              |\n    +---------+---------+\n    |                   |\n  AWS Region        GCP Region\n    |                   |\n    +----> Data Sync <--+\n```\n- Highest complexity and cost\n- Best for global latency optimization\n- Requires robust data synchronization\n\n**Active-Passive (DR)**\n```\nUsers -> Primary (AWS)\n              |\n         [Failover]\n              |\n         Secondary (Azure)\n```\n- Lower complexity than active-active\n- Cloud provider becomes failure domain\n- Cold or warm standby in secondary cloud\n\n**Segmented by Workload**\n```\nAnalytics -> GCP (BigQuery)\nCore App  -> AWS (ECS, RDS)\nOffice    -> Azure (M365 integration)\n```\n- Each workload on best-fit cloud\n- No cross-cloud data synchronization\n- Simplest multi-cloud pattern\n\n## Abstraction Layers\n\n### Infrastructure Abstraction\n\n**Terraform (Recommended)**\n```hcl\n# Provider-agnostic module structure\nmodule \"compute\" {\n  source = \"./modules/compute\"\n\n  provider_type = var.cloud_provider  # aws, azure, gcp\n  instance_type = var.instance_size\n  region        = var.region\n}\n\n# Provider-specific implementations\n# modules/compute/aws.tf\nresource \"aws_instance\" \"main\" {\n  count         = var.provider_type == \"aws\" ? 1 : 0\n  ami           = data.aws_ami.latest.id\n  instance_type = local.aws_instance_map[var.instance_size]\n}\n\n# modules/compute/azure.tf\nresource \"azurerm_virtual_machine\" \"main\" {\n  count    = var.provider_type == \"azure\" ? 1 : 0\n  vm_size  = local.azure_vm_map[var.instance_size]\n}\n```\n\n**Pulumi (Code-First)**\n```typescript\n// Abstract cloud resources with TypeScript\ninterface ComputeConfig {\n  size: \"small\" | \"medium\" | \"large\";\n  region: string;\n}\n\nfunction createCompute(config: ComputeConfig, provider: \"aws\" | \"gcp\") {\n  if (provider === \"aws\") {\n    return new aws.ec2.Instance(\"web\", {\n      instanceType: sizeMap.aws[config.size],\n      // ...\n    });\n  } else {\n    return new gcp.compute.Instance(\"web\", {\n      machineType: sizeMap.gcp[config.size],\n      // ...\n    });\n  }\n}\n```\n\n### Container Orchestration (Kubernetes)\n\n**Portable Kubernetes Deployment**\n```yaml\n# Same manifests work across EKS, AKS, GKE\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: web-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: web\n  template:\n    spec:\n      containers:\n      - name: web\n        image: myregistry/web:v1\n        resources:\n          requests:\n            memory: \"256Mi\"\n            cpu: \"250m\"\n          limits:\n            memory: \"512Mi\"\n            cpu: \"500m\"\n```\n\n**Cloud-Specific Considerations**\n| Feature | EKS | AKS | GKE |\n|---------|-----|-----|-----|\n| Load Balancer | ALB/NLB annotations | Azure LB | GCP LB |\n| Storage Class | gp3, io2 | managed-premium | pd-ssd |\n| IAM Integration | IRSA | Workload Identity | Workload Identity |\n| Ingress | AWS ALB Controller | AGIC | GKE Ingress |\n\n### Application Abstraction\n\n**Database Abstraction**\n```python\n# Use standard protocols (SQL, Redis, S3 API)\nfrom sqlalchemy import create_engine\n\n# Same code works with:\n# - AWS RDS PostgreSQL\n# - Azure Database for PostgreSQL\n# - GCP Cloud SQL PostgreSQL\n# - Self-managed PostgreSQL\n\nDATABASE_URL = os.environ[\"DATABASE_URL\"]\nengine = create_engine(DATABASE_URL)\n```\n\n**Object Storage Abstraction**\n```python\nimport boto3\nfrom botocore.config import Config\n\n# S3-compatible API works with:\n# - AWS S3\n# - GCP Cloud Storage (interoperability mode)\n# - MinIO\n# - Cloudflare R2\n\ns3_client = boto3.client(\n    's3',\n    endpoint_url=os.environ.get(\"S3_ENDPOINT\"),  # Override for non-AWS\n    aws_access_key_id=os.environ[\"ACCESS_KEY\"],\n    aws_secret_access_key=os.environ[\"SECRET_KEY\"],\n)\n```\n\n## Data Synchronization\n\n### Database Replication\n\n**Cross-Cloud PostgreSQL**\n```\nAWS RDS Primary\n      |\n      | Logical Replication\n      v\nGCP Cloud SQL Replica (read-only)\n```\n\nConfiguration:\n```sql\n-- On primary (AWS RDS)\nCREATE PUBLICATION my_publication FOR ALL TABLES;\n\n-- On replica (GCP Cloud SQL)\nCREATE SUBSCRIPTION my_subscription\n  CONNECTION 'host=aws-rds-endpoint dbname=mydb user=repl'\n  PUBLICATION my_publication;\n```\n\n**Conflict Resolution Strategies**\n- Last-write-wins (timestamp-based)\n- Application-level conflict resolution\n- CRDT data structures for eventually consistent data\n- Avoid multi-master for transactional data\n\n### Object Storage Sync\n\n**Rclone for Cross-Cloud Sync**\n```bash\n# Sync S3 to GCS\nrclone sync s3:my-bucket gcs:my-bucket \\\n  --transfers 32 \\\n  --checkers 16 \\\n  --s3-upload-concurrency 8\n\n# Bidirectional sync with conflict handling\nrclone bisync s3:bucket gcs:bucket \\\n  --conflict-resolve newer\n```\n\n**Event-Driven Replication**\n```\nS3 Bucket -> S3 Event -> Lambda -> GCS Upload\n                              |\n                              v\n                       Consistency Check\n```\n\n## Vendor Lock-In Mitigation\n\n### Lock-In Risk Assessment\n\n| Service Type | Lock-In Risk | Mitigation Strategy |\n|--------------|--------------|---------------------|\n| Compute (VMs) | Low | Standard OS images, IaC |\n| Kubernetes | Low | Portable manifests, avoid proprietary add-ons |\n| Object Storage | Low | S3-compatible API, standard formats |\n| Managed Databases | Medium | Standard SQL, logical backups |\n| Serverless Functions | High | Abstraction layers, containers |\n| Proprietary AI/ML | High | Open-source alternatives, ONNX models |\n| Managed Services | High | Evaluate portability before adoption |\n\n### Mitigation Strategies\n\n**1. Use Open Standards**\n- SQL databases over proprietary NoSQL\n- Kubernetes over ECS/Cloud Run\n- S3 API for object storage\n- OpenTelemetry for observability\n- OIDC for authentication\n\n**2. Abstract Proprietary Services**\n```typescript\n// Wrap cloud-specific services\ninterface QueueService {\n  send(message: string): Promise<void>;\n  receive(): Promise<string>;\n}\n\nclass SQSQueue implements QueueService {\n  async send(message: string) {\n    await this.sqsClient.sendMessage({ QueueUrl: this.url, MessageBody: message });\n  }\n}\n\nclass PubSubQueue implements QueueService {\n  async send(message: string) {\n    await this.pubsubClient.topic(this.topic).publish(Buffer.from(message));\n  }\n}\n\n// Factory pattern for cloud selection\nfunction createQueue(provider: string): QueueService {\n  switch (provider) {\n    case \"aws\": return new SQSQueue();\n    case \"gcp\": return new PubSubQueue();\n  }\n}\n```\n\n**3. Maintain Exit Capability**\n- Regular data export testing\n- Document cloud-specific dependencies\n- Keep IaC portable across providers\n- Estimate migration effort annually\n\n**4. Containerize Everything**\n```dockerfile\n# Portable container runs anywhere\nFROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\nEXPOSE 3000\nCMD [\"node\", \"server.js\"]\n```\n\n## Network Connectivity\n\n### Cross-Cloud Networking\n\n**VPN Interconnect**\n```\nAWS VPC                          GCP VPC\n   |                                |\n   +---> AWS VPN Gateway            |\n              |                     |\n              | IPsec tunnel        |\n              |                     |\n              +---> GCP Cloud VPN <-+\n```\n\n**Dedicated Interconnect (Enterprise)**\n```\nOn-Premises Data Center\n         |\n    +----+----+\n    |         |\nAWS Direct  GCP Cloud\nConnect     Interconnect\n    |         |\n    v         v\nAWS VPC    GCP VPC\n    |         |\n    +----+----+\n         |\n    Transit Hub (e.g., Megaport, Equinix)\n```\n\n### Service Mesh Across Clouds\n\n**Istio Multi-Cluster**\n```yaml\n# Primary cluster (AWS EKS)\napiVersion: install.istio.io/v1alpha1\nkind: IstioOperator\nspec:\n  values:\n    global:\n      meshID: multi-cloud-mesh\n      multiCluster:\n        clusterName: eks-primary\n      network: aws-network\n\n# Remote cluster (GCP GKE)\nspec:\n  values:\n    global:\n      meshID: multi-cloud-mesh\n      multiCluster:\n        clusterName: gke-secondary\n      network: gcp-network\n```\n\n## Cost Management\n\n### Cross-Cloud Cost Visibility\n\n**FinOps Tools**\n- CloudHealth by VMware\n- Apptio Cloudability\n- Spot.io (now part of NetApp)\n- Kubecost for Kubernetes\n\n**Unified Tagging Strategy**\n```\nRequired Tags (all clouds):\n- environment: prod/staging/dev\n- cost-center: engineering/marketing/sales\n- owner: team-name\n- project: project-code\n- managed-by: terraform/manual\n```\n\n### Cost Comparison Framework\n\n```\n| Workload Type | AWS | Azure | GCP | Decision |\n|---------------|-----|-------|-----|----------|\n| General Compute | EC2 m5 | D-series | n2-standard | Compare $/vCPU/hour |\n| GPU Training | p4d | NC-series | A2 | GCP often cheaper |\n| Object Storage | S3 | Blob | GCS | Similar, check egress |\n| Analytics | Redshift | Synapse | BigQuery | BigQuery for ad-hoc |\n| Kubernetes | EKS | AKS | GKE | GKE Autopilot simplest |\n```\n\n## Observability\n\n### Unified Monitoring Stack\n\n**OpenTelemetry Collector**\n```yaml\n# Collect from all clouds, export to single backend\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n      http:\n\nprocessors:\n  batch:\n  attributes:\n    actions:\n      - key: cloud.provider\n        action: upsert\n        value: ${CLOUD_PROVIDER}\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n  jaeger:\n    endpoint: jaeger:14250\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch, attributes]\n      exporters: [jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch, attributes]\n      exporters: [prometheus]\n```\n\n**Grafana for Unified Dashboards**\n- AWS CloudWatch data source\n- Azure Monitor data source\n- GCP Cloud Monitoring data source\n- Single pane of glass across all clouds\n\n## Security Considerations\n\n### Identity Federation\n\n**Cross-Cloud Identity**\n```\nCorporate IdP (Okta/Azure AD)\n         |\n    SAML/OIDC\n         |\n    +----+----+----+\n    |    |    |    |\n  AWS  Azure  GCP  K8s\n  IAM   AD   IAM  RBAC\n```\n\n### Secrets Management\n\n**HashiCorp Vault (Cloud-Agnostic)**\n```hcl\n# Single secrets management across clouds\nresource \"vault_aws_secret_backend_role\" \"aws_role\" {\n  backend = vault_aws_secret_backend.aws.path\n  name    = \"app-role\"\n  credential_type = \"iam_user\"\n}\n\nresource \"vault_gcp_secret_roleset\" \"gcp_role\" {\n  backend     = vault_gcp_secret_backend.gcp.path\n  roleset     = \"app-role\"\n  project     = var.gcp_project\n  token_scopes = [\"https://www.googleapis.com/auth/cloud-platform\"]\n}\n```\n\n### Network Security\n\n**Zero-Trust Across Clouds**\n- mTLS between all services (service mesh)\n- No implicit trust based on network location\n- Identity-based access control\n- Encrypted transit between clouds (VPN/interconnect)\n","skills/code-documenter/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-documenter%2FSKILL.md -->","skills/code-documenter/references/api-docs-fastapi-django.md":"# API Documentation: FastAPI & Django\n\n## FastAPI (Auto-generates from types)\n\nFastAPI automatically generates OpenAPI documentation from type hints and docstrings.\n\n### Endpoint Documentation\n\n```python\nfrom fastapi import FastAPI, HTTPException, status\nfrom pydantic import BaseModel, Field\n\nclass UserCreate(BaseModel):\n    \"\"\"User creation request body.\"\"\"\n\n    name: str = Field(..., min_length=1, max_length=100, example=\"John Doe\")\n    email: str = Field(..., example=\"john@example.com\")\n\nclass UserResponse(BaseModel):\n    \"\"\"User response with generated ID.\"\"\"\n\n    id: int = Field(..., example=1)\n    name: str\n    email: str\n\n@app.post(\n    \"/users\",\n    response_model=UserResponse,\n    status_code=status.HTTP_201_CREATED,\n    summary=\"Create a new user\",\n    tags=[\"Users\"],\n)\nasync def create_user(user: UserCreate) -> UserResponse:\n    \"\"\"Create a new user account.\n\n    Args:\n        user: User creation data including name and email.\n\n    Returns:\n        Created user with generated ID.\n\n    Raises:\n        HTTPException: 400 if email already exists.\n    \"\"\"\n```\n\n### Router with Tags\n\n```python\nfrom fastapi import APIRouter\n\nrouter = APIRouter(\n    prefix=\"/users\",\n    tags=[\"Users\"],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n@router.get(\n    \"/{user_id}\",\n    response_model=UserResponse,\n    summary=\"Get user by ID\",\n)\nasync def get_user(user_id: int) -> UserResponse:\n    \"\"\"Retrieve a user by their unique identifier.\"\"\"\n```\n\n## Django REST Framework (drf-spectacular)\n\n### ViewSet Documentation\n\n```python\nfrom rest_framework import viewsets, status\nfrom rest_framework.decorators import action\nfrom drf_spectacular.utils import extend_schema, OpenApiParameter\n\nclass UserViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    ViewSet for managing user accounts.\n\n    list: Get all users with pagination.\n    create: Create a new user account.\n    retrieve: Get a specific user by ID.\n    update: Update all user fields.\n    partial_update: Update specific user fields.\n    destroy: Delete a user account.\n    \"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n    @extend_schema(\n        summary=\"Get current user\",\n        description=\"Returns the authenticated user's profile\",\n        responses={200: UserSerializer},\n    )\n    @action(detail=False, methods=[\"get\"])\n    def me(self, request):\n        \"\"\"Get the authenticated user's profile.\"\"\"\n        serializer = self.get_serializer(request.user)\n        return Response(serializer.data)\n```\n\n### Serializer Documentation\n\n```python\nfrom rest_framework import serializers\n\nclass UserSerializer(serializers.ModelSerializer):\n    \"\"\"Serializer for user model with validation.\"\"\"\n\n    class Meta:\n        model = User\n        fields = [\"id\", \"name\", \"email\", \"created_at\"]\n        read_only_fields = [\"id\", \"created_at\"]\n\n    name = serializers.CharField(\n        help_text=\"User's display name\",\n        max_length=100,\n    )\n    email = serializers.EmailField(\n        help_text=\"User's email address (unique)\",\n    )\n```\n\n### Custom Schema\n\n```python\nfrom drf_spectacular.utils import extend_schema, OpenApiExample\n\n@extend_schema(\n    request=UserCreateSerializer,\n    responses={\n        201: UserSerializer,\n        400: OpenApiTypes.OBJECT,\n    },\n    examples=[\n        OpenApiExample(\n            \"Valid request\",\n            value={\"name\": \"John\", \"email\": \"john@example.com\"},\n        ),\n    ],\n)\ndef create(self, request):\n    \"\"\"Create a new user.\"\"\"\n```\n\n## Quick Reference\n\n| Framework | Documentation Source | Output |\n|-----------|---------------------|--------|\n| FastAPI | Type hints + docstrings | Auto Swagger UI |\n| DRF | Serializers + drf-spectacular | Auto Swagger UI |\n\n| FastAPI Decorator | Purpose |\n|-------------------|---------|\n| `summary` | Short endpoint description |\n| `description` | Detailed description |\n| `tags` | Group endpoints |\n| `response_model` | Response schema |\n| `responses` | Additional response codes |\n\n| DRF Decorator | Purpose |\n|---------------|---------|\n| `@extend_schema` | Customize schema |\n| `OpenApiParameter` | Query/path params |\n| `OpenApiExample` | Request examples |\n","skills/code-documenter/references/api-docs-nestjs-express.md":"# API Documentation: NestJS & Express\n\n## NestJS (@nestjs/swagger)\n\nNestJS requires explicit decorators for OpenAPI documentation.\n\n### Controller Documentation\n\n```typescript\nimport { Controller, Post, Body, Get, Param } from '@nestjs/common';\nimport {\n  ApiTags,\n  ApiOperation,\n  ApiResponse,\n  ApiParam,\n  ApiBearerAuth,\n} from '@nestjs/swagger';\n\n@ApiTags('Users')\n@ApiBearerAuth()\n@Controller('users')\nexport class UsersController {\n  @Post()\n  @ApiOperation({ summary: 'Create a new user' })\n  @ApiResponse({\n    status: 201,\n    description: 'User created successfully',\n    type: UserDto,\n  })\n  @ApiResponse({\n    status: 400,\n    description: 'Invalid input data',\n  })\n  async create(@Body() dto: CreateUserDto): Promise<UserDto> {\n    return this.usersService.create(dto);\n  }\n\n  @Get(':id')\n  @ApiOperation({ summary: 'Get user by ID' })\n  @ApiParam({\n    name: 'id',\n    description: 'User unique identifier',\n    example: '123',\n  })\n  @ApiResponse({ status: 200, type: UserDto })\n  @ApiResponse({ status: 404, description: 'User not found' })\n  async findOne(@Param('id') id: string): Promise<UserDto> {\n    return this.usersService.findOne(id);\n  }\n}\n```\n\n### DTO Documentation\n\n```typescript\nimport { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsEmail, IsString, MinLength } from 'class-validator';\n\nexport class CreateUserDto {\n  @ApiProperty({\n    description: \"User's display name\",\n    example: 'John Doe',\n    minLength: 1,\n    maxLength: 100,\n  })\n  @IsString()\n  @MinLength(1)\n  name: string;\n\n  @ApiProperty({\n    description: \"User's email address\",\n    example: 'john@example.com',\n  })\n  @IsEmail()\n  email: string;\n\n  @ApiPropertyOptional({\n    description: 'Profile picture URL',\n    example: 'https://example.com/avatar.jpg',\n  })\n  avatarUrl?: string;\n}\n```\n\n## Express (swagger-jsdoc)\n\nExpress uses JSDoc comments with swagger annotations.\n\n### Setup\n\n```javascript\nconst swaggerJsdoc = require('swagger-jsdoc');\nconst swaggerUi = require('swagger-ui-express');\n\nconst options = {\n  definition: {\n    openapi: '3.0.0',\n    info: {\n      title: 'API Documentation',\n      version: '1.0.0',\n    },\n  },\n  apis: ['./routes/*.js'],\n};\n\nconst specs = swaggerJsdoc(options);\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));\n```\n\n### Route Documentation\n\n```javascript\n/**\n * @swagger\n * /users:\n *   post:\n *     summary: Create a new user\n *     tags: [Users]\n *     requestBody:\n *       required: true\n *       content:\n *         application/json:\n *           schema:\n *             $ref: '#/components/schemas/CreateUser'\n *     responses:\n *       201:\n *         description: User created successfully\n *         content:\n *           application/json:\n *             schema:\n *               $ref: '#/components/schemas/User'\n *       400:\n *         description: Invalid input\n */\nrouter.post('/users', createUser);\n\n/**\n * @swagger\n * /users/{id}:\n *   get:\n *     summary: Get user by ID\n *     tags: [Users]\n *     parameters:\n *       - in: path\n *         name: id\n *         required: true\n *         schema:\n *           type: string\n *         description: User ID\n *     responses:\n *       200:\n *         description: User found\n *         content:\n *           application/json:\n *             schema:\n *               $ref: '#/components/schemas/User'\n *       404:\n *         description: User not found\n */\nrouter.get('/users/:id', getUser);\n```\n\n### Schema Documentation\n\n```javascript\n/**\n * @swagger\n * components:\n *   schemas:\n *     CreateUser:\n *       type: object\n *       required:\n *         - name\n *         - email\n *       properties:\n *         name:\n *           type: string\n *           description: User's display name\n *           example: John Doe\n *         email:\n *           type: string\n *           format: email\n *           description: User's email address\n *           example: john@example.com\n *     User:\n *       allOf:\n *         - $ref: '#/components/schemas/CreateUser'\n *         - type: object\n *           properties:\n *             id:\n *               type: string\n *               description: Unique identifier\n *             createdAt:\n *               type: string\n *               format: date-time\n */\n```\n\n## Quick Reference\n\n| NestJS Decorator | Purpose |\n|------------------|---------|\n| `@ApiTags()` | Group endpoints |\n| `@ApiOperation()` | Endpoint summary |\n| `@ApiResponse()` | Response documentation |\n| `@ApiParam()` | Path parameter |\n| `@ApiQuery()` | Query parameter |\n| `@ApiBody()` | Request body |\n| `@ApiBearerAuth()` | Auth requirement |\n| `@ApiProperty()` | DTO property |\n\n| Express swagger-jsdoc | Purpose |\n|-----------------------|---------|\n| `@swagger` | Start swagger block |\n| `tags` | Group endpoints |\n| `summary` | Short description |\n| `parameters` | Path/query params |\n| `requestBody` | Request body schema |\n| `responses` | Response schemas |\n| `$ref` | Reference schema |\n","skills/code-documenter/references/coverage-reports.md":"# Coverage Reports\n\n## Documentation Coverage Report Template\n\n```markdown\n# Documentation Report: {project_name}\n\n## Summary\n- **Files analyzed**: 45\n- **Functions documented**: 120/150 (80%)\n- **Classes documented**: 25/25 (100%)\n- **API endpoints documented**: 30/30 (100%)\n\n## Coverage Before/After\n- Before: 45%\n- After: 92%\n\n## Files Modified\n\n| File | Functions Added | Notes |\n|------|-----------------|-------|\n| src/services/user.ts | 8 | All public methods |\n| src/services/auth.ts | 5 | Added examples |\n| src/controllers/users.ts | 6 | Added @Api decorators |\n| src/dto/user.dto.ts | 4 | Added @ApiProperty |\n\n## API Documentation\n\n- **Framework**: NestJS\n- **Strategy**: @nestjs/swagger decorators\n- **Swagger UI**: /api/docs\n- **OpenAPI spec**: /api-json\n\n## Documentation Style\n\n- **Python**: Google style docstrings\n- **TypeScript**: JSDoc with @param, @returns\n- **API**: OpenAPI 3.0 via decorators\n\n## Next Steps\n\n### Recommendations\n1. Run `npm run docs:lint` to validate JSDoc\n2. Add `eslint-plugin-jsdoc` to enforce documentation\n3. Consider adding examples for complex functions\n4. Set up documentation CI checks\n\n### Missing Documentation\n| File | Missing | Priority |\n|------|---------|----------|\n| src/utils/crypto.ts | 3 functions | High |\n| src/helpers/date.ts | 2 functions | Medium |\n\n### CI Integration\n```yaml\n# Add to CI pipeline\n- name: Check documentation\n  run: npm run docs:check\n\n- name: Generate API docs\n  run: npm run docs:generate\n```\n```\n\n## Checklist During Documentation\n\n```markdown\n## Documentation Checklist\n\n### Before Starting\n- [ ] Confirmed format preference (Google/JSDoc/etc.)\n- [ ] Identified files to exclude (tests, generated)\n- [ ] Detected framework for API docs\n\n### Functions/Methods\n- [ ] All public functions documented\n- [ ] Parameters described with types\n- [ ] Return values documented\n- [ ] Exceptions/errors documented\n- [ ] Examples added for complex functions\n\n### Classes\n- [ ] Class purpose described\n- [ ] Constructor parameters documented\n- [ ] Public methods documented\n- [ ] Important attributes explained\n\n### API Endpoints\n- [ ] All endpoints have summaries\n- [ ] Request bodies documented\n- [ ] Response schemas defined\n- [ ] Error responses documented\n- [ ] Authentication requirements noted\n\n### Final Checks\n- [ ] Ran documentation linter\n- [ ] Verified Swagger UI renders correctly\n- [ ] No inaccurate documentation\n- [ ] Coverage report generated\n```\n\n## Framework-Specific Linting\n\n```bash\n# JavaScript/TypeScript - ESLint\nnpm install eslint-plugin-jsdoc --save-dev\n# Add to .eslintrc: \"plugins\": [\"jsdoc\"]\n\n# Python - pydocstyle\npip install pydocstyle\npydocstyle --convention=google src/\n\n# Python - interrogate (coverage)\npip install interrogate\ninterrogate -v src/\n```\n\n## Quick Reference\n\n| Metric | Good | Acceptable | Poor |\n|--------|------|------------|------|\n| Function coverage | >90% | 70-90% | <70% |\n| Class coverage | 100% | >90% | <90% |\n| API endpoint coverage | 100% | 100% | <100% |\n| Example coverage | >50% | 30-50% | <30% |\n","skills/code-documenter/references/documentation-systems.md":"# Documentation Systems & Infrastructure\n\n## Static Site Generators\n\n### Docusaurus (Meta)\n\n```bash\n# Setup\nnpx create-docusaurus@latest docs classic\ncd docs && npm start\n\n# Structure\ndocs/\n├── docs/           # Documentation pages\n├── blog/           # Blog posts\n├── src/\n│   └── pages/      # Custom pages\n└── docusaurus.config.js\n```\n\n**docusaurus.config.js:**\n```javascript\nmodule.exports = {\n  title: 'My API',\n  tagline: 'Build amazing things',\n  url: 'https://docs.example.com',\n  baseUrl: '/',\n\n  themeConfig: {\n    navbar: {\n      items: [\n        {to: '/docs/intro', label: 'Docs', position: 'left'},\n        {to: '/api', label: 'API', position: 'left'},\n      ],\n    },\n\n    // Algolia search\n    algolia: {\n      apiKey: '<algolia-search-api-key>',\n      indexName: 'your_index',\n      contextualSearch: true,\n    },\n\n    prism: {\n      theme: lightCodeTheme,\n      darkTheme: darkCodeTheme,\n      additionalLanguages: ['python', 'rust'],\n    },\n  },\n};\n```\n\n### MkDocs (Python)\n\n```yaml\n# mkdocs.yml\nsite_name: My API Documentation\ntheme:\n  name: material\n  features:\n    - navigation.tabs\n    - navigation.sections\n    - toc.integrate\n    - search.suggest\n    - search.highlight\n  palette:\n    - scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n\nplugins:\n  - search\n  - mkdocstrings:\n      handlers:\n        python:\n          options:\n            show_source: true\n  - git-revision-date-localized\n\nmarkdown_extensions:\n  - pymdownx.highlight\n  - pymdownx.superfences\n  - admonition\n  - codehilite\n\nnav:\n  - Home: index.md\n  - Getting Started: getting-started.md\n  - API Reference: api/\n```\n\n### VitePress (Vue)\n\n```typescript\n// .vitepress/config.ts\nexport default defineConfig({\n  title: 'API Docs',\n  description: 'Developer documentation',\n\n  themeConfig: {\n    nav: [\n      { text: 'Guide', link: '/guide/' },\n      { text: 'API', link: '/api/' },\n    ],\n\n    sidebar: {\n      '/guide/': [\n        {\n          text: 'Introduction',\n          items: [\n            { text: 'Getting Started', link: '/guide/getting-started' },\n            { text: 'Configuration', link: '/guide/config' },\n          ],\n        },\n      ],\n    },\n\n    search: {\n      provider: 'local',\n    },\n\n    editLink: {\n      pattern: 'https://github.com/user/repo/edit/main/docs/:path',\n    },\n  },\n});\n```\n\n## Multi-Version Documentation\n\n### Version Switcher\n\n```javascript\n// Docusaurus versions\n{\n  versions: {\n    current: {\n      label: '2.0 (Next)',\n      path: 'next',\n    },\n  },\n  onlyIncludeVersions: ['current', '1.5', '1.4'],\n}\n```\n\n### Migration Guides\n\n```markdown\n# Migration Guide: v1 to v2\n\n## Breaking Changes\n\n### Authentication\n**v1:**\n```python\nclient.authenticate(api_key)\n```\n\n**v2:**\n```python\nclient = Client(api_key=api_key)  # Pass in constructor\n```\n\n### Renamed Methods\n| v1 | v2 | Notes |\n|----|----|----- |\n| `get_user()` | `fetch_user()` | Async now |\n| `delete_user()` | `remove_user()` | Returns Promise |\n\n## Deprecation Timeline\n- v1.x: Supported until Dec 2025\n- v2.0: Released Jan 2025\n- v2.1: Current (June 2025)\n```\n\n## Search Implementation\n\n### Algolia DocSearch\n\n```html\n<!-- Add to theme -->\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@docsearch/css@3\" />\n\n<script src=\"https://cdn.jsdelivr.net/npm/@docsearch/js@3\"></script>\n<script>\n  docsearch({\n    appId: '<algolia-app-id>',\n    apiKey: '<algolia-search-api-key>',\n    indexName: 'your_index',\n    container: '#docsearch',\n  });\n</script>\n```\n\n### Local Search (Lunr.js)\n\n```javascript\nconst idx = lunr(function() {\n  this.ref('id');\n  this.field('title', { boost: 10 });\n  this.field('content');\n\n  documents.forEach(doc => this.add(doc));\n});\n\n// Search\nconst results = idx.search('authentication');\n```\n\n## Documentation Testing\n\n### Link Checking\n\n```bash\n# linkcheck (Python)\npip install linkchecker\nlinkchecker http://localhost:3000/docs\n\n# broken-link-checker (Node)\nnpm install -g broken-link-checker\nblc http://localhost:3000 -ro\n```\n\n### Code Example Testing\n\n```python\n# doctest for Python examples\n\"\"\"\n>>> add(2, 3)\n5\n>>> add(-1, 1)\n0\n\"\"\"\n\n# Run tests\npython -m doctest -v docs/*.md\n```\n\n```javascript\n// Jest for TypeScript examples\n// Extract code blocks and test\nimport { runExamples } from './test-docs';\n\ntest('API examples work', async () => {\n  const examples = extractExamples('./docs/api.md');\n  await expect(runExamples(examples)).resolves.toBeTruthy();\n});\n```\n\n## Performance Optimization\n\n### Build Optimization\n\n```javascript\n// Webpack/Vite config\nexport default {\n  build: {\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          'vendor': ['react', 'react-dom'],\n        },\n      },\n    },\n  },\n\n  optimizeDeps: {\n    include: ['prismjs'],\n  },\n};\n```\n\n### CDN & Caching\n\n```nginx\n# nginx.conf\nlocation /docs {\n  expires 1y;\n  add_header Cache-Control \"public, immutable\";\n}\n\nlocation ~* \\.(html)$ {\n  expires 1h;\n  add_header Cache-Control \"public, must-revalidate\";\n}\n```\n\n## Analytics Integration\n\n### Google Analytics\n\n```javascript\n// Docusaurus\ngtag: {\n  trackingID: 'G-XXXXXXXXXX',\n  anonymizeIP: true,\n},\n```\n\n### Custom Analytics\n\n```javascript\n// Track search queries\nfunction trackSearch(query, results) {\n  analytics.track('docs_search', {\n    query,\n    resultCount: results.length,\n    timestamp: new Date(),\n  });\n}\n```\n\n## Quick Reference\n\n| Tool | Best For | Tech Stack |\n|------|----------|-----------|\n| Docusaurus | React projects, versioning | React, MDX |\n| MkDocs | Python projects, simple setup | Python, Jinja2 |\n| VitePress | Vue projects, fast builds | Vue, Vite |\n| Nextra | Next.js integration | React, Next.js |\n| Mintlify | Modern UI, AI search | React |\n\n| Search Solution | Cost | Features |\n|----------------|------|----------|\n| Algolia DocSearch | Free (OSS) | Fast, typo-tolerant |\n| Local (Lunr.js) | Free | Offline, no server |\n| Typesense | Free (self-host) | Privacy-focused |\n| Meilisearch | Free (self-host) | Fast, relevance |\n","skills/code-documenter/references/interactive-api-docs.md":"# Interactive API Documentation\n\n## OpenAPI 3.1 Advanced Features\n\n### Reusable Components\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: Users API\n  version: 2.0.0\n\ncomponents:\n  # Reusable schemas\n  schemas:\n    User:\n      type: object\n      required: [id, email]\n      properties:\n        id:\n          type: string\n          format: uuid\n          example: \"123e4567-e89b-12d3-a456-426614174000\"\n        email:\n          type: string\n          format: email\n          example: \"user@example.com\"\n\n    Error:\n      type: object\n      properties:\n        code:\n          type: string\n        message:\n          type: string\n        details:\n          type: object\n\n    PaginatedResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items: {}\n        total:\n          type: integer\n        page:\n          type: integer\n\n  # Reusable parameters\n  parameters:\n    PageParam:\n      name: page\n      in: query\n      schema:\n        type: integer\n        default: 1\n        minimum: 1\n\n    LimitParam:\n      name: limit\n      in: query\n      schema:\n        type: integer\n        default: 20\n        minimum: 1\n        maximum: 100\n\n  # Security schemes\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\n    ApiKeyAuth:\n      type: apiKey\n      in: header\n      name: X-API-Key\n\n    OAuth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://api.example.com/oauth/authorize\n          tokenUrl: https://api.example.com/oauth/token\n          scopes:\n            read:users: Read user data\n            write:users: Modify user data\n\n  # Reusable responses\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    Unauthorized:\n      description: Authentication required\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\npaths:\n  /users:\n    get:\n      summary: List users\n      parameters:\n        - $ref: '#/components/parameters/PageParam'\n        - $ref: '#/components/parameters/LimitParam'\n      security:\n        - BearerAuth: []\n      responses:\n        '200':\n          description: Success\n          content:\n            application/json:\n              schema:\n                allOf:\n                  - $ref: '#/components/schemas/PaginatedResponse'\n                  - type: object\n                    properties:\n                      data:\n                        type: array\n                        items:\n                          $ref: '#/components/schemas/User'\n```\n\n## Interactive Documentation Portals\n\n### Swagger UI Customization\n\n```javascript\n// Custom Swagger UI\nconst swaggerUi = require('swagger-ui-express');\nconst swaggerDocument = require('./openapi.json');\n\nconst options = {\n  customCss: '.swagger-ui .topbar { display: none }',\n  customSiteTitle: \"API Docs\",\n  customfavIcon: \"/favicon.ico\",\n  swaggerOptions: {\n    persistAuthorization: true,\n    displayRequestDuration: true,\n    filter: true,\n    tryItOutEnabled: true,\n    requestInterceptor: (req) => {\n      req.headers['X-Custom-Header'] = 'value';\n      return req;\n    },\n  },\n};\n\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));\n```\n\n### Redoc (Modern Alternative)\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <title>API Documentation</title>\n  <link href=\"https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700\" rel=\"stylesheet\">\n</head>\n<body>\n  <redoc spec-url='./openapi.yaml'\n    hide-download-button\n    required-props-first\n    native-scrollbars\n    theme='{\n      \"colors\": {\n        \"primary\": {\n          \"main\": \"#4285F4\"\n        }\n      },\n      \"typography\": {\n        \"fontSize\": \"16px\",\n        \"fontFamily\": \"Roboto, sans-serif\"\n      }\n    }'>\n  </redoc>\n  <script src=\"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js\"></script>\n</body>\n</html>\n```\n\n### Stoplight Elements\n\n```javascript\nimport { API } from '@stoplight/elements';\nimport '@stoplight/elements/styles.min.css';\n\nfunction App() {\n  return (\n    <API\n      apiDescriptionUrl=\"./openapi.yaml\"\n      router=\"hash\"\n      layout=\"sidebar\"\n      tryItCredentialsPolicy=\"include\"\n    />\n  );\n}\n```\n\n## Multi-Protocol Documentation\n\n### GraphQL Schema Documentation\n\n```graphql\n\"\"\"\nUser account in the system\n\"\"\"\ntype User {\n  \"\"\"\n  Unique user identifier\n  \"\"\"\n  id: ID!\n\n  \"\"\"\n  User's email address (unique)\n  @example \"user@example.com\"\n  \"\"\"\n  email: String!\n\n  \"\"\"\n  Display name\n  @example \"John Doe\"\n  \"\"\"\n  name: String!\n\n  \"\"\"\n  User's posts (paginated)\n  \"\"\"\n  posts(\n    \"\"\"Number of items per page (max 100)\"\"\"\n    limit: Int = 20\n    \"\"\"Page offset\"\"\"\n    offset: Int = 0\n  ): PostConnection!\n}\n\ntype Query {\n  \"\"\"\n  Fetch a user by ID\n  \"\"\"\n  user(\n    \"\"\"User's unique identifier\"\"\"\n    id: ID!\n  ): User\n\n  \"\"\"\n  Search users by name or email\n  \"\"\"\n  searchUsers(\n    \"\"\"Search query\"\"\"\n    query: String!\n    \"\"\"Maximum results to return\"\"\"\n    limit: Int = 10\n  ): [User!]!\n}\n\ntype Mutation {\n  \"\"\"\n  Create a new user account\n  \"\"\"\n  createUser(\n    \"\"\"User creation input\"\"\"\n    input: CreateUserInput!\n  ): CreateUserPayload!\n}\n\n\"\"\"\nInput for creating a user\n\"\"\"\ninput CreateUserInput {\n  \"\"\"User's email address\"\"\"\n  email: String!\n  \"\"\"Display name\"\"\"\n  name: String!\n}\n```\n\n**GraphQL Playground:**\n```javascript\nconst { ApolloServer } = require('apollo-server');\n\nconst server = new ApolloServer({\n  typeDefs,\n  resolvers,\n  introspection: true,  // Enable in dev\n  playground: {\n    settings: {\n      'editor.theme': 'dark',\n      'editor.fontSize': 14,\n    },\n  },\n});\n```\n\n### WebSocket Protocol Documentation\n\n```yaml\n# AsyncAPI 2.0\nasyncapi: 2.5.0\ninfo:\n  title: Chat WebSocket API\n  version: 1.0.0\n  description: Real-time chat messaging\n\nchannels:\n  chat/{roomId}:\n    parameters:\n      roomId:\n        description: Chat room identifier\n        schema:\n          type: string\n\n    subscribe:\n      summary: Receive messages\n      message:\n        oneOf:\n          - $ref: '#/components/messages/ChatMessage'\n          - $ref: '#/components/messages/UserJoined'\n\n    publish:\n      summary: Send a message\n      message:\n        $ref: '#/components/messages/ChatMessage'\n\ncomponents:\n  messages:\n    ChatMessage:\n      name: message\n      payload:\n        type: object\n        properties:\n          userId:\n            type: string\n          content:\n            type: string\n          timestamp:\n            type: string\n            format: date-time\n\n    UserJoined:\n      name: userJoined\n      payload:\n        type: object\n        properties:\n          userId:\n            type: string\n          username:\n            type: string\n```\n\n### gRPC Documentation\n\n```protobuf\nsyntax = \"proto3\";\n\npackage users.v1;\n\n// User service manages user accounts\nservice UserService {\n  // Get a user by ID\n  // Returns: User object or NOT_FOUND error\n  rpc GetUser(GetUserRequest) returns (User) {}\n\n  // List all users with pagination\n  // Returns: Paginated list of users\n  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {}\n\n  // Create a new user\n  // Returns: Created user or ALREADY_EXISTS error\n  rpc CreateUser(CreateUserRequest) returns (User) {}\n\n  // Stream user updates in real-time\n  // Returns: Stream of user update events\n  rpc WatchUsers(WatchUsersRequest) returns (stream UserEvent) {}\n}\n\n// User account\nmessage User {\n  // Unique identifier\n  string id = 1;\n\n  // Email address (unique, required)\n  string email = 2;\n\n  // Display name\n  string name = 3;\n\n  // Account creation timestamp\n  google.protobuf.Timestamp created_at = 4;\n}\n```\n\n## SDK Documentation Strategies\n\n### Multi-Language Examples\n\n```markdown\n# Create User\n\n## Python\n```python\nfrom myapi import Client\n\nclient = Client(api_key=\"<api-key>\")\nuser = client.users.create(\n    name=\"John Doe\",\n    email=\"john@example.com\"\n)\nprint(user.id)\n```\n\n## TypeScript\n```typescript\nimport { Client } from '@myapi/sdk';\n\nconst client = new Client({ apiKey: '<api-key>' });\nconst user = await client.users.create({\n  name: 'John Doe',\n  email: 'john@example.com',\n});\nconsole.log(user.id);\n```\n\n## Go\n```go\nimport \"github.com/myapi/sdk-go\"\n\nclient := sdk.NewClient(\"<api-key>\")\nuser, err := client.Users.Create(ctx, &sdk.CreateUserInput{\n    Name:  \"John Doe\",\n    Email: \"john@example.com\",\n})\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Println(user.ID)\n```\n\n## Ruby\n```ruby\nrequire 'myapi'\n\nclient = MyAPI::Client.new(api_key: '<api-key>')\nuser = client.users.create(\n  name: 'John Doe',\n  email: 'john@example.com'\n)\nputs user.id\n```\n```\n\n### SDK Reference Template\n\n```markdown\n# Users SDK\n\n## Installation\n```bash\nnpm install @myapi/sdk\n```\n\n## Configuration\n```typescript\nimport { Client } from '@myapi/sdk';\n\nconst client = new Client({\n  apiKey: process.env.API_KEY,\n  baseUrl: 'https://api.example.com',  // Optional\n  timeout: 30000,  // Optional, default 30s\n});\n```\n\n## Methods\n\n### `client.users.create(data)`\nCreate a new user.\n\n**Parameters:**\n- `data.name` (string, required) - User's display name\n- `data.email` (string, required) - User's email address\n\n**Returns:** Promise<User>\n\n**Throws:**\n- `ValidationError` - Invalid input data\n- `ConflictError` - Email already exists\n- `AuthenticationError` - Invalid API key\n\n**Example:**\n```typescript\nconst user = await client.users.create({\n  name: 'John Doe',\n  email: 'john@example.com',\n});\n```\n\n## Error Handling\n```typescript\nimport { ValidationError, ConflictError } from '@myapi/sdk';\n\ntry {\n  await client.users.create(data);\n} catch (error) {\n  if (error instanceof ValidationError) {\n    console.error('Invalid data:', error.fields);\n  } else if (error instanceof ConflictError) {\n    console.error('User already exists');\n  }\n}\n```\n```\n\n## Quick Reference\n\n| Tool | Protocol | Features |\n|------|----------|----------|\n| Swagger UI | REST | Try-it-out, auth |\n| Redoc | REST | Clean, responsive |\n| Stoplight | REST | Modern, mock server |\n| GraphQL Playground | GraphQL | Explorer, history |\n| AsyncAPI Studio | WebSocket | Visual editor |\n| grpcui | gRPC | Interactive console |\n","skills/code-documenter/references/python-docstrings.md":"# Python Docstrings\n\n## Google Style (Recommended)\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"Calculate total cost including tax.\n\n    Args:\n        items: List of items to calculate total for.\n        tax_rate: Tax rate as decimal (e.g., 0.08 for 8%).\n\n    Returns:\n        Total cost including tax.\n\n    Raises:\n        ValueError: If tax_rate is negative or items is empty.\n\n    Example:\n        >>> calculate_total([Item(10), Item(20)], 0.1)\n        33.0\n    \"\"\"\n```\n\n## NumPy Style\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"\n    Calculate total cost including tax.\n\n    Parameters\n    ----------\n    items : list[Item]\n        List of items to calculate total for.\n    tax_rate : float, optional\n        Tax rate as decimal (e.g., 0.08 for 8%). Default is 0.0.\n\n    Returns\n    -------\n    float\n        Total cost including tax.\n\n    Raises\n    ------\n    ValueError\n        If tax_rate is negative or items is empty.\n\n    Examples\n    --------\n    >>> calculate_total([Item(10), Item(20)], 0.1)\n    33.0\n    \"\"\"\n```\n\n## Sphinx Style\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"Calculate total cost including tax.\n\n    :param items: List of items to calculate total for.\n    :type items: list[Item]\n    :param tax_rate: Tax rate as decimal (e.g., 0.08 for 8%).\n    :type tax_rate: float\n    :returns: Total cost including tax.\n    :rtype: float\n    :raises ValueError: If tax_rate is negative or items is empty.\n\n    .. code-block:: python\n\n        >>> calculate_total([Item(10), Item(20)], 0.1)\n        33.0\n    \"\"\"\n```\n\n## Class Documentation\n\n```python\nclass UserService:\n    \"\"\"Service for managing user operations.\n\n    This service handles CRUD operations for users and\n    integrates with the authentication system.\n\n    Attributes:\n        db: Database session for queries.\n        cache: Redis client for caching.\n\n    Example:\n        >>> service = UserService(db, cache)\n        >>> user = await service.create_user(data)\n    \"\"\"\n\n    def __init__(self, db: AsyncSession, cache: Redis) -> None:\n        \"\"\"Initialize UserService.\n\n        Args:\n            db: Database session for queries.\n            cache: Redis client for caching.\n        \"\"\"\n```\n\n## Quick Reference\n\n| Style | Args Format | Returns Format |\n|-------|-------------|----------------|\n| Google | `Args:` block | `Returns:` block |\n| NumPy | `Parameters` section | `Returns` section |\n| Sphinx | `:param name:` | `:returns:` |\n\n## Sections Available\n\n| Section | Google | NumPy | Sphinx |\n|---------|--------|-------|--------|\n| Parameters | `Args:` | `Parameters` | `:param:` |\n| Returns | `Returns:` | `Returns` | `:returns:` |\n| Raises | `Raises:` | `Raises` | `:raises:` |\n| Examples | `Example:` | `Examples` | `.. code-block::` |\n| Notes | `Note:` | `Notes` | `.. note::` |\n| Attributes | `Attributes:` | `Attributes` | `:ivar:` |\n","skills/code-documenter/references/typescript-jsdoc.md":"# TypeScript JSDoc\n\n## Function Documentation\n\n```typescript\n/**\n * Calculate total cost including tax.\n *\n * @param items - List of items to calculate total for\n * @param taxRate - Tax rate as decimal (e.g., 0.08 for 8%)\n * @returns Total cost including tax\n * @throws {Error} If taxRate is negative or items is empty\n *\n * @example\n * ```typescript\n * const total = calculateTotal(items, 0.08);\n * console.log(total); // 108.00\n * ```\n */\nfunction calculateTotal(items: Item[], taxRate = 0): number {\n```\n\n## Class Documentation\n\n```typescript\n/**\n * Service for managing user operations.\n *\n * Handles CRUD operations and integrates with authentication system.\n *\n * @example\n * ```typescript\n * const service = new UserService(db, cache);\n * const user = await service.create(userData);\n * ```\n */\nclass UserService {\n  /**\n   * Create a new UserService instance.\n   *\n   * @param db - Database connection\n   * @param cache - Redis cache client\n   */\n  constructor(\n    private readonly db: Database,\n    private readonly cache: Cache,\n  ) {}\n}\n```\n\n## Interface Documentation\n\n```typescript\n/**\n * User data transfer object.\n *\n * @interface UserDto\n */\ninterface UserDto {\n  /** Unique user identifier */\n  id: string;\n\n  /** User's email address (unique) */\n  email: string;\n\n  /** User's display name */\n  name: string;\n\n  /** Account creation timestamp */\n  createdAt: Date;\n}\n```\n\n## Generic Types\n\n```typescript\n/**\n * Paginated response wrapper.\n *\n * @template T - Type of items in the data array\n */\ninterface PaginatedResponse<T> {\n  /** Array of items for current page */\n  data: T[];\n\n  /** Total number of items across all pages */\n  total: number;\n\n  /** Current page number (1-indexed) */\n  page: number;\n\n  /** Number of items per page */\n  limit: number;\n}\n```\n\n## Async Functions\n\n```typescript\n/**\n * Fetch user by ID from database.\n *\n * @param id - User's unique identifier\n * @returns Promise resolving to user data or null if not found\n * @throws {DatabaseError} If connection fails\n *\n * @async\n */\nasync function findUserById(id: string): Promise<User | null> {\n```\n\n## Quick Reference\n\n| Tag | Purpose | Example |\n|-----|---------|---------|\n| `@param` | Parameter description | `@param name - User's name` |\n| `@returns` | Return value | `@returns User object` |\n| `@throws` | Exception thrown | `@throws {Error} If invalid` |\n| `@example` | Usage example | Code block |\n| `@see` | Reference link | `@see UserService` |\n| `@deprecated` | Mark deprecated | `@deprecated Use v2 instead` |\n| `@template` | Generic type param | `@template T - Item type` |\n| `@async` | Async function | Mark async |\n| `@private` | Private member | Internal use |\n| `@readonly` | Read-only property | Cannot modify |\n\n## Common Patterns\n\n```typescript\n// Optional parameters\n/** @param [options] - Optional configuration */\n\n// Default values\n/** @param [limit=10] - Items per page (default: 10) */\n\n// Multiple types\n/** @param input - Input value (string or number) */\n\n// Callback parameters\n/**\n * @callback FilterFn\n * @param item - Item to filter\n * @returns Whether item passes filter\n */\n```\n","skills/code-documenter/references/user-guides-tutorials.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-documenter%2Freferences%2Fuser-guides-tutorials.md -->","skills/code-reviewer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2FSKILL.md -->","skills/code-reviewer/references/common-issues.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Fcommon-issues.md -->","skills/code-reviewer/references/feedback-examples.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Ffeedback-examples.md -->","skills/code-reviewer/references/receiving-feedback.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freceiving-feedback.md -->","skills/code-reviewer/references/report-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freport-template.md -->","skills/code-reviewer/references/review-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freview-checklist.md -->","skills/code-reviewer/references/spec-compliance-review.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Fspec-compliance-review.md -->","skills/cpp-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2FSKILL.md -->","skills/cpp-pro/references/build-tooling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fbuild-tooling.md -->","skills/cpp-pro/references/concurrency.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fconcurrency.md -->","skills/cpp-pro/references/memory-performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fmemory-performance.md -->","skills/cpp-pro/references/modern-cpp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fmodern-cpp.md -->","skills/cpp-pro/references/templates.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Ftemplates.md -->","skills/csharp-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2FSKILL.md -->","skills/csharp-developer/references/aspnet-core.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Faspnet-core.md -->","skills/csharp-developer/references/blazor.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fblazor.md -->","skills/csharp-developer/references/entity-framework.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fentity-framework.md -->","skills/csharp-developer/references/modern-csharp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fmodern-csharp.md -->","skills/csharp-developer/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fperformance.md -->","skills/database-optimizer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2FSKILL.md -->","skills/database-optimizer/references/index-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Findex-strategies.md -->","skills/database-optimizer/references/monitoring-analysis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fmonitoring-analysis.md -->","skills/database-optimizer/references/mysql-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fmysql-tuning.md -->","skills/database-optimizer/references/postgresql-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fpostgresql-tuning.md -->","skills/database-optimizer/references/query-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fquery-optimization.md -->","skills/debugging-wizard/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2FSKILL.md -->","skills/debugging-wizard/references/common-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fcommon-patterns.md -->","skills/debugging-wizard/references/debugging-tools.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fdebugging-tools.md -->","skills/debugging-wizard/references/quick-fixes.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fquick-fixes.md -->","skills/debugging-wizard/references/strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fstrategies.md -->","skills/debugging-wizard/references/systematic-debugging.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fsystematic-debugging.md -->","skills/devops-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2FSKILL.md -->","skills/devops-engineer/references/deployment-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fdeployment-strategies.md -->","skills/devops-engineer/references/docker-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fdocker-patterns.md -->","skills/devops-engineer/references/github-actions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fgithub-actions.md -->","skills/devops-engineer/references/gitlab-ci.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fgitlab-ci.md -->","skills/devops-engineer/references/incident-response.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fincident-response.md -->","skills/devops-engineer/references/kubernetes.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fkubernetes.md -->","skills/devops-engineer/references/platform-engineering.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fplatform-engineering.md -->","skills/devops-engineer/references/release-automation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Frelease-automation.md -->","skills/devops-engineer/references/terraform-iac.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fterraform-iac.md -->","skills/django-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2FSKILL.md -->","skills/django-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fauthentication.md -->","skills/django-expert/references/drf-serializers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fdrf-serializers.md -->","skills/django-expert/references/models-orm.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fmodels-orm.md -->","skills/django-expert/references/testing-django.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Ftesting-django.md -->","skills/django-expert/references/viewsets-views.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fviewsets-views.md -->","skills/django-storages-s3/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2FSKILL.md -->","skills/django-storages-s3/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fconfiguration.md -->","skills/django-storages-s3/references/custom-backends.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fcustom-backends.md -->","skills/django-storages-s3/references/presigned-urls.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fpresigned-urls.md -->","skills/django-storages-s3/references/testing-storages.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Ftesting-storages.md -->","skills/dotnet-core-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2FSKILL.md -->","skills/dotnet-core-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fauthentication.md -->","skills/dotnet-core-expert/references/clean-architecture.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fclean-architecture.md -->","skills/dotnet-core-expert/references/cloud-native.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fcloud-native.md -->","skills/dotnet-core-expert/references/entity-framework.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fentity-framework.md -->","skills/dotnet-core-expert/references/minimal-apis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fminimal-apis.md -->","skills/embedded-systems/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2FSKILL.md -->","skills/embedded-systems/references/communication-protocols.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fcommunication-protocols.md -->","skills/embedded-systems/references/memory-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fmemory-optimization.md -->","skills/embedded-systems/references/microcontroller-programming.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fmicrocontroller-programming.md -->","skills/embedded-systems/references/power-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fpower-optimization.md -->","skills/embedded-systems/references/rtos-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Frtos-patterns.md -->","skills/fastapi-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2FSKILL.md -->","skills/fastapi-expert/references/async-sqlalchemy.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fasync-sqlalchemy.md -->","skills/fastapi-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fauthentication.md -->","skills/fastapi-expert/references/endpoints-routing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fendpoints-routing.md -->","skills/fastapi-expert/references/migration-from-django.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fmigration-from-django.md -->","skills/fastapi-expert/references/pydantic-v2.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fpydantic-v2.md -->","skills/fastapi-expert/references/testing-async.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Ftesting-async.md -->","skills/feature-forge/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2FSKILL.md -->","skills/feature-forge/references/acceptance-criteria.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Facceptance-criteria.md -->","skills/feature-forge/references/ears-syntax.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fears-syntax.md -->","skills/feature-forge/references/interview-questions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Finterview-questions.md -->","skills/feature-forge/references/pre-discovery-subagents.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fpre-discovery-subagents.md -->","skills/feature-forge/references/specification-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fspecification-template.md -->","skills/fine-tuning-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2FSKILL.md -->","skills/fine-tuning-expert/references/dataset-preparation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fdataset-preparation.md -->","skills/fine-tuning-expert/references/deployment-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fdeployment-optimization.md -->","skills/fine-tuning-expert/references/evaluation-metrics.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fevaluation-metrics.md -->","skills/fine-tuning-expert/references/hyperparameter-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fhyperparameter-tuning.md -->","skills/fine-tuning-expert/references/lora-peft.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Flora-peft.md -->","skills/flutter-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2FSKILL.md -->","skills/flutter-expert/references/bloc-state.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fbloc-state.md -->","skills/flutter-expert/references/gorouter-navigation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fgorouter-navigation.md -->","skills/flutter-expert/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fperformance.md -->","skills/flutter-expert/references/project-structure.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fproject-structure.md -->","skills/flutter-expert/references/riverpod-state.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Friverpod-state.md -->","skills/flutter-expert/references/widget-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fwidget-patterns.md -->","skills/fullstack-guardian/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2FSKILL.md -->","skills/fullstack-guardian/references/api-design-standards.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fapi-design-standards.md -->","skills/fullstack-guardian/references/architecture-decisions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Farchitecture-decisions.md -->","skills/fullstack-guardian/references/backend-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fbackend-patterns.md -->","skills/fullstack-guardian/references/common-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fcommon-patterns.md -->","skills/fullstack-guardian/references/deliverables-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fdeliverables-checklist.md -->","skills/fullstack-guardian/references/design-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fdesign-template.md -->","skills/fullstack-guardian/references/error-handling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Ferror-handling.md -->","skills/fullstack-guardian/references/frontend-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Ffrontend-patterns.md -->","skills/fullstack-guardian/references/integration-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fintegration-patterns.md -->","skills/fullstack-guardian/references/security-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fsecurity-checklist.md -->","skills/game-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2FSKILL.md -->","skills/game-developer/references/ecs-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fecs-patterns.md -->","skills/game-developer/references/multiplayer-networking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fmultiplayer-networking.md -->","skills/game-developer/references/performance-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fperformance-optimization.md -->","skills/game-developer/references/unity-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Funity-patterns.md -->","skills/game-developer/references/unreal-cpp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Funreal-cpp.md -->","skills/golang-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2FSKILL.md -->","skills/golang-pro/references/concurrency.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fconcurrency.md -->","skills/golang-pro/references/generics.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fgenerics.md -->","skills/golang-pro/references/interfaces.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Finterfaces.md -->","skills/golang-pro/references/project-structure.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fproject-structure.md -->","skills/golang-pro/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Ftesting.md -->","skills/graphql-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2FSKILL.md -->","skills/graphql-architect/references/federation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Ffederation.md -->","skills/graphql-architect/references/migration-from-rest.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fmigration-from-rest.md -->","skills/graphql-architect/references/resolvers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fresolvers.md -->","skills/graphql-architect/references/schema-design.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fschema-design.md -->","skills/graphql-architect/references/security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fsecurity.md -->","skills/graphql-architect/references/subscriptions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fsubscriptions.md -->","skills/java-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2FSKILL.md -->","skills/java-architect/references/jpa-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fjpa-optimization.md -->","skills/java-architect/references/reactive-webflux.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Freactive-webflux.md -->","skills/java-architect/references/spring-boot-setup.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fspring-boot-setup.md -->","skills/java-architect/references/spring-security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fspring-security.md -->","skills/java-architect/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Ftesting-patterns.md -->","skills/javascript-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2FSKILL.md -->","skills/javascript-pro/references/async-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fasync-patterns.md -->","skills/javascript-pro/references/browser-apis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fbrowser-apis.md -->","skills/javascript-pro/references/modern-syntax.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fmodern-syntax.md -->","skills/javascript-pro/references/modules.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fmodules.md -->","skills/javascript-pro/references/node-essentials.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fnode-essentials.md -->","skills/kotlin-specialist/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2FSKILL.md -->","skills/kotlin-specialist/references/android-compose.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fandroid-compose.md -->","skills/kotlin-specialist/references/coroutines-flow.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fcoroutines-flow.md -->","skills/kotlin-specialist/references/dsl-idioms.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fdsl-idioms.md -->","skills/kotlin-specialist/references/ktor-server.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fktor-server.md -->","skills/kotlin-specialist/references/multiplatform-kmp.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fmultiplatform-kmp.md -->","skills/kubernetes-specialist/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2FSKILL.md -->","skills/kubernetes-specialist/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fconfiguration.md -->","skills/kubernetes-specialist/references/cost-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fcost-optimization.md -->","skills/kubernetes-specialist/references/custom-operators.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fcustom-operators.md -->","skills/kubernetes-specialist/references/gitops.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fgitops.md -->","skills/kubernetes-specialist/references/helm-charts.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fhelm-charts.md -->","skills/kubernetes-specialist/references/multi-cluster.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fmulti-cluster.md -->","skills/kubernetes-specialist/references/networking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fnetworking.md -->","skills/kubernetes-specialist/references/service-mesh.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fservice-mesh.md -->","skills/kubernetes-specialist/references/storage.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fstorage.md -->","skills/kubernetes-specialist/references/troubleshooting.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Ftroubleshooting.md -->","skills/kubernetes-specialist/references/workloads.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fworkloads.md -->","skills/laravel-specialist/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2FSKILL.md -->","skills/laravel-specialist/references/eloquent.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Feloquent.md -->","skills/laravel-specialist/references/livewire.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Flivewire.md -->","skills/laravel-specialist/references/queues.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Fqueues.md -->","skills/laravel-specialist/references/routing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Frouting.md -->","skills/laravel-specialist/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Ftesting.md -->","skills/legacy-modernizer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2FSKILL.md -->","skills/legacy-modernizer/references/legacy-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Flegacy-testing.md -->","skills/legacy-modernizer/references/migration-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fmigration-strategies.md -->","skills/legacy-modernizer/references/refactoring-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Frefactoring-patterns.md -->","skills/legacy-modernizer/references/strangler-fig-pattern.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fstrangler-fig-pattern.md -->","skills/legacy-modernizer/references/system-assessment.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fsystem-assessment.md -->","skills/mcp-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2FSKILL.md -->","skills/mcp-developer/references/protocol.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fprotocol.md -->","skills/mcp-developer/references/python-sdk.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fpython-sdk.md -->","skills/mcp-developer/references/resources.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fresources.md -->","skills/mcp-developer/references/tools.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Ftools.md -->","skills/mcp-developer/references/typescript-sdk.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Ftypescript-sdk.md -->","skills/microservices-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2FSKILL.md -->","skills/microservices-architect/references/communication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fcommunication.md -->","skills/microservices-architect/references/data.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fdata.md -->","skills/microservices-architect/references/decomposition.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fdecomposition.md -->","skills/microservices-architect/references/observability.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fobservability.md -->","skills/microservices-architect/references/patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fpatterns.md -->","skills/ml-pipeline/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2FSKILL.md -->","skills/ml-pipeline/references/experiment-tracking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fexperiment-tracking.md -->","skills/ml-pipeline/references/feature-engineering.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Ffeature-engineering.md -->","skills/ml-pipeline/references/model-validation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fmodel-validation.md -->","skills/ml-pipeline/references/pipeline-orchestration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fpipeline-orchestration.md -->","skills/ml-pipeline/references/training-pipelines.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Ftraining-pipelines.md -->","skills/monitoring-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2FSKILL.md -->","skills/monitoring-expert/references/alerting-rules.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Falerting-rules.md -->","skills/monitoring-expert/references/application-profiling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fapplication-profiling.md -->","skills/monitoring-expert/references/capacity-planning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fcapacity-planning.md -->","skills/monitoring-expert/references/dashboards.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fdashboards.md -->","skills/monitoring-expert/references/opentelemetry.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fopentelemetry.md -->","skills/monitoring-expert/references/performance-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fperformance-testing.md -->","skills/monitoring-expert/references/prometheus-metrics.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fprometheus-metrics.md -->","skills/monitoring-expert/references/structured-logging.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fstructured-logging.md -->","skills/nestjs-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2FSKILL.md -->","skills/nestjs-expert/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fauthentication.md -->","skills/nestjs-expert/references/controllers-routing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fcontrollers-routing.md -->","skills/nestjs-expert/references/dtos-validation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fdtos-validation.md -->","skills/nestjs-expert/references/migration-from-express.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fmigration-from-express.md -->","skills/nestjs-expert/references/services-di.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fservices-di.md -->","skills/nestjs-expert/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Ftesting-patterns.md -->","skills/nextjs-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2FSKILL.md -->","skills/nextjs-developer/references/app-router.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fapp-router.md -->","skills/nextjs-developer/references/data-fetching.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fdata-fetching.md -->","skills/nextjs-developer/references/deployment.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fdeployment.md -->","skills/nextjs-developer/references/server-actions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fserver-actions.md -->","skills/nextjs-developer/references/server-components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fserver-components.md -->","skills/pandas-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2FSKILL.md -->","skills/pandas-pro/references/aggregation-groupby.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Faggregation-groupby.md -->","skills/pandas-pro/references/data-cleaning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fdata-cleaning.md -->","skills/pandas-pro/references/dataframe-operations.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fdataframe-operations.md -->","skills/pandas-pro/references/merging-joining.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fmerging-joining.md -->","skills/pandas-pro/references/performance-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fperformance-optimization.md -->","skills/php-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2FSKILL.md -->","skills/php-pro/references/async-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fasync-patterns.md -->","skills/php-pro/references/laravel-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Flaravel-patterns.md -->","skills/php-pro/references/modern-php-features.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fmodern-php-features.md -->","skills/php-pro/references/symfony-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fsymfony-patterns.md -->","skills/php-pro/references/testing-quality.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Ftesting-quality.md -->","skills/playwright-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2FSKILL.md -->","skills/playwright-expert/references/api-mocking.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fapi-mocking.md -->","skills/playwright-expert/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fconfiguration.md -->","skills/playwright-expert/references/debugging-flaky.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fdebugging-flaky.md -->","skills/playwright-expert/references/page-object-model.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fpage-object-model.md -->","skills/playwright-expert/references/selectors-locators.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fselectors-locators.md -->","skills/postgres-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2FSKILL.md -->","skills/postgres-pro/references/extensions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fextensions.md -->","skills/postgres-pro/references/jsonb.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fjsonb.md -->","skills/postgres-pro/references/maintenance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fmaintenance.md -->","skills/postgres-pro/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fperformance.md -->","skills/postgres-pro/references/replication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Freplication.md -->","skills/prompt-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2FSKILL.md -->","skills/prompt-engineer/references/context-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fcontext-management.md -->","skills/prompt-engineer/references/evaluation-frameworks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fevaluation-frameworks.md -->","skills/prompt-engineer/references/prompt-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fprompt-optimization.md -->","skills/prompt-engineer/references/prompt-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fprompt-patterns.md -->","skills/prompt-engineer/references/structured-outputs.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fstructured-outputs.md -->","skills/prompt-engineer/references/system-prompts.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fsystem-prompts.md -->","skills/python-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2FSKILL.md -->","skills/python-pro/references/async-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fasync-patterns.md -->","skills/python-pro/references/packaging.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fpackaging.md -->","skills/python-pro/references/standard-library.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fstandard-library.md -->","skills/python-pro/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Ftesting.md -->","skills/python-pro/references/type-system.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Ftype-system.md -->","skills/rag-architect/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2FSKILL.md -->","skills/rag-architect/references/chunking-strategies.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fchunking-strategies.md -->","skills/rag-architect/references/embedding-models.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fembedding-models.md -->","skills/rag-architect/references/rag-evaluation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Frag-evaluation.md -->","skills/rag-architect/references/retrieval-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fretrieval-optimization.md -->","skills/rag-architect/references/vector-databases.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fvector-databases.md -->","skills/rails-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2FSKILL.md -->","skills/rails-expert/references/active-record.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Factive-record.md -->","skills/rails-expert/references/api-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fapi-development.md -->","skills/rails-expert/references/background-jobs.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fbackground-jobs.md -->","skills/rails-expert/references/hotwire-turbo.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fhotwire-turbo.md -->","skills/rails-expert/references/rspec-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Frspec-testing.md -->","skills/react-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2FSKILL.md -->","skills/react-expert/references/hooks-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fhooks-patterns.md -->","skills/react-expert/references/migration-class-to-modern.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fmigration-class-to-modern.md -->","skills/react-expert/references/performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fperformance.md -->","skills/react-expert/references/react-19-features.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Freact-19-features.md -->","skills/react-expert/references/server-components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fserver-components.md -->","skills/react-expert/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fstate-management.md -->","skills/react-expert/references/testing-react.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Ftesting-react.md -->","skills/react-native-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2FSKILL.md -->","skills/react-native-expert/references/expo-router.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fexpo-router.md -->","skills/react-native-expert/references/list-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Flist-optimization.md -->","skills/react-native-expert/references/platform-handling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fplatform-handling.md -->","skills/react-native-expert/references/project-structure.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fproject-structure.md -->","skills/react-native-expert/references/storage-hooks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fstorage-hooks.md -->","skills/rust-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2FSKILL.md -->","skills/rust-engineer/references/async.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Fasync.md -->","skills/rust-engineer/references/error-handling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ferror-handling.md -->","skills/rust-engineer/references/ownership.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Fownership.md -->","skills/rust-engineer/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ftesting.md -->","skills/rust-engineer/references/traits.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ftraits.md -->","skills/salesforce-developer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2FSKILL.md -->","skills/salesforce-developer/references/apex-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fapex-development.md -->","skills/salesforce-developer/references/deployment-devops.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fdeployment-devops.md -->","skills/salesforce-developer/references/integration-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fintegration-patterns.md -->","skills/salesforce-developer/references/lightning-web-components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Flightning-web-components.md -->","skills/salesforce-developer/references/soql-sosl.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fsoql-sosl.md -->","skills/secure-code-guardian/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2FSKILL.md -->","skills/secure-code-guardian/references/authentication.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fauthentication.md -->","skills/secure-code-guardian/references/input-validation.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Finput-validation.md -->","skills/secure-code-guardian/references/owasp-prevention.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fowasp-prevention.md -->","skills/secure-code-guardian/references/security-headers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fsecurity-headers.md -->","skills/secure-code-guardian/references/xss-csrf.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fxss-csrf.md -->","skills/security-reviewer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2FSKILL.md -->","skills/security-reviewer/references/infrastructure-security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Finfrastructure-security.md -->","skills/security-reviewer/references/penetration-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fpenetration-testing.md -->","skills/security-reviewer/references/report-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Freport-template.md -->","skills/security-reviewer/references/sast-tools.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fsast-tools.md -->","skills/security-reviewer/references/secret-scanning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fsecret-scanning.md -->","skills/security-reviewer/references/vulnerability-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fvulnerability-patterns.md -->","skills/shopify-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2FSKILL.md -->","skills/shopify-expert/references/app-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fapp-development.md -->","skills/shopify-expert/references/checkout-customization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fcheckout-customization.md -->","skills/shopify-expert/references/liquid-templating.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fliquid-templating.md -->","skills/shopify-expert/references/performance-optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fperformance-optimization.md -->","skills/shopify-expert/references/storefront-api.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fstorefront-api.md -->","skills/spark-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2FSKILL.md -->","skills/spark-engineer/references/partitioning-caching.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fpartitioning-caching.md -->","skills/spark-engineer/references/performance-tuning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fperformance-tuning.md -->","skills/spark-engineer/references/rdd-operations.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Frdd-operations.md -->","skills/spark-engineer/references/spark-sql-dataframes.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fspark-sql-dataframes.md -->","skills/spark-engineer/references/streaming-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fstreaming-patterns.md -->","skills/spec-miner/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2FSKILL.md -->","skills/spec-miner/references/analysis-checklist.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fanalysis-checklist.md -->","skills/spec-miner/references/analysis-process.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fanalysis-process.md -->","skills/spec-miner/references/ears-format.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fears-format.md -->","skills/spec-miner/references/specification-template.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fspecification-template.md -->","skills/spring-boot-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2FSKILL.md -->","skills/spring-boot-engineer/references/cloud.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fcloud.md -->","skills/spring-boot-engineer/references/data.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fdata.md -->","skills/spring-boot-engineer/references/security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fsecurity.md -->","skills/spring-boot-engineer/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Ftesting.md -->","skills/spring-boot-engineer/references/web.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fweb.md -->","skills/sql-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2FSKILL.md -->","skills/sql-pro/references/database-design.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fdatabase-design.md -->","skills/sql-pro/references/dialect-differences.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fdialect-differences.md -->","skills/sql-pro/references/optimization.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Foptimization.md -->","skills/sql-pro/references/query-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fquery-patterns.md -->","skills/sql-pro/references/window-functions.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fwindow-functions.md -->","skills/sre-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2FSKILL.md -->","skills/sre-engineer/references/automation-toil.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fautomation-toil.md -->","skills/sre-engineer/references/error-budget-policy.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Ferror-budget-policy.md -->","skills/sre-engineer/references/incident-chaos.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fincident-chaos.md -->","skills/sre-engineer/references/monitoring-alerting.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fmonitoring-alerting.md -->","skills/sre-engineer/references/slo-sli-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fslo-sli-management.md -->","skills/swift-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2FSKILL.md -->","skills/swift-expert/references/async-concurrency.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fasync-concurrency.md -->","skills/swift-expert/references/memory-performance.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fmemory-performance.md -->","skills/swift-expert/references/protocol-oriented.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fprotocol-oriented.md -->","skills/swift-expert/references/swiftui-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fswiftui-patterns.md -->","skills/swift-expert/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Ftesting-patterns.md -->","skills/terraform-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2FSKILL.md -->","skills/terraform-engineer/references/best-practices.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fbest-practices.md -->","skills/terraform-engineer/references/module-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fmodule-patterns.md -->","skills/terraform-engineer/references/providers.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fproviders.md -->","skills/terraform-engineer/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fstate-management.md -->","skills/terraform-engineer/references/testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Ftesting.md -->","skills/test-master/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2FSKILL.md -->","skills/test-master/references/automation-frameworks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fautomation-frameworks.md -->","skills/test-master/references/e2e-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fe2e-testing.md -->","skills/test-master/references/integration-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fintegration-testing.md -->","skills/test-master/references/performance-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fperformance-testing.md -->","skills/test-master/references/qa-methodology.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fqa-methodology.md -->","skills/test-master/references/security-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fsecurity-testing.md -->","skills/test-master/references/tdd-iron-laws.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftdd-iron-laws.md -->","skills/test-master/references/test-reports.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftest-reports.md -->","skills/test-master/references/testing-anti-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftesting-anti-patterns.md -->","skills/test-master/references/unit-testing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Funit-testing.md -->","skills/the-fool/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2FSKILL.md -->","skills/the-fool/references/dialectic-synthesis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fdialectic-synthesis.md -->","skills/the-fool/references/evidence-audit.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fevidence-audit.md -->","skills/the-fool/references/mode-selection-guide.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fmode-selection-guide.md -->","skills/the-fool/references/pre-mortem-analysis.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fpre-mortem-analysis.md -->","skills/the-fool/references/red-team-adversarial.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fred-team-adversarial.md -->","skills/the-fool/references/socratic-questioning.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fsocratic-questioning.md -->","skills/typescript-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2FSKILL.md -->","skills/typescript-pro/references/advanced-types.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fadvanced-types.md -->","skills/typescript-pro/references/configuration.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fconfiguration.md -->","skills/typescript-pro/references/patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fpatterns.md -->","skills/typescript-pro/references/type-guards.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Ftype-guards.md -->","skills/typescript-pro/references/utility-types.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Futility-types.md -->","skills/vue-expert-js/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2FSKILL.md -->","skills/vue-expert-js/references/component-architecture.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fcomponent-architecture.md -->","skills/vue-expert-js/references/composables-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fcomposables-patterns.md -->","skills/vue-expert-js/references/jsdoc-typing.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fjsdoc-typing.md -->","skills/vue-expert-js/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fstate-management.md -->","skills/vue-expert-js/references/testing-patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Ftesting-patterns.md -->","skills/vue-expert/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2FSKILL.md -->","skills/vue-expert/references/build-tooling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fbuild-tooling.md -->","skills/vue-expert/references/components.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fcomponents.md -->","skills/vue-expert/references/composition-api.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fcomposition-api.md -->","skills/vue-expert/references/mobile-hybrid.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fmobile-hybrid.md -->","skills/vue-expert/references/nuxt.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fnuxt.md -->","skills/vue-expert/references/state-management.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fstate-management.md -->","skills/vue-expert/references/typescript.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Ftypescript.md -->","skills/websocket-engineer/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2FSKILL.md -->","skills/websocket-engineer/references/alternatives.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Falternatives.md -->","skills/websocket-engineer/references/patterns.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fpatterns.md -->","skills/websocket-engineer/references/protocol.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fprotocol.md -->","skills/websocket-engineer/references/scaling.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fscaling.md -->","skills/websocket-engineer/references/security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fsecurity.md -->","skills/wordpress-pro/SKILL.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2FSKILL.md -->","skills/wordpress-pro/references/gutenberg-blocks.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fgutenberg-blocks.md -->","skills/wordpress-pro/references/hooks-filters.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fhooks-filters.md -->","skills/wordpress-pro/references/performance-security.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fperformance-security.md -->","skills/wordpress-pro/references/plugin-architecture.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fplugin-architecture.md -->","skills/wordpress-pro/references/theme-development.md":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Ftheme-development.md -->"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Claude Skills Project Configuration\n\n> This file governs Claude's behavior when working on the claude-skills repository.\n\n---\n\n## Skill Authorship Standards\n\nSkills follow the [Agent Skills specification](https://agentskills.io/specification). This section covers project-specific conventions that go beyond the base spec.\n\n### The Description Trap\n\n**Critical:** Never put process steps or workflow sequences in descriptions. When descriptions contain step-by-step instructions, agents follow the brief description instead of reading the full skill content. This defeats the purpose of detailed skills.\n\nBrief capability statements (what it does) and trigger conditions (when to use it) are both appropriate. Process steps (how it works) are not.\n\n**BAD - Process steps in description:**\n```yaml\ndescription: Use for debugging. First investigate root cause, then analyze\npatterns, test hypotheses, and implement fixes with tests.\n```\n\n**GOOD - Capability + trigger:**\n```yaml\ndescription: Diagnoses bugs through root cause analysis and pattern matching.\nUse when encountering errors or unexpected behavior requiring investigation.\n```\n\n**Format:** `[Brief capability statement]. Use when [triggering conditions].`\n\nDescriptions tell WHAT the skill does and WHEN to use it. The SKILL.md body tells HOW.\n\n---\n\n### Frontmatter Requirements\n\nPer the [Agent Skills specification](https://agentskills.io/specification), only `name` and `description` are top-level required fields. Custom fields go under `metadata`.\n\n```yaml\n---\nname: skill-name-with-hyphens\ndescription: [Brief capability statement]. Use when [triggering conditions] - max 1024 chars\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.0.0\"\n  domain: frontend\n  triggers: keyword1, keyword2, keyword3\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: fullstack-guardian, test-master, devops-engineer\n---\n```\n\n**Top-level fields (spec-defined):**\n- `name`: Letters, numbers, and hyphens only (no parentheses or special characters)\n- `description`: Maximum 1024 characters. Capability statement + trigger conditions. No process steps.\n- `license`: Always `MIT` for this project\n- `allowed-tools`: Space-delimited tool list (only on skills that restrict tools)\n\n**Metadata fields (project-specific):**\n- `author`: GitHub profile URL of the skill author\n- `company`: Optional. URL of the author's company (`https://synergetic.solutions` on skills authored by @jeffallan); omit for contributed skills unless the contributor supplies one\n- `version`: Semantic version string (quoted, e.g., `\"1.0.0\"`)\n- `domain`: Category from the domain list below\n- `triggers`: Comma-separated searchable keywords\n- `role`: `specialist` | `expert` | `architect` | `engineer`\n- `scope`: `implementation` | `review` | `design` | `system-design` | `testing` | `analysis` | `infrastructure` | `optimization` | `architecture`\n- `output-format`: `code` | `document` | `report` | `architecture` | `specification` | `schema` | `manifests` | `analysis` | `analysis-and-code`\n- `related-skills`: Comma-separated skill directory names (e.g., `fullstack-guardian, test-master`). Must resolve to existing skill directories.\n\n**Domain values:**\n`language` · `backend` · `frontend` · `infrastructure` · `api-architecture` · `quality` · `devops` · `security` · `data-ml` · `platform` · `specialized` · `workflow`\n\n---\n\n### Reference File Standards\n\nReference files follow the [Agent Skills specification](https://agentskills.io/specification). No specific headers are required.\n\n**Guidelines:**\n- 100-600 lines per reference file\n- Keep files focused on a single topic\n- Complete, working code examples with TypeScript types\n- Cross-reference related skills where relevant\n- Include \"when to use\" and \"when not to use\" guidance\n- Practical patterns over theoretical explanations\n\n### Framework Idiom Principle\n\nReference files for framework-specific skills must reflect the idiomatic best practices of that framework, not generic patterns applied uniformly across all skills. If a framework provides a built-in mechanism (e.g., global error handling, middleware, dependency injection), reference examples should use it rather than duplicating that behavior manually. Each framework's conventions for error handling, architecture, and code organization take precedence over cross-project consistency.\n\n---\n\n### Documentation Backlink\n\nEvery `SKILL.md` MUST end with a single canonical Documentation link pointing back to the docs site:\n\n```\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- `{domain}` is `metadata.domain` from the frontmatter and must be one of the `domains` in `pyproject.toml` (`[tool.skill-linter]`); the docs site build fails on any other value.\n- `{skill-name}` is the skill's directory name.\n- Format is exactly markdown link syntax (`[text](url)`), not a bare URL. Strict CommonMark renderers do not auto-link bare URLs, and the line's purpose is to render as a real `<a href>` on aggregators (skills.sh, etc.) for SEO backlinks.\n\n**Why two surfaces, two behaviors:**\n\n- Aggregators consume raw `SKILL.md` from GitHub and render the line as a hyperlink. This is the SEO point.\n- The docs site itself would render this line as a self-link, which is redundant. The site's content sync (`site/src/components/site-sync/`, configured by `site/scripts/sync-content.mjs`) strips the line, and the credit line above it, at build time so the docs site, public markdown mirrors, `llms.txt`, and `llms-full.txt` never show it.\n\n**When adding or renaming a skill:** update both the directory name (which becomes `{skill-name}`) and the `metadata.domain` consistently with this URL formula, otherwise the backlink will 404.\n\n### Maintainer Credit\n\nSkills authored by @jeffallan (those with `metadata.company: https://synergetic.solutions`) carry one more line, directly above the Documentation backlink, separated by a blank line. It matches the docs site footer credit:\n\n```\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- The Documentation backlink stays the last line\n- Contributed skills do not get this line unless their `metadata.company` is set to the same URL\n- `syncSkillPages` strips it at build time alongside the Documentation backlink, so the docs site, markdown mirrors, and `llms` files never show it (the site footer shows the same credit once per page)\n\n---\n\n### Progressive Disclosure Architecture\n\n**Tier 1 - SKILL.md (~80-100 lines)**\n- Role definition and expertise level\n- When-to-use guidance (triggers)\n- Core workflow (5 steps)\n- Constraints (MUST DO / MUST NOT DO)\n- Routing table to references\n\n**Tier 2 - Reference Files (100-600 lines each)**\n- Deep technical content\n- Complete code examples\n- Edge cases and anti-patterns\n- Loaded only when context requires\n\n**Goal:** 50% token reduction through selective loading.\n\n---\n\n## Project Workflow\n\n### When Creating New Skills\n\n1. Check existing skills for overlap\n2. Write SKILL.md with capability + trigger description (no process steps)\n3. Create reference files for deep content (100+ lines)\n4. Add routing table linking topics to references\n5. Append the canonical Documentation backlink as the last line (see Documentation Backlink above)\n6. Test skill triggers with realistic prompts\n7. Add the skill to SKILLS_GUIDE.md in its category\n\n### When Modifying Skills\n\n1. Read the full current skill before editing\n2. Maintain capability + trigger description format (no process steps)\n3. Preserve progressive disclosure structure\n4. Update related cross-references\n5. Verify routing table accuracy\n\n### When Accepting Contributions\n\nEvery external contribution (issue or PR) gets the `gratitude` label, whatever its outcome.\n\nFor each PR:\n\n1. Discuss fit with project goals before any merge decision (scope, overlap with existing skills, promotional content), then verify the content\n2. Squash-merge with subject `type(scope): title (#N)`\n   - Conflicted fork PRs: squash locally (`git merge --squash`), add `Co-authored-by:` with the contributor's commit email, then close the PR with a comment pointing at the commit\n3. Record it in a separate commit, `docs(changelog): record #N <summary> under Unreleased`, adding an entry under `[Unreleased]` → Added/Changed/Fixed that ends with `(#N)` (append `closing #ISSUE` when it resolves one)\n4. If skill or reference counts changed, run `uvx skill-linter@0.2.0 stamp` in the same push, otherwise `stamp --check` fails CI on main\n5. Post-merge corrections go in their own commit with their own `### Changed` entry\n\nAt release time, credit contributors (including issue reporters) in a `### Contributors` section of the version entry:\n\n```markdown\n### Contributors\n- @handle: What they contributed (#N)\n```\n\n---\n\n## Shared Code from the Handbook\n\nThe site code under `site/src/components/site-*/`, the shared CI workflows and `.github/workflows/handbook-update.yml` come from the private handbook through Copier (`.copier-answers.yml`). Change them in the handbook, not here.\n\nThe versions of the shared site packages come from the handbook too (`personal-sites/package.json`): `astro`, `@astrojs/starlight`, `@jdevalk/astro-seo-graph`, `astro-og-canvas`, `starlight-links-validator`, the `@fontsource` fonts, `js-yaml`, `smol-toml`, `sharp`, `prettier` and `prettier-plugin-astro`. Don't bump them in `site/package.json`. Bump them in the handbook. **They reach this repo only with a handbook release**, as the update PR that `handbook-update.yml` opens, carrying the new `site/package.json` and lockfile.\n\nThe same goes for the actions in the workflows the handbook delivers (`handbook-update.yml`, `docs-site.yml`, `docs-deploy.yml`). Dependabot here skips those files (`exclude-paths`), and the handbook bumps them.\n\n## Release Checklist\n\nWhen releasing a new version, follow these steps.\n\n### 1. Update Version and Counts\n\nVersion and counts are managed through `version.json`:\n\n```json\n{\n  \"version\": \"0.4.2\",\n  \"skillCount\": 65,\n  \"workflowCount\": 9,\n  \"referenceFileCount\": 355\n}\n```\n\n**To release a new version:**\n\n1. Update the `version` field in `version.json`\n2. Stamp the counts and version:\n\n```bash\nuvx skill-linter@0.2.0 stamp\n```\n\nThis computes the counts from the filesystem (skills, per-domain skills, references, workflows), writes them to `version.json`, and stamps the version and counts into the files listed under `[[tool.skill-linter.stamp.files]]` in `pyproject.toml` (README.md, QUICKSTART.md, plugin.json, marketplace.json, the docs site and others).\n\n`uvx skill-linter@0.2.0 stamp --check` reports stale files without writing (CI runs it).\n\n### 2. Update CHANGELOG.md\n\nAdd new version entry at the top following Keep a Changelog format:\n\n```markdown\n## [X.Y.Z] - YYYY-MM-DD\n\n### Added\n- New features, skills, commands\n\n### Changed\n- Modified functionality, updated skills\n\n### Fixed\n- Bug fixes\n```\n\nAdd version comparison link at bottom:\n```markdown\n[X.Y.Z]: https://github.com/jeffallan/claude-skills/compare/vPREVIOUS...vX.Y.Z\n```\n\n### 3. Update Documentation for New/Modified Content\n\n**For new skills:**\n- Add to `SKILLS_GUIDE.md` in appropriate category\n- Add to decision trees if applicable\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For new commands:**\n- Add to `docs/WORKFLOW_COMMANDS.md`\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For modified skills/commands:**\n- Update any cross-references\n- Update SKILLS_GUIDE.md if triggers changed\n\n### 4. Generate Social Preview (conditional)\n\nThe social preview embeds skill, workflow, and reference counts but **not** the version. Only regenerate when at least one of `skillCount`, `workflowCount`, or `referenceFileCount` in `version.json` changed in this release — patch releases that only fix docs or a single skill normally do not need it.\n\nWhen regeneration is needed:\n\n```bash\nnpx -y -p puppeteer node ./assets/capture-screenshot.js\n```\n\nThis creates `assets/social-preview.png` from `assets/social-preview.html`.\n\n### 5. Validate\n\n**Critical:** Run validation before release to prevent broken skills from being published.\n\n```bash\nmake validate\n```\n\nThis runs [skill-linter](https://pypi.org/project/skill-linter/), configured in `pyproject.toml` (`[tool.skill-linter]`), and is the same check CI runs:\n\n- `skill-linter release` checks the shipped plugin tree (manifest, commands map, marketplace entry), then runs every other rule: frontmatter, names and descriptions, sections and references, Markdown syntax (unclosed fences, table separators, comments and column counts), commands, the changelog and stamped counts\n- `skill-linter stamp --check` fails when a stamped count or version is stale\n\n**Options:**\n```bash\nuvx skill-linter@0.2.0 check --skill react-expert   # One skill's rules only\nuvx skill-linter@0.2.0 check --only markdown        # One rule group\nuvx skill-linter@0.2.0 check --format json          # JSON output\nuvx skill-linter@0.2.0 rules                        # Every rule, its group and severity\n```\n\n**Exit codes:** 0 = no errors (warnings OK), 1 = errors found\n\n### 6. Final Verification\n\nAfter running validation, manually verify:\n\n```bash\n# Check no old version references remain (except historical changelog)\ngrep -r \"OLD_VERSION\" --include=\"*.md\" --include=\"*.json\" --include=\"*.html\"\n```\n\n### 7. Plugin Branch\n\nThe plugin ships from the `plugin` branch, not from `main`. Pushing the `vX.Y.Z` tag runs `.github/workflows/publish-plugin.yml`, which rebuilds that branch from `scripts/build-plugin-dist.sh` (only `.claude-plugin/plugin.json`, `skills/`, `commands/`, `references/`, `README.md`, `LICENSE`). Both the marketplace entry and the Claude plugin directory read from it.\n\n- Never edit the `plugin` branch by hand; the next publish overwrites it\n- After the tag push, confirm the workflow succeeded and the branch's latest commit names the new tag\n- A file the plugin needs at runtime must live under one of the shipped paths, or it will be missing from installs\n\n---\n\n## Attribution\n\nBehavioral patterns and process discipline adapted from:\n- **[obra/superpowers](https://github.com/obra/superpowers)** by Jesse Vincent (@obra)\n- License: MIT\n\nResearch documented in: `research/superpowers.md`\n","isInternal":false,"tokens":3420,"sizeBytes":14453},{"name":"components.md","path":"skills/angular-architect/references/components.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/angular-architect/references/components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Standalone Components & Signals\n\n## Standalone Component Pattern\n\n```typescript\nimport { Component, signal, computed, effect } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n@Component({\n  selector: 'app-user-profile',\n  standalone: true,\n  imports: [CommonModule, FormsModule],\n  templateUrl: './user-profile.component.html',\n  styleUrl: './user-profile.component.scss',\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserProfileComponent {\n  // Signal-based state\n  count = signal(0);\n  doubleCount = computed(() => this.count() * 2);\n\n  constructor() {\n    // Side effects\n    effect(() => {\n      console.log(`Count is: ${this.count()}`);\n    });\n  }\n\n  increment() {\n    this.count.update(value => value + 1);\n  }\n}\n```\n\n## Input/Output with Signals\n\n```typescript\nimport { Component, input, output, model } from '@angular/core';\n\n@Component({\n  selector: 'app-search-box',\n  standalone: true,\n  template: `\n    <input\n      [value]=\"query()\"\n      (input)=\"onQueryChange($event)\"\n      [placeholder]=\"placeholder()\" />\n  `\n})\nexport class SearchBoxComponent {\n  // Signal inputs (Angular 17.1+)\n  placeholder = input<string>('Search...');\n  initialQuery = input<string>('');\n\n  // Signal outputs\n  queryChange = output<string>();\n\n  // Two-way binding with model signal\n  query = model<string>('');\n\n  onQueryChange(event: Event) {\n    const value = (event.target as HTMLInputElement).value;\n    this.query.set(value);\n    this.queryChange.emit(value);\n  }\n}\n\n// Parent usage\n@Component({\n  template: `\n    <app-search-box\n      [(query)]=\"searchQuery\"\n      [placeholder]=\"'Find users...'\"\n      (queryChange)=\"onSearch($event)\" />\n  `\n})\nexport class ParentComponent {\n  searchQuery = signal('');\n\n  onSearch(query: string) {\n    console.log('Searching:', query);\n  }\n}\n```\n\n## Smart vs Dumb Components\n\n```typescript\n// Smart Component (Container)\n@Component({\n  selector: 'app-users-container',\n  standalone: true,\n  imports: [UserListComponent],\n  template: `\n    <app-user-list\n      [users]=\"users()\"\n      [loading]=\"loading()\"\n      (userSelected)=\"onUserSelected($event)\" />\n  `\n})\nexport class UsersContainerComponent {\n  private usersService = inject(UsersService);\n\n  users = signal<User[]>([]);\n  loading = signal(true);\n\n  constructor() {\n    effect(() => {\n      this.usersService.getUsers().subscribe({\n        next: users => {\n          this.users.set(users);\n          this.loading.set(false);\n        },\n        error: err => console.error(err)\n      });\n    });\n  }\n\n  onUserSelected(user: User) {\n    // Handle business logic\n  }\n}\n\n// Dumb Component (Presentational)\n@Component({\n  selector: 'app-user-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div (click)=\"userSelected.emit(user)\">\n          {{ user.name }}\n        </div>\n      }\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserListComponent {\n  users = input.required<User[]>();\n  loading = input<boolean>(false);\n  userSelected = output<User>();\n}\n```\n\n## Content Projection\n\n```typescript\n// Card component with multiple slots\n@Component({\n  selector: 'app-card',\n  standalone: true,\n  template: `\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <ng-content select=\"[header]\"></ng-content>\n      </div>\n      <div class=\"card-body\">\n        <ng-content></ng-content>\n      </div>\n      <div class=\"card-footer\">\n        <ng-content select=\"[footer]\"></ng-content>\n      </div>\n    </div>\n  `\n})\nexport class CardComponent {}\n\n// Usage\n@Component({\n  template: `\n    <app-card>\n      <h2 header>Card Title</h2>\n      <p>Card content goes here</p>\n      <button footer>Action</button>\n    </app-card>\n  `\n})\nexport class ParentComponent {}\n```\n\n## Dependency Injection\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { UserService } from './user.service';\n\n@Component({\n  selector: 'app-user-dashboard',\n  standalone: true\n})\nexport class UserDashboardComponent {\n  // Modern inject() API\n  private userService = inject(UserService);\n  private router = inject(Router);\n\n  // Optional dependency\n  private logger = inject(LoggerService, { optional: true });\n\n  users = signal<User[]>([]);\n\n  ngOnInit() {\n    this.loadUsers();\n  }\n\n  loadUsers() {\n    this.userService.getUsers().subscribe({\n      next: users => this.users.set(users),\n      error: err => this.logger?.error('Failed to load users', err)\n    });\n  }\n}\n```\n\n## New Control Flow (@if, @for)\n\n```typescript\n@Component({\n  template: `\n    <!-- @if instead of *ngIf -->\n    @if (user(); as currentUser) {\n      <div>Hello, {{ currentUser.name }}</div>\n    } @else if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      <div>Please log in</div>\n    }\n\n    <!-- @for instead of *ngFor -->\n    @for (item of items(); track item.id) {\n      <div>{{ item.name }}</div>\n    } @empty {\n      <div>No items found</div>\n    }\n\n    <!-- @switch instead of *ngSwitch -->\n    @switch (status()) {\n      @case ('pending') {\n        <span>Pending...</span>\n      }\n      @case ('success') {\n        <span>Success!</span>\n      }\n      @default {\n        <span>Unknown</span>\n      }\n    }\n  `\n})\nexport class ModernControlFlowComponent {\n  user = signal<User | null>(null);\n  loading = signal(false);\n  items = signal<Item[]>([]);\n  status = signal<'pending' | 'success' | 'error'>('pending');\n}\n```\n\n## Performance: OnPush & TrackBy\n\n```typescript\n@Component({\n  selector: 'app-product-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @for (product of products(); track trackByProductId($index, product)) {\n      <app-product-card [product]=\"product\" />\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ProductListComponent {\n  products = input.required<Product[]>();\n\n  // TrackBy for optimal rendering\n  trackByProductId(index: number, product: Product): number {\n    return product.id;\n  }\n}\n```\n\n## Quick Reference\n\n| Pattern | Angular 17+ Approach |\n|---------|---------------------|\n| Component | Standalone by default |\n| State | Signals (`signal()`, `computed()`) |\n| Input | `input()`, `input.required()` |\n| Output | `output<T>()` |\n| Two-way | `model<T>()` |\n| DI | `inject()` function |\n| Control Flow | `@if`, `@for`, `@switch` |\n| Change Detection | `ChangeDetectionStrategy.OnPush` |\n","isInternal":false,"tokens":1568,"sizeBytes":6498},{"name":"ngrx.md","path":"skills/angular-architect/references/ngrx.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/angular-architect/references/ngrx.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# NgRx State Management\n\n## Store Setup\n\n```typescript\n// app.config.ts\nimport { provideStore } from '@ngrx/store';\nimport { provideEffects } from '@ngrx/effects';\nimport { provideStoreDevtools } from '@ngrx/store-devtools';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideStore({\n      users: usersReducer,\n      products: productsReducer\n    }),\n    provideEffects([UsersEffects, ProductsEffects]),\n    provideStoreDevtools({\n      maxAge: 25,\n      logOnly: !isDevMode()\n    })\n  ]\n};\n```\n\n## Actions (Modern)\n\n```typescript\n// users.actions.ts\nimport { createActionGroup, emptyProps, props } from '@ngrx/store';\nimport { User } from './user.model';\n\nexport const UsersActions = createActionGroup({\n  source: 'Users',\n  events: {\n    'Load Users': emptyProps(),\n    'Load Users Success': props<{ users: User[] }>(),\n    'Load Users Failure': props<{ error: string }>(),\n\n    'Add User': props<{ user: User }>(),\n    'Add User Success': props<{ user: User }>(),\n    'Add User Failure': props<{ error: string }>(),\n\n    'Update User': props<{ id: string; changes: Partial<User> }>(),\n    'Update User Success': props<{ user: User }>(),\n\n    'Delete User': props<{ id: string }>(),\n    'Delete User Success': props<{ id: string }>()\n  }\n});\n```\n\n## Reducer with Entity Adapter\n\n```typescript\n// users.reducer.ts\nimport { createReducer, on } from '@ngrx/store';\nimport { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';\nimport { UsersActions } from './users.actions';\nimport { User } from './user.model';\n\nexport interface UsersState extends EntityState<User> {\n  loading: boolean;\n  error: string | null;\n  selectedUserId: string | null;\n}\n\nexport const usersAdapter: EntityAdapter<User> = createEntityAdapter<User>({\n  selectId: (user: User) => user.id,\n  sortComparer: (a, b) => a.name.localeCompare(b.name)\n});\n\nconst initialState: UsersState = usersAdapter.getInitialState({\n  loading: false,\n  error: null,\n  selectedUserId: null\n});\n\nexport const usersReducer = createReducer(\n  initialState,\n\n  // Load users\n  on(UsersActions.loadUsers, (state) => ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n\n  on(UsersActions.loadUsersSuccess, (state, { users }) =>\n    usersAdapter.setAll(users, {\n      ...state,\n      loading: false\n    })\n  ),\n\n  on(UsersActions.loadUsersFailure, (state, { error }) => ({\n    ...state,\n    loading: false,\n    error\n  })),\n\n  // Add user\n  on(UsersActions.addUserSuccess, (state, { user }) =>\n    usersAdapter.addOne(user, state)\n  ),\n\n  // Update user\n  on(UsersActions.updateUserSuccess, (state, { user }) =>\n    usersAdapter.updateOne(\n      { id: user.id, changes: user },\n      state\n    )\n  ),\n\n  // Delete user\n  on(UsersActions.deleteUserSuccess, (state, { id }) =>\n    usersAdapter.removeOne(id, state)\n  )\n);\n```\n\n## Selectors\n\n```typescript\n// users.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport { usersAdapter, UsersState } from './users.reducer';\n\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\n\n// Entity adapter selectors\nconst {\n  selectIds,\n  selectEntities,\n  selectAll,\n  selectTotal\n} = usersAdapter.getSelectors();\n\nexport const selectUserIds = createSelector(\n  selectUsersState,\n  selectIds\n);\n\nexport const selectUserEntities = createSelector(\n  selectUsersState,\n  selectEntities\n);\n\nexport const selectAllUsers = createSelector(\n  selectUsersState,\n  selectAll\n);\n\nexport const selectUsersTotal = createSelector(\n  selectUsersState,\n  selectTotal\n);\n\nexport const selectUsersLoading = createSelector(\n  selectUsersState,\n  (state) => state.loading\n);\n\nexport const selectUsersError = createSelector(\n  selectUsersState,\n  (state) => state.error\n);\n\n// Parameterized selector\nexport const selectUserById = (id: string) =>\n  createSelector(\n    selectUserEntities,\n    (entities) => entities[id]\n  );\n\n// Composed selector\nexport const selectActiveUsers = createSelector(\n  selectAllUsers,\n  (users) => users.filter(user => user.isActive)\n);\n\n// Selector with multiple inputs\nexport const selectUserWithPosts = createSelector(\n  selectUserById,\n  selectAllPosts,\n  (user, posts) => ({\n    user,\n    posts: posts.filter(post => post.userId === user?.id)\n  })\n);\n```\n\n## Effects\n\n```typescript\n// users.effects.ts\nimport { Injectable, inject } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { catchError, map, mergeMap, exhaustMap } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\n\n@Injectable()\nexport class UsersEffects {\n  private actions$ = inject(Actions);\n  private usersService = inject(UsersService);\n\n  // Load users effect\n  loadUsers$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.loadUsers),\n      mergeMap(() =>\n        this.usersService.getAll().pipe(\n          map(users => UsersActions.loadUsersSuccess({ users })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Add user effect (exhaustMap prevents duplicate submits)\n  addUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.addUser),\n      exhaustMap(({ user }) =>\n        this.usersService.create(user).pipe(\n          map(createdUser => UsersActions.addUserSuccess({ user: createdUser })),\n          catchError(error =>\n            of(UsersActions.addUserFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Update user effect\n  updateUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.updateUser),\n      mergeMap(({ id, changes }) =>\n        this.usersService.update(id, changes).pipe(\n          map(user => UsersActions.updateUserSuccess({ user })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Delete user effect\n  deleteUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.deleteUser),\n      mergeMap(({ id }) =>\n        this.usersService.delete(id).pipe(\n          map(() => UsersActions.deleteUserSuccess({ id })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Non-dispatching effect (side effect only)\n  logUserActions$ = createEffect(\n    () =>\n      this.actions$.pipe(\n        ofType(\n          UsersActions.addUserSuccess,\n          UsersActions.updateUserSuccess,\n          UsersActions.deleteUserSuccess\n        ),\n        tap(action => console.log('User action:', action))\n      ),\n    { dispatch: false }\n  );\n}\n```\n\n## Component Integration\n\n```typescript\n// users-list.component.ts\nimport { Component, inject } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { UsersActions } from './store/users.actions';\nimport {\n  selectAllUsers,\n  selectUsersLoading,\n  selectUsersError\n} from './store/users.selectors';\n\n@Component({\n  selector: 'app-users-list',\n  standalone: true,\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else if (error(); as err) {\n      <div>Error: {{ err }}</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div>\n          {{ user.name }}\n          <button (click)=\"onDelete(user.id)\">Delete</button>\n        </div>\n      }\n    }\n  `\n})\nexport class UsersListComponent {\n  private store = inject(Store);\n\n  // Select data as signals\n  users = toSignal(this.store.select(selectAllUsers), { initialValue: [] });\n  loading = toSignal(this.store.select(selectUsersLoading), { initialValue: false });\n  error = toSignal(this.store.select(selectUsersError), { initialValue: null });\n\n  ngOnInit() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  onDelete(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n}\n```\n\n## Facade Pattern\n\n```typescript\n// users.facade.ts\n@Injectable({ providedIn: 'root' })\nexport class UsersFacade {\n  private store = inject(Store);\n\n  // Selectors\n  users$ = this.store.select(selectAllUsers);\n  loading$ = this.store.select(selectUsersLoading);\n  error$ = this.store.select(selectUsersError);\n\n  // Actions\n  loadUsers() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  addUser(user: User) {\n    this.store.dispatch(UsersActions.addUser({ user }));\n  }\n\n  updateUser(id: string, changes: Partial<User>) {\n    this.store.dispatch(UsersActions.updateUser({ id, changes }));\n  }\n\n  deleteUser(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n\n  getUserById(id: string) {\n    return this.store.select(selectUserById(id));\n  }\n}\n\n// Usage in component\n@Component({\n  selector: 'app-users',\n  standalone: true\n})\nexport class UsersComponent {\n  private facade = inject(UsersFacade);\n\n  users = toSignal(this.facade.users$, { initialValue: [] });\n  loading = toSignal(this.facade.loading$, { initialValue: false });\n\n  ngOnInit() {\n    this.facade.loadUsers();\n  }\n\n  onAdd(user: User) {\n    this.facade.addUser(user);\n  }\n}\n```\n\n## Quick Reference\n\n| Concept | Usage |\n|---------|-------|\n| Actions | `createActionGroup()` |\n| Reducer | `createReducer()`, `on()` |\n| Entity | `createEntityAdapter()` |\n| Selectors | `createSelector()`, `createFeatureSelector()` |\n| Effects | `createEffect()`, `ofType()` |\n| Store | `inject(Store)`, `store.select()`, `store.dispatch()` |\n| DevTools | `provideStoreDevtools()` |\n| Testing | Mock store, marble testing |\n","isInternal":false,"tokens":2247,"sizeBytes":9585},{"name":"routing.md","path":"skills/angular-architect/references/routing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/angular-architect/references/routing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Angular Routing\n\n## Routes Configuration\n\n```typescript\n// app.routes.ts\nimport { Routes } from '@angular/router';\nimport { HomeComponent } from './home/home.component';\n\nexport const routes: Routes = [\n  {\n    path: '',\n    redirectTo: '/home',\n    pathMatch: 'full'\n  },\n  {\n    path: 'home',\n    component: HomeComponent,\n    title: 'Home'\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./users/users.component').then(m => m.UsersComponent),\n    title: 'Users'\n  },\n  {\n    path: 'users/:id',\n    loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent),\n    canActivate: [authGuard],\n    resolve: { user: userResolver }\n  },\n  {\n    path: 'admin',\n    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),\n    canActivate: [authGuard, adminGuard]\n  },\n  {\n    path: '**',\n    loadComponent: () => import('./not-found/not-found.component').then(m => m.NotFoundComponent),\n    title: '404 Not Found'\n  }\n];\n\n// app.config.ts\nimport { provideRouter, withComponentInputBinding } from '@angular/router';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideRouter(\n      routes,\n      withComponentInputBinding(),  // Bind route params to @Input()\n      withViewTransitions(),        // Enable view transitions\n      withPreloading(PreloadAllModules)\n    )\n  ]\n};\n```\n\n## Lazy Loading\n\n```typescript\n// Feature routes\n// admin/admin.routes.ts\nimport { Routes } from '@angular/router';\n\nexport const ADMIN_ROUTES: Routes = [\n  {\n    path: '',\n    loadComponent: () => import('./admin-dashboard.component').then(m => m.AdminDashboardComponent)\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./admin-users.component').then(m => m.AdminUsersComponent)\n  },\n  {\n    path: 'settings',\n    loadComponent: () => import('./admin-settings.component').then(m => m.AdminSettingsComponent)\n  }\n];\n```\n\n## Functional Guards\n\n```typescript\n// guards/auth.guard.ts\nimport { inject } from '@angular/core';\nimport { Router, CanActivateFn } from '@angular/router';\nimport { AuthService } from '../services/auth.service';\n\nexport const authGuard: CanActivateFn = (route, state) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.isAuthenticated()) {\n    return true;\n  }\n\n  // Redirect to login with return URL\n  return router.createUrlTree(['/login'], {\n    queryParams: { returnUrl: state.url }\n  });\n};\n\n// Admin guard\nexport const adminGuard: CanActivateFn = () => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.hasRole('admin')) {\n    return true;\n  }\n\n  return router.createUrlTree(['/unauthorized']);\n};\n\n// Can deactivate (unsaved changes)\nexport const canDeactivateGuard: CanDeactivateFn<FormComponent> = (component) => {\n  if (component.hasUnsavedChanges()) {\n    return confirm('You have unsaved changes. Are you sure you want to leave?');\n  }\n  return true;\n};\n```\n\n## Resolvers\n\n```typescript\n// resolvers/user.resolver.ts\nimport { inject } from '@angular/core';\nimport { ResolveFn } from '@angular/router';\nimport { catchError, of } from 'rxjs';\nimport { User } from '../models/user.model';\nimport { UsersService } from '../services/users.service';\n\nexport const userResolver: ResolveFn<User | null> = (route, state) => {\n  const usersService = inject(UsersService);\n  const id = route.paramMap.get('id')!;\n\n  return usersService.getById(id).pipe(\n    catchError(() => of(null))\n  );\n};\n\n// Component receives resolved data\n@Component({\n  selector: 'app-user-detail',\n  standalone: true,\n  template: `\n    @if (user) {\n      <h1>{{ user.name }}</h1>\n    } @else {\n      <p>User not found</p>\n    }\n  `\n})\nexport class UserDetailComponent {\n  user = input<User | null>(null);  // Resolved data bound as input\n}\n```\n\n## Route Parameters\n\n```typescript\nimport { Component, inject, input } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n  selector: 'app-product-detail',\n  standalone: true\n})\nexport class ProductDetailComponent {\n  private route = inject(ActivatedRoute);\n  private router = inject(Router);\n\n  // Modern approach: route params as inputs\n  id = input.required<string>();\n\n  // Legacy approach: subscribe to params\n  ngOnInit() {\n    this.route.paramMap.subscribe(params => {\n      const id = params.get('id');\n      this.loadProduct(id);\n    });\n\n    // Query params\n    this.route.queryParamMap.subscribe(params => {\n      const filter = params.get('filter');\n      const sort = params.get('sort');\n    });\n  }\n\n  // Navigate programmatically\n  goToEdit() {\n    this.router.navigate(['/products', this.id(), 'edit']);\n  }\n\n  // Navigate with query params\n  applyFilter(filter: string) {\n    this.router.navigate([], {\n      relativeTo: this.route,\n      queryParams: { filter },\n      queryParamsHandling: 'merge'  // Preserve other params\n    });\n  }\n}\n```\n\n## Router Events\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { Router, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n  selector: 'app-root',\n  standalone: true\n})\nexport class AppComponent {\n  private router = inject(Router);\n  loading = signal(false);\n\n  constructor() {\n    // Show loading on navigation start\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationStart)\n    ).subscribe(() => {\n      this.loading.set(true);\n    });\n\n    // Hide loading on navigation end\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationEnd)\n    ).subscribe(() => {\n      this.loading.set(false);\n    });\n\n    // Handle navigation errors\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationError)\n    ).subscribe((event: NavigationError) => {\n      console.error('Navigation error:', event.error);\n      this.loading.set(false);\n    });\n  }\n}\n```\n\n## Child Routes & Outlets\n\n```typescript\n// Parent route with child routes\nconst routes: Routes = [\n  {\n    path: 'dashboard',\n    component: DashboardComponent,\n    children: [\n      {\n        path: 'stats',\n        component: StatsComponent,\n        outlet: 'panel'  // Named outlet\n      },\n      {\n        path: 'charts',\n        component: ChartsComponent,\n        outlet: 'panel'\n      }\n    ]\n  }\n];\n\n// Dashboard component template\n@Component({\n  template: `\n    <div class=\"dashboard\">\n      <div class=\"main\">\n        <router-outlet></router-outlet>  <!-- Primary outlet -->\n      </div>\n      <div class=\"panel\">\n        <router-outlet name=\"panel\"></router-outlet>  <!-- Named outlet -->\n      </div>\n    </div>\n  `\n})\nexport class DashboardComponent {}\n\n// Navigate to named outlet\nthis.router.navigate(['/dashboard', { outlets: { panel: ['stats'] } }]);\n```\n\n## Preloading Strategies\n\n```typescript\n// Custom preloading strategy\nimport { Injectable } from '@angular/core';\nimport { PreloadingStrategy, Route } from '@angular/router';\nimport { Observable, of, timer } from 'rxjs';\nimport { mergeMap } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class CustomPreloadingStrategy implements PreloadingStrategy {\n  preload(route: Route, load: () => Observable<any>): Observable<any> {\n    // Only preload routes with data.preload = true\n    if (route.data?.['preload']) {\n      const delay = route.data?.['preloadDelay'] || 0;\n      return timer(delay).pipe(\n        mergeMap(() => load())\n      );\n    }\n    return of(null);\n  }\n}\n\n// Route config with preload data\nconst routes: Routes = [\n  {\n    path: 'important',\n    loadChildren: () => import('./important/important.routes'),\n    data: { preload: true, preloadDelay: 2000 }\n  }\n];\n\n// Register in app config\nprovideRouter(routes, withPreloading(CustomPreloadingStrategy))\n```\n\n## Route Guards with Observables\n\n```typescript\nexport const dataGuard: CanActivateFn = (route, state) => {\n  const dataService = inject(DataService);\n  const router = inject(Router);\n\n  return dataService.checkAccess(route.params['id']).pipe(\n    map(hasAccess => {\n      if (hasAccess) {\n        return true;\n      }\n      return router.createUrlTree(['/no-access']);\n    }),\n    catchError(() => {\n      return of(router.createUrlTree(['/error']));\n    })\n  );\n};\n```\n\n## Quick Reference\n\n| Feature | Usage |\n|---------|-------|\n| Routes | `Routes` array in app.routes.ts |\n| Lazy load | `loadComponent()`, `loadChildren()` |\n| Guards | `CanActivateFn`, `CanDeactivateFn` |\n| Resolvers | `ResolveFn<T>` |\n| Params | `route.paramMap`, `input<T>()` |\n| Query | `route.queryParamMap` |\n| Navigate | `router.navigate()`, `routerLink` |\n| Events | `router.events` |\n| Outlets | `<router-outlet name=\"...\">` |\n| Preload | `withPreloading()` |\n","isInternal":false,"tokens":2050,"sizeBytes":8752},{"name":"rxjs.md","path":"skills/angular-architect/references/rxjs.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/angular-architect/references/rxjs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# RxJS Patterns\n\n## Essential Operators\n\n```typescript\nimport { Component, inject, signal } from '@angular/core';\nimport {\n  map, filter, switchMap, catchError,\n  debounceTime, distinctUntilChanged,\n  tap, shareReplay, takeUntil\n} from 'rxjs/operators';\nimport { Subject, of, EMPTY } from 'rxjs';\n\n@Component({\n  selector: 'app-search',\n  standalone: true\n})\nexport class SearchComponent {\n  private searchService = inject(SearchService);\n  private destroy$ = new Subject<void>();\n\n  searchTerm$ = new Subject<string>();\n  results = signal<SearchResult[]>([]);\n\n  ngOnInit() {\n    this.searchTerm$.pipe(\n      debounceTime(300),              // Wait 300ms after typing\n      distinctUntilChanged(),         // Only if value changed\n      filter(term => term.length > 2), // Minimum 3 characters\n      tap(() => this.loading.set(true)),\n      switchMap(term =>               // Cancel previous requests\n        this.searchService.search(term).pipe(\n          catchError(err => {\n            console.error(err);\n            return of([]);            // Return empty on error\n          })\n        )\n      ),\n      tap(() => this.loading.set(false)),\n      takeUntil(this.destroy$)        // Auto-unsubscribe\n    ).subscribe(results => this.results.set(results));\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Subject Types\n\n```typescript\nimport { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';\n\nexport class SubjectExamples {\n  // Subject: No initial value, only emits to future subscribers\n  private clickSubject = new Subject<MouseEvent>();\n  click$ = this.clickSubject.asObservable();\n\n  onClick(event: MouseEvent) {\n    this.clickSubject.next(event);\n  }\n\n  // BehaviorSubject: Has initial value, emits latest value to new subscribers\n  private loadingSubject = new BehaviorSubject<boolean>(false);\n  loading$ = this.loadingSubject.asObservable();\n\n  setLoading(loading: boolean) {\n    this.loadingSubject.next(loading);\n  }\n\n  // ReplaySubject: Replays N previous values to new subscribers\n  private activitySubject = new ReplaySubject<Activity>(3); // Last 3 activities\n  activity$ = this.activitySubject.asObservable();\n\n  // AsyncSubject: Only emits last value when completed\n  private finalResultSubject = new AsyncSubject<Result>();\n  finalResult$ = this.finalResultSubject.asObservable();\n}\n```\n\n## Higher-Order Operators\n\n```typescript\nimport { switchMap, mergeMap, concatMap, exhaustMap } from 'rxjs/operators';\n\nexport class HigherOrderExamples {\n  private http = inject(HttpClient);\n\n  // switchMap: Cancel previous, use latest (search, typeahead)\n  searchUsers(term$: Observable<string>) {\n    return term$.pipe(\n      switchMap(term => this.http.get<User[]>(`/api/users?q=${term}`))\n    );\n  }\n\n  // mergeMap: Process all concurrently (independent requests)\n  uploadFiles(files: File[]) {\n    return from(files).pipe(\n      mergeMap(file => this.http.post('/api/upload', file))\n    );\n  }\n\n  // concatMap: Process sequentially (order matters)\n  processQueue(tasks: Task[]) {\n    return from(tasks).pipe(\n      concatMap(task => this.http.post('/api/process', task))\n    );\n  }\n\n  // exhaustMap: Ignore new until current completes (prevent double-click)\n  saveForm(clicks$: Observable<void>, formData: any) {\n    return clicks$.pipe(\n      exhaustMap(() => this.http.post('/api/save', formData))\n    );\n  }\n}\n```\n\n## Error Handling\n\n```typescript\nimport { catchError, retry, retryWhen, delay, tap } from 'rxjs/operators';\nimport { throwError, of, timer } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class DataService {\n  private http = inject(HttpClient);\n\n  // Retry with exponential backoff\n  getData() {\n    return this.http.get<Data>('/api/data').pipe(\n      retryWhen(errors =>\n        errors.pipe(\n          mergeMap((error, index) => {\n            if (index >= 3) {\n              return throwError(() => error);\n            }\n            const delayMs = Math.pow(2, index) * 1000;\n            return timer(delayMs);\n          })\n        )\n      ),\n      catchError(err => {\n        console.error('Failed after retries:', err);\n        return of(null); // Fallback value\n      })\n    );\n  }\n\n  // Catch and rethrow with context\n  saveData(data: Data) {\n    return this.http.post('/api/data', data).pipe(\n      catchError(err => {\n        if (err.status === 401) {\n          // Handle auth error\n          return throwError(() => new Error('Unauthorized'));\n        }\n        return throwError(() => err);\n      })\n    );\n  }\n}\n```\n\n## Memory Management\n\n```typescript\nimport { Component, DestroyRef, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n@Component({\n  selector: 'app-auto-cleanup',\n  standalone: true\n})\nexport class AutoCleanupComponent {\n  private dataService = inject(DataService);\n  private destroyRef = inject(DestroyRef);\n\n  data = signal<Data[]>([]);\n\n  constructor() {\n    // Modern approach: takeUntilDestroyed\n    this.dataService.getData().pipe(\n      takeUntilDestroyed()  // Auto-cleanup on destroy\n    ).subscribe(data => this.data.set(data));\n\n    // Manual cleanup with DestroyRef\n    const subscription = this.dataService.getUpdates().subscribe();\n    this.destroyRef.onDestroy(() => subscription.unsubscribe());\n  }\n}\n\n// Legacy approach (still valid)\n@Component({\n  selector: 'app-manual-cleanup',\n  standalone: true\n})\nexport class ManualCleanupComponent implements OnDestroy {\n  private destroy$ = new Subject<void>();\n\n  ngOnInit() {\n    this.dataService.getData().pipe(\n      takeUntil(this.destroy$)\n    ).subscribe();\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Combining Observables\n\n```typescript\nimport { combineLatest, forkJoin, merge, zip } from 'rxjs';\n\nexport class CombiningExamples {\n  private http = inject(HttpClient);\n\n  // combineLatest: Emit when any source emits (latest values)\n  getDashboard() {\n    return combineLatest({\n      user: this.http.get<User>('/api/user'),\n      stats: this.http.get<Stats>('/api/stats'),\n      notifications: this.http.get<Notification[]>('/api/notifications')\n    }).pipe(\n      map(({ user, stats, notifications }) => ({\n        user,\n        stats,\n        notifications\n      }))\n    );\n  }\n\n  // forkJoin: Emit when all sources complete (like Promise.all)\n  loadAllData() {\n    return forkJoin({\n      users: this.http.get<User[]>('/api/users'),\n      products: this.http.get<Product[]>('/api/products'),\n      orders: this.http.get<Order[]>('/api/orders')\n    });\n  }\n\n  // merge: Emit when any source emits (flattens all)\n  getActivityFeed() {\n    return merge(\n      this.http.get<Activity[]>('/api/recent'),\n      this.http.get<Activity[]>('/api/trending')\n    );\n  }\n}\n```\n\n## Custom Operators\n\n```typescript\nimport { Observable, OperatorFunction } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\n// Custom operator for logging\nexport function debug<T>(tag: string): OperatorFunction<T, T> {\n  return (source: Observable<T>) =>\n    source.pipe(\n      tap({\n        next: value => console.log(`[${tag}] Next:`, value),\n        error: err => console.error(`[${tag}] Error:`, err),\n        complete: () => console.log(`[${tag}] Complete`)\n      })\n    );\n}\n\n// Usage\nthis.http.get('/api/data').pipe(\n  debug('API Call'),\n  map(data => transform(data))\n).subscribe();\n```\n\n## ShareReplay for Caching\n\n```typescript\nimport { shareReplay } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class ConfigService {\n  private http = inject(HttpClient);\n\n  // Cache config, share with all subscribers\n  config$ = this.http.get<Config>('/api/config').pipe(\n    shareReplay({ bufferSize: 1, refCount: true })\n  );\n\n  // All components get same config without extra HTTP calls\n  getConfig() {\n    return this.config$;\n  }\n}\n```\n\n## Quick Reference\n\n| Use Case | Operator |\n|----------|----------|\n| Transform values | `map`, `pluck` |\n| Filter values | `filter`, `distinctUntilChanged` |\n| Time-based | `debounceTime`, `throttleTime`, `delay` |\n| Cancel previous | `switchMap` |\n| Process all | `mergeMap` |\n| Sequential | `concatMap` |\n| Ignore new | `exhaustMap` |\n| Combine latest | `combineLatest` |\n| Wait for all | `forkJoin` |\n| Error handling | `catchError`, `retry` |\n| Cleanup | `takeUntilDestroyed`, `takeUntil` |\n| Share result | `shareReplay` |\n","isInternal":false,"tokens":1918,"sizeBytes":8357},{"name":"testing.md","path":"skills/angular-architect/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/angular-architect/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Angular Testing\n\n## Component Testing\n\n```typescript\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { signal } from '@angular/core';\nimport { UserListComponent } from './user-list.component';\nimport { UsersService } from './users.service';\nimport { of } from 'rxjs';\n\ndescribe('UserListComponent', () => {\n  let component: UserListComponent;\n  let fixture: ComponentFixture<UserListComponent>;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  const mockUsers = [\n    { id: '1', name: 'John Doe', email: 'john@example.com' },\n    { id: '2', name: 'Jane Smith', email: 'jane@example.com' }\n  ];\n\n  beforeEach(async () => {\n    // Create spy object\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll', 'delete']);\n\n    await TestBed.configureTestingModule({\n      imports: [UserListComponent],  // Standalone component\n      providers: [\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    }).compileComponents();\n\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n    fixture = TestBed.createComponent(UserListComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n\n  it('should load users on init', () => {\n    usersService.getAll.and.returnValue(of(mockUsers));\n\n    fixture.detectChanges();  // Trigger ngOnInit\n\n    expect(usersService.getAll).toHaveBeenCalled();\n    expect(component.users()).toEqual(mockUsers);\n  });\n\n  it('should display users in template', () => {\n    component.users.set(mockUsers);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    const userElements = compiled.querySelectorAll('.user-item');\n\n    expect(userElements.length).toBe(2);\n    expect(userElements[0].textContent).toContain('John Doe');\n  });\n\n  it('should emit userSelected when user clicked', () => {\n    const emitSpy = spyOn(component.userSelected, 'emit');\n\n    component.onUserClick(mockUsers[0]);\n\n    expect(emitSpy).toHaveBeenCalledWith(mockUsers[0]);\n  });\n\n  it('should show loading state', () => {\n    component.loading.set(true);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    expect(compiled.querySelector('.loading')).toBeTruthy();\n  });\n});\n```\n\n## Service Testing\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';\nimport { UsersService } from './users.service';\nimport { User } from './user.model';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let httpMock: HttpTestingController;\n\n  const mockUsers: User[] = [\n    { id: '1', name: 'John', email: 'john@example.com' },\n    { id: '2', name: 'Jane', email: 'jane@example.com' }\n  ];\n\n  beforeEach(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule],\n      providers: [UsersService]\n    });\n\n    service = TestBed.inject(UsersService);\n    httpMock = TestBed.inject(HttpTestingController);\n  });\n\n  afterEach(() => {\n    httpMock.verify();  // Verify no outstanding requests\n  });\n\n  it('should fetch all users', (done) => {\n    service.getAll().subscribe(users => {\n      expect(users).toEqual(mockUsers);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('GET');\n    req.flush(mockUsers);\n  });\n\n  it('should create a user', (done) => {\n    const newUser: User = { id: '3', name: 'Bob', email: 'bob@example.com' };\n\n    service.create(newUser).subscribe(user => {\n      expect(user).toEqual(newUser);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('POST');\n    expect(req.request.body).toEqual(newUser);\n    req.flush(newUser);\n  });\n\n  it('should handle error', (done) => {\n    service.getAll().subscribe({\n      next: () => fail('should have failed'),\n      error: (error) => {\n        expect(error.status).toBe(500);\n        done();\n      }\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    req.flush('Server error', { status: 500, statusText: 'Internal Server Error' });\n  });\n});\n```\n\n## RxJS Marble Testing\n\n```typescript\nimport { TestScheduler } from 'rxjs/testing';\nimport { delay, map } from 'rxjs/operators';\n\ndescribe('RxJS Operators', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should map values', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--c--|', { a: 1, b: 2, c: 3 });\n      const expected = '    --x--y--z--|';\n      const result$ = source$.pipe(map(x => x * 10));\n\n      expectObservable(result$).toBe(expected, { x: 10, y: 20, z: 30 });\n    });\n  });\n\n  it('should delay emissions', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--|', { a: 1, b: 2 });\n      const expected = '    ----a--b--|';\n      const result$ = source$.pipe(delay(20));\n\n      expectObservable(result$).toBe(expected, { a: 1, b: 2 });\n    });\n  });\n});\n```\n\n## Testing with Signals\n\n```typescript\nimport { signal } from '@angular/core';\n\ndescribe('Counter Component', () => {\n  it('should update signal value', () => {\n    const count = signal(0);\n\n    expect(count()).toBe(0);\n\n    count.set(5);\n    expect(count()).toBe(5);\n\n    count.update(val => val + 1);\n    expect(count()).toBe(6);\n  });\n\n  it('should compute derived value', () => {\n    const count = signal(5);\n    const doubled = computed(() => count() * 2);\n\n    expect(doubled()).toBe(10);\n\n    count.set(10);\n    expect(doubled()).toBe(20);\n  });\n});\n```\n\n## Testing NgRx\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockStore, MockStore } from '@ngrx/store/testing';\nimport { UsersComponent } from './users.component';\nimport { selectAllUsers, selectUsersLoading } from './store/users.selectors';\n\ndescribe('UsersComponent with NgRx', () => {\n  let component: UsersComponent;\n  let fixture: ComponentFixture<UsersComponent>;\n  let store: MockStore;\n\n  const initialState = {\n    users: {\n      ids: ['1', '2'],\n      entities: {\n        '1': { id: '1', name: 'John' },\n        '2': { id: '2', name: 'Jane' }\n      },\n      loading: false\n    }\n  };\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [UsersComponent],\n      providers: [\n        provideMockStore({ initialState })\n      ]\n    }).compileComponents();\n\n    store = TestBed.inject(MockStore);\n    fixture = TestBed.createComponent(UsersComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should select users from store', () => {\n    store.overrideSelector(selectAllUsers, [\n      { id: '1', name: 'John' },\n      { id: '2', name: 'Jane' }\n    ]);\n\n    fixture.detectChanges();\n\n    expect(component.users().length).toBe(2);\n  });\n\n  it('should dispatch action on delete', () => {\n    const dispatchSpy = spyOn(store, 'dispatch');\n\n    component.onDelete('1');\n\n    expect(dispatchSpy).toHaveBeenCalledWith(\n      UsersActions.deleteUser({ id: '1' })\n    );\n  });\n});\n```\n\n## Testing Effects\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockActions } from '@ngrx/effects/testing';\nimport { Observable, of, throwError } from 'rxjs';\nimport { UsersEffects } from './users.effects';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\nimport { hot, cold } from 'jasmine-marbles';\n\ndescribe('UsersEffects', () => {\n  let actions$: Observable<any>;\n  let effects: UsersEffects;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  beforeEach(() => {\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        UsersEffects,\n        provideMockActions(() => actions$),\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    });\n\n    effects = TestBed.inject(UsersEffects);\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n  });\n\n  it('should load users successfully', () => {\n    const users = [{ id: '1', name: 'John' }];\n    const action = UsersActions.loadUsers();\n    const outcome = UsersActions.loadUsersSuccess({ users });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-b|', { b: users });\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n\n  it('should handle load users failure', () => {\n    const action = UsersActions.loadUsers();\n    const error = new Error('Failed to load');\n    const outcome = UsersActions.loadUsersFailure({ error: error.message });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-#|', {}, error);\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n});\n```\n\n## Testing Guards\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { Router } from '@angular/router';\nimport { authGuard } from './auth.guard';\nimport { AuthService } from './auth.service';\n\ndescribe('authGuard', () => {\n  let authService: jasmine.SpyObj<AuthService>;\n  let router: jasmine.SpyObj<Router>;\n\n  beforeEach(() => {\n    const authServiceSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated']);\n    const routerSpy = jasmine.createSpyObj('Router', ['createUrlTree']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        { provide: AuthService, useValue: authServiceSpy },\n        { provide: Router, useValue: routerSpy }\n      ]\n    });\n\n    authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;\n    router = TestBed.inject(Router) as jasmine.SpyObj<Router>;\n  });\n\n  it('should allow access when authenticated', () => {\n    authService.isAuthenticated.and.returnValue(true);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, {} as any)\n    );\n\n    expect(result).toBe(true);\n  });\n\n  it('should redirect when not authenticated', () => {\n    authService.isAuthenticated.and.returnValue(false);\n    const urlTree = {} as any;\n    router.createUrlTree.and.returnValue(urlTree);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, { url: '/protected' } as any)\n    );\n\n    expect(result).toBe(urlTree);\n    expect(router.createUrlTree).toHaveBeenCalledWith(\n      ['/login'],\n      { queryParams: { returnUrl: '/protected' } }\n    );\n  });\n});\n```\n\n## Quick Reference\n\n| Test Type | Key Tools |\n|-----------|-----------|\n| Component | `TestBed`, `ComponentFixture`, `detectChanges()` |\n| Service | `HttpClientTestingModule`, `HttpTestingController` |\n| RxJS | `TestScheduler`, marble diagrams |\n| NgRx Store | `provideMockStore`, `MockStore` |\n| Effects | `provideMockActions`, jasmine-marbles |\n| Guards | `TestBed.runInInjectionContext()` |\n| Signals | Direct value checks with `()` |\n| Spies | `jasmine.createSpyObj()`, `spyOn()` |\n","isInternal":false,"tokens":2605,"sizeBytes":11197},{"name":"SKILL.md","path":"skills/angular-architect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/angular-architect/SKILL.md","title":"angular-architect","category":"anthropic-skill","format":"markdown","content":"---\nname: angular-architect\ndescription: Generates Angular 17+ standalone components, configures advanced routing with lazy loading and guards, implements NgRx state management, applies RxJS patterns, and optimizes bundle performance. Use when building Angular 17+ applications with standalone components or signals, setting up NgRx stores, establishing RxJS reactive patterns, performance tuning, or writing Angular tests for enterprise apps.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: frontend\n  triggers: Angular, Angular 17, standalone components, signals, RxJS, NgRx, Angular performance, Angular routing, Angular testing\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: typescript-pro, test-master\n---\n\n# Angular Architect\n\nSenior Angular architect specializing in Angular 17+ with standalone components, signals, and enterprise-grade application development.\n\n## Core Workflow\n\n1. **Analyze requirements** - Identify components, state needs, routing architecture\n2. **Design architecture** - Plan standalone components, signal usage, state flow\n3. **Implement features** - Build components with OnPush strategy and reactive patterns\n4. **Manage state** - Setup NgRx store, effects, selectors as needed; verify store hydration and action flow with Redux DevTools before proceeding\n5. **Optimize** - Apply performance best practices and bundle optimization; run `ng build --configuration production` to verify bundle size and flag regressions\n6. **Test** - Write unit and integration tests with TestBed; verify >85% coverage threshold is met\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Components | `references/components.md` | Standalone components, signals, input/output |\n| RxJS | `references/rxjs.md` | Observables, operators, subjects, error handling |\n| NgRx | `references/ngrx.md` | Store, effects, selectors, entity adapter |\n| Routing | `references/routing.md` | Router config, guards, lazy loading, resolvers |\n| Testing | `references/testing.md` | TestBed, component tests, service tests |\n\n## Key Patterns\n\n### Standalone Component with OnPush and Signals\n\n```typescript\nimport { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n  selector: 'app-user-card',\n  standalone: true,\n  imports: [CommonModule],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <div class=\"user-card\">\n      <h2>{{ fullName() }}</h2>\n      <button (click)=\"onSelect()\">Select</button>\n    </div>\n  `,\n})\nexport class UserCardComponent {\n  firstName = input.required<string>();\n  lastName = input.required<string>();\n  selected = output<string>();\n\n  fullName = computed(() => `${this.firstName()} ${this.lastName()}`);\n\n  onSelect(): void {\n    this.selected.emit(this.fullName());\n  }\n}\n```\n\n### RxJS Subscription Management with `takeUntilDestroyed`\n\n```typescript\nimport { Component, OnInit, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { UserService } from './user.service';\n\n@Component({ selector: 'app-users', standalone: true, template: `...` })\nexport class UsersComponent implements OnInit {\n  private userService = inject(UserService);\n  // DestroyRef is captured at construction time for use in ngOnInit\n  private destroyRef = inject(DestroyRef);\n\n  ngOnInit(): void {\n    this.userService.getUsers()\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe({\n        next: (users) => { /* handle */ },\n        error: (err) => console.error('Failed to load users', err),\n      });\n  }\n}\n```\n\n### NgRx Action / Reducer / Selector\n\n```typescript\n// actions\nexport const loadUsers = createAction('[Users] Load Users');\nexport const loadUsersSuccess = createAction('[Users] Load Users Success', props<{ users: User[] }>());\nexport const loadUsersFailure = createAction('[Users] Load Users Failure', props<{ error: string }>());\n\n// reducer\nexport interface UsersState { users: User[]; loading: boolean; error: string | null; }\nconst initialState: UsersState = { users: [], loading: false, error: null };\n\nexport const usersReducer = createReducer(\n  initialState,\n  on(loadUsers, (state) => ({ ...state, loading: true, error: null })),\n  on(loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),\n  on(loadUsersFailure, (state, { error }) => ({ ...state, error, loading: false })),\n);\n\n// selectors\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\nexport const selectAllUsers = createSelector(selectUsersState, (s) => s.users);\nexport const selectUsersLoading = createSelector(selectUsersState, (s) => s.loading);\n```\n\n## Constraints\n\n### MUST DO\n- Use standalone components (Angular 17+ default)\n- Use signals for reactive state where appropriate\n- Use OnPush change detection strategy\n- Use strict TypeScript configuration\n- Implement proper error handling in RxJS streams\n- Use `trackBy` functions in `*ngFor` loops\n- Write tests with >85% coverage\n- Follow Angular style guide\n\n### MUST NOT DO\n- Use NgModule-based components (except when required for compatibility)\n- Forget to unsubscribe from observables (use `takeUntilDestroyed` or `async` pipe)\n- Use async operations without proper error handling\n- Skip accessibility attributes\n- Expose sensitive data in client-side code\n- Use `any` type without justification\n- Mutate state directly in NgRx\n- Skip unit tests for critical logic\n\n## Output Templates\n\nWhen implementing Angular features, provide:\n1. Component file with standalone configuration\n2. Service file if business logic is involved\n3. State management files if using NgRx\n4. Test file with comprehensive test cases\n5. Brief explanation of architectural decisions\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/frontend/angular-architect/)\n","frontmatter":{"name":"angular-architect","description":"Generates Angular 17+ standalone components, configures advanced routing with lazy loading and guards, implements NgRx state management, applies RxJS patterns, and optimizes bundle performance. Use when building Angular 17+ applications with standalone components or signals, setting up NgRx stores, establishing RxJS reactive patterns, performance tuning, or writing Angular tests for enterprise apps.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"Angular, Angular 17, standalone components, signals, RxJS, NgRx, Angular performance, Angular routing, Angular testing","role":"specialist","scope":"implementation","output-format":"code","related-skills":"typescript-pro, test-master"}},"isInternal":false,"tokens":1358,"sizeBytes":6169},{"name":"error-handling.md","path":"skills/api-designer/references/error-handling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/api-designer/references/error-handling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# API Error Handling\n\n## Error Response Design\n\nConsistent, informative error responses are critical for API usability.\n\n## Standard Error Format\n\n### Basic Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 123 not found\",\n    \"details\": null\n  }\n}\n```\n\n### RFC 7807 Problem Details\n\nStandardized error format (application/problem+json):\n\n```http\nHTTP/1.1 404 Not Found\nContent-Type: application/problem+json\n\n{\n  \"type\": \"https://api.example.com/errors/resource-not-found\",\n  \"title\": \"Resource Not Found\",\n  \"status\": 404,\n  \"detail\": \"User with ID 123 does not exist\",\n  \"instance\": \"/users/123\"\n}\n```\n\n**Fields:**\n- `type` - URI reference identifying error type\n- `title` - Short, human-readable summary\n- `status` - HTTP status code\n- `detail` - Human-readable explanation specific to this occurrence\n- `instance` - URI reference for this specific occurrence\n\n### Extended Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be between 18 and 120\"\n      }\n    ],\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\",\n    \"documentation_url\": \"https://api.example.com/docs/errors#validation-error\"\n  }\n}\n```\n\n## Error Categories\n\n### 1. Validation Errors (400 Bad Request)\n\nClient sent invalid data.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"\",\n  \"email\": \"invalid-email\",\n  \"age\": 15\n}\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"name\",\n        \"code\": \"REQUIRED\",\n        \"message\": \"Name is required\"\n      },\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be at least 18\",\n        \"constraints\": {\n          \"min\": 18,\n          \"max\": 120\n        }\n      }\n    ]\n  }\n}\n```\n\n### 2. Authentication Errors (401 Unauthorized)\n\nMissing or invalid authentication credentials.\n\n```http\nGET /users/123\nAuthorization: Bearer <invalid-access-token>\n\nResponse: 401 Unauthorized\nWWW-Authenticate: Bearer realm=\"api\", error=\"invalid_token\"\n\n{\n  \"error\": {\n    \"code\": \"INVALID_TOKEN\",\n    \"message\": \"The access token is invalid or has expired\",\n    \"details\": {\n      \"reason\": \"token_expired\",\n      \"expired_at\": \"2024-01-15T10:00:00Z\"\n    }\n  }\n}\n```\n\n**Common auth error codes:**\n- `MISSING_TOKEN` - No auth token provided\n- `INVALID_TOKEN` - Token is malformed or invalid\n- `EXPIRED_TOKEN` - Token has expired\n- `REVOKED_TOKEN` - Token has been revoked\n\n### 3. Authorization Errors (403 Forbidden)\n\nAuthenticated but not authorized to perform action.\n\n```http\nDELETE /users/123\nAuthorization: Bearer <access-token>\n\nResponse: 403 Forbidden\n{\n  \"error\": {\n    \"code\": \"INSUFFICIENT_PERMISSIONS\",\n    \"message\": \"You do not have permission to delete this user\",\n    \"details\": {\n      \"required_permission\": \"users:delete\",\n      \"your_permissions\": [\"users:read\", \"users:update\"]\n    }\n  }\n}\n```\n\n### 4. Not Found Errors (404 Not Found)\n\nResource doesn't exist.\n\n```http\nGET /users/99999\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 99999 not found\",\n    \"details\": {\n      \"resource_type\": \"User\",\n      \"resource_id\": \"99999\"\n    }\n  }\n}\n```\n\n### 5. Conflict Errors (409 Conflict)\n\nRequest conflicts with current state.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"email\": \"existing@example.com\",\n  \"name\": \"John Doe\"\n}\n\nResponse: 409 Conflict\n{\n  \"error\": {\n    \"code\": \"RESOURCE_ALREADY_EXISTS\",\n    \"message\": \"User with email 'existing@example.com' already exists\",\n    \"details\": {\n      \"field\": \"email\",\n      \"value\": \"existing@example.com\",\n      \"existing_resource\": \"/users/123\"\n    }\n  }\n}\n```\n\n### 6. Rate Limiting (429 Too Many Requests)\n\nClient exceeded rate limit.\n\n```http\nGET /users\n\nResponse: 429 Too Many Requests\nRetry-After: 60\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1705320000\n\n{\n  \"error\": {\n    \"code\": \"RATE_LIMIT_EXCEEDED\",\n    \"message\": \"You have exceeded the rate limit\",\n    \"details\": {\n      \"limit\": 100,\n      \"window\": \"1 hour\",\n      \"retry_after\": 60,\n      \"reset_at\": \"2024-01-15T11:00:00Z\"\n    }\n  }\n}\n```\n\n### 7. Server Errors (500 Internal Server Error)\n\nUnexpected server error.\n\n```http\nGET /users/123\n\nResponse: 500 Internal Server Error\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\",\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\"\n  }\n}\n```\n\n**Never expose:**\n- Stack traces\n- Database errors\n- Internal paths\n- Sensitive configuration\n\n### 8. Service Unavailable (503 Service Unavailable)\n\nService temporarily unavailable.\n\n```http\nGET /users\n\nResponse: 503 Service Unavailable\nRetry-After: 300\n\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service is temporarily unavailable due to maintenance\",\n    \"details\": {\n      \"retry_after\": 300,\n      \"maintenance_end\": \"2024-01-15T12:00:00Z\"\n    }\n  }\n}\n```\n\n## Error Code Catalog\n\nDefine standard error codes for your API:\n\n```json\n{\n  \"VALIDATION_ERROR\": {\n    \"status\": 400,\n    \"description\": \"Request validation failed\",\n    \"subcodes\": {\n      \"REQUIRED\": \"Required field is missing\",\n      \"INVALID_FORMAT\": \"Field has invalid format\",\n      \"OUT_OF_RANGE\": \"Value is out of allowed range\",\n      \"INVALID_ENUM\": \"Value is not in allowed set\"\n    }\n  },\n  \"AUTHENTICATION_ERROR\": {\n    \"status\": 401,\n    \"description\": \"Authentication failed\",\n    \"subcodes\": {\n      \"MISSING_TOKEN\": \"No authentication token provided\",\n      \"INVALID_TOKEN\": \"Token is invalid\",\n      \"EXPIRED_TOKEN\": \"Token has expired\"\n    }\n  },\n  \"AUTHORIZATION_ERROR\": {\n    \"status\": 403,\n    \"description\": \"Insufficient permissions\",\n    \"subcodes\": {\n      \"INSUFFICIENT_PERMISSIONS\": \"Missing required permission\",\n      \"RESOURCE_FORBIDDEN\": \"Access to resource is forbidden\"\n    }\n  },\n  \"RESOURCE_NOT_FOUND\": {\n    \"status\": 404,\n    \"description\": \"Resource not found\"\n  },\n  \"CONFLICT_ERROR\": {\n    \"status\": 409,\n    \"description\": \"Request conflicts with current state\",\n    \"subcodes\": {\n      \"RESOURCE_ALREADY_EXISTS\": \"Resource already exists\",\n      \"CONCURRENT_MODIFICATION\": \"Resource was modified by another request\"\n    }\n  },\n  \"RATE_LIMIT_EXCEEDED\": {\n    \"status\": 429,\n    \"description\": \"Rate limit exceeded\"\n  },\n  \"INTERNAL_SERVER_ERROR\": {\n    \"status\": 500,\n    \"description\": \"Internal server error\"\n  }\n}\n```\n\n## Validation Error Details\n\n### Field-Level Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"credit_card.number\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Credit card number must be 16 digits\",\n        \"value_provided\": \"1234\",\n        \"constraints\": {\n          \"pattern\": \"^[0-9]{16}$\"\n        }\n      },\n      {\n        \"field\": \"items[0].quantity\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Quantity must be at least 1\",\n        \"value_provided\": 0,\n        \"constraints\": {\n          \"min\": 1,\n          \"max\": 1000\n        }\n      }\n    ]\n  }\n}\n```\n\n### Cross-Field Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"fields\": [\"start_date\", \"end_date\"],\n        \"code\": \"INVALID_RANGE\",\n        \"message\": \"End date must be after start date\",\n        \"values_provided\": {\n          \"start_date\": \"2024-01-20\",\n          \"end_date\": \"2024-01-15\"\n        }\n      }\n    ]\n  }\n}\n```\n\n## Request ID Tracking\n\nAlways include request ID for debugging:\n\n```http\nResponse Headers:\nX-Request-ID: req_abc123\n\nResponse Body:\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred\",\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\nClients can reference request ID in support tickets.\n\n## Error Documentation\n\nDocument all possible errors for each endpoint:\n\n```yaml\n/users/{id}:\n  get:\n    responses:\n      '200':\n        description: Success\n      '401':\n        description: Authentication failed\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              missing_token:\n                value:\n                  error:\n                    code: MISSING_TOKEN\n                    message: No authentication token provided\n              invalid_token:\n                value:\n                  error:\n                    code: INVALID_TOKEN\n                    message: Token is invalid or expired\n      '404':\n        description: User not found\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              not_found:\n                value:\n                  error:\n                    code: RESOURCE_NOT_FOUND\n                    message: User with ID 123 not found\n```\n\n## Retry Guidance\n\nHelp clients understand if they should retry:\n\n```json\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service temporarily unavailable\",\n    \"retry\": {\n      \"retryable\": true,\n      \"retry_after\": 60,\n      \"max_retries\": 3,\n      \"backoff\": \"exponential\"\n    }\n  }\n}\n```\n\n### Retryable Errors\n\n- 408 Request Timeout\n- 429 Too Many Requests (with Retry-After)\n- 500 Internal Server Error (sometimes)\n- 502 Bad Gateway\n- 503 Service Unavailable\n- 504 Gateway Timeout\n\n### Non-Retryable Errors\n\n- 400 Bad Request\n- 401 Unauthorized\n- 403 Forbidden\n- 404 Not Found\n- 409 Conflict\n- 422 Unprocessable Entity\n\n## Multi-Language Support\n\nSupport error messages in multiple languages:\n\n```http\nGET /users/invalid\nAccept-Language: es\n\nResponse: 404 Not Found\nContent-Language: es\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"Usuario con ID 'invalid' no encontrado\"\n  }\n}\n```\n\nAlways include `code` so clients can implement their own translations.\n\n## Best Practices\n\n1. **Use standard HTTP status codes** - Don't return 200 for errors\n2. **Include machine-readable codes** - Error codes for client logic\n3. **Provide human-readable messages** - Clear explanations\n4. **Be specific but safe** - Don't expose sensitive information\n5. **Include request ID** - For tracking and debugging\n6. **Document all errors** - Every possible error for each endpoint\n7. **Be consistent** - Same format across all endpoints\n8. **Help clients retry** - Indicate if error is retryable\n9. **Validate early** - Return validation errors immediately\n10. **Log errors server-side** - Track errors for monitoring\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Generic error messages** - \"Error occurred\" without details\n- **Exposing stack traces** - Security risk\n- **Inconsistent error format** - Different structure per endpoint\n- **Missing error codes** - Only human-readable messages\n- **Wrong status codes** - Returning 200 with error in body\n- **No request ID** - Makes debugging impossible\n- **Undocumented errors** - Clients don't know what to expect\n- **Too much information** - Exposing internal implementation\n","isInternal":false,"tokens":2934,"sizeBytes":11592},{"name":"openapi.md","path":"skills/api-designer/references/openapi.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/api-designer/references/openapi.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# OpenAPI 3.1 Specification\n\n## What is OpenAPI?\n\nOpenAPI (formerly Swagger) is a standard for describing REST APIs. It enables:\n- Interactive documentation\n- Code generation (SDKs, clients, servers)\n- API testing tools\n- Contract validation\n- Mock servers\n\n## Basic Structure\n\n### Minimal OpenAPI 3.1 Spec\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 1.0.0\n  description: A sample API\n  contact:\n    name: API Support\n    email: support@example.com\n    url: https://example.com/support\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n\nservers:\n  - url: https://api.example.com/v1\n    description: Production server\n  - url: https://staging-api.example.com/v1\n    description: Staging server\n  - url: http://localhost:3000/v1\n    description: Local development\n\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users\n      operationId: listUsers\n      tags:\n        - Users\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  data:\n                    type: array\n                    items:\n                      $ref: '#/components/schemas/User'\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n      properties:\n        id:\n          type: integer\n          format: int64\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          example: John Doe\n```\n\n## Info Object\n\nMetadata about the API:\n\n```yaml\ninfo:\n  title: Users API\n  version: 1.0.0\n  description: |\n    # Users API\n\n    This API manages user accounts and profiles.\n\n    ## Features\n    - User CRUD operations\n    - Authentication with JWT\n    - Role-based authorization\n\n  termsOfService: https://example.com/terms\n\n  contact:\n    name: API Support Team\n    email: api-support@example.com\n    url: https://example.com/support\n\n  license:\n    name: MIT\n    url: https://opensource.org/licenses/MIT\n\n  x-api-id: users-api-v1\n  x-audience: external\n```\n\n## Servers\n\nDefine API base URLs:\n\n```yaml\nservers:\n  - url: https://api.example.com/v1\n    description: Production\n    variables:\n      version:\n        default: v1\n        enum:\n          - v1\n          - v2\n\n  - url: https://{environment}.example.com/v1\n    description: Dynamic environment\n    variables:\n      environment:\n        default: api\n        enum:\n          - api\n          - staging\n          - dev\n```\n\n## Paths and Operations\n\n### Complete Endpoint Example\n\n```yaml\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users with optional filtering\n      operationId: listUsers\n      tags:\n        - Users\n\n      parameters:\n        - name: offset\n          in: query\n          description: Number of items to skip\n          required: false\n          schema:\n            type: integer\n            minimum: 0\n            default: 0\n\n        - name: limit\n          in: query\n          description: Maximum number of items to return\n          required: false\n          schema:\n            type: integer\n            minimum: 1\n            maximum: 100\n            default: 20\n\n        - name: status\n          in: query\n          description: Filter by user status\n          required: false\n          schema:\n            type: string\n            enum:\n              - active\n              - inactive\n              - suspended\n\n      security:\n        - bearerAuth: []\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/UserListResponse'\n              examples:\n                success:\n                  $ref: '#/components/examples/UserListSuccess'\n\n        '401':\n          $ref: '#/components/responses/Unauthorized'\n\n        '429':\n          $ref: '#/components/responses/RateLimitExceeded'\n\n    post:\n      summary: Create user\n      description: Create a new user account\n      operationId: createUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/CreateUserRequest'\n            examples:\n              basic:\n                $ref: '#/components/examples/CreateUserBasic'\n\n      responses:\n        '201':\n          description: User created successfully\n          headers:\n            Location:\n              description: URL of the created user\n              schema:\n                type: string\n                format: uri\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '400':\n          $ref: '#/components/responses/ValidationError'\n\n        '409':\n          description: User already exists\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Error'\n\n  /users/{userId}:\n    parameters:\n      - name: userId\n        in: path\n        description: User ID\n        required: true\n        schema:\n          type: integer\n          format: int64\n\n    get:\n      summary: Get user\n      description: Retrieve a specific user by ID\n      operationId: getUser\n      tags:\n        - Users\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    put:\n      summary: Update user\n      description: Replace user data\n      operationId: updateUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/UpdateUserRequest'\n\n      responses:\n        '200':\n          description: User updated successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    delete:\n      summary: Delete user\n      description: Delete a user account\n      operationId: deleteUser\n      tags:\n        - Users\n\n      responses:\n        '204':\n          description: User deleted successfully\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n```\n\n## Components\n\nReusable components for your API spec.\n\n### Schemas\n\n```yaml\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n        - name\n      properties:\n        id:\n          type: integer\n          format: int64\n          readOnly: true\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n          example: John Doe\n        status:\n          type: string\n          enum:\n            - active\n            - inactive\n            - suspended\n          default: active\n        created_at:\n          type: string\n          format: date-time\n          readOnly: true\n          example: \"2024-01-15T10:30:00Z\"\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    CreateUserRequest:\n      type: object\n      required:\n        - email\n        - name\n      properties:\n        email:\n          type: string\n          format: email\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    UserListResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/User'\n        pagination:\n          $ref: '#/components/schemas/Pagination'\n\n    Pagination:\n      type: object\n      properties:\n        offset:\n          type: integer\n          minimum: 0\n        limit:\n          type: integer\n          minimum: 1\n        total:\n          type: integer\n          minimum: 0\n        has_more:\n          type: boolean\n\n    Error:\n      type: object\n      required:\n        - error\n      properties:\n        error:\n          type: object\n          required:\n            - code\n            - message\n          properties:\n            code:\n              type: string\n              example: RESOURCE_NOT_FOUND\n            message:\n              type: string\n              example: User with ID 123 not found\n            details:\n              type: object\n            request_id:\n              type: string\n              example: req_abc123\n```\n\n### Security Schemes\n\n```yaml\ncomponents:\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: JWT access token\n\n    apiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: API key for authentication\n\n    oauth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://auth.example.com/oauth/authorize\n          tokenUrl: https://auth.example.com/oauth/token\n          scopes:\n            users:read: Read user data\n            users:write: Create and update users\n            users:delete: Delete users\n```\n\nApply security globally or per-operation:\n\n```yaml\n# Global security\nsecurity:\n  - bearerAuth: []\n\n# Or per-operation\npaths:\n  /users:\n    get:\n      security:\n        - bearerAuth: []\n        - apiKey: []  # Alternative auth method\n```\n\n### Responses\n\nReusable response definitions:\n\n```yaml\ncomponents:\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: RESOURCE_NOT_FOUND\n              message: The requested resource was not found\n\n    Unauthorized:\n      description: Authentication required\n      headers:\n        WWW-Authenticate:\n          schema:\n            type: string\n          description: Authentication method\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    ValidationError:\n      description: Validation failed\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: VALIDATION_ERROR\n              message: Request validation failed\n              details:\n                - field: email\n                  code: INVALID_FORMAT\n                  message: Email must be a valid email address\n\n    RateLimitExceeded:\n      description: Rate limit exceeded\n      headers:\n        X-RateLimit-Limit:\n          schema:\n            type: integer\n          description: Request limit per hour\n        X-RateLimit-Remaining:\n          schema:\n            type: integer\n          description: Remaining requests\n        X-RateLimit-Reset:\n          schema:\n            type: integer\n            format: int64\n          description: Time when limit resets (Unix timestamp)\n        Retry-After:\n          schema:\n            type: integer\n          description: Seconds to wait before retry\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n```\n\n### Examples\n\n```yaml\ncomponents:\n  examples:\n    UserListSuccess:\n      summary: Successful user list response\n      value:\n        data:\n          - id: 1\n            email: john@example.com\n            name: John Doe\n            status: active\n            created_at: \"2024-01-15T10:30:00Z\"\n          - id: 2\n            email: jane@example.com\n            name: Jane Smith\n            status: active\n            created_at: \"2024-01-16T14:20:00Z\"\n        pagination:\n          offset: 0\n          limit: 20\n          total: 150\n          has_more: true\n\n    CreateUserBasic:\n      summary: Create user with minimal fields\n      value:\n        email: newuser@example.com\n        name: New User\n```\n\n## Data Types\n\n### Primitive Types\n\n```yaml\n# String\ntype: string\nexample: \"Hello World\"\n\n# String with format\ntype: string\nformat: email\nexample: \"user@example.com\"\n\n# Integer\ntype: integer\nformat: int64\nexample: 123\n\n# Number (float)\ntype: number\nformat: double\nexample: 99.99\n\n# Boolean\ntype: boolean\nexample: true\n\n# Date-time\ntype: string\nformat: date-time\nexample: \"2024-01-15T10:30:00Z\"\n\n# Date\ntype: string\nformat: date\nexample: \"2024-01-15\"\n\n# UUID\ntype: string\nformat: uuid\nexample: \"550e8400-e29b-41d4-a716-446655440000\"\n\n# URI\ntype: string\nformat: uri\nexample: \"https://example.com/users/123\"\n```\n\n### Arrays\n\n```yaml\ntype: array\nitems:\n  type: string\nminItems: 1\nmaxItems: 10\nuniqueItems: true\nexample: [\"tag1\", \"tag2\", \"tag3\"]\n\n# Array of objects\ntype: array\nitems:\n  $ref: '#/components/schemas/User'\n```\n\n### Objects\n\n```yaml\ntype: object\nrequired:\n  - name\n  - email\nproperties:\n  name:\n    type: string\n  email:\n    type: string\n    format: email\n  age:\n    type: integer\n    minimum: 0\n    maximum: 120\n\n# Additional properties\nadditionalProperties: false  # Strict - no extra properties\nadditionalProperties: true   # Allow any extra properties\nadditionalProperties:        # Extra properties must be strings\n  type: string\n```\n\n### Enums\n\n```yaml\ntype: string\nenum:\n  - active\n  - inactive\n  - suspended\ndefault: active\n```\n\n### OneOf / AnyOf / AllOf\n\n```yaml\n# OneOf - exactly one schema matches\noneOf:\n  - $ref: '#/components/schemas/CreditCard'\n  - $ref: '#/components/schemas/BankAccount'\n\n# AnyOf - one or more schemas match\nanyOf:\n  - $ref: '#/components/schemas/User'\n  - $ref: '#/components/schemas/Organization'\n\n# AllOf - all schemas must match (inheritance)\nallOf:\n  - $ref: '#/components/schemas/BaseUser'\n  - type: object\n    properties:\n      admin_level:\n        type: integer\n```\n\n## Validation\n\n### String Validation\n\n```yaml\ntype: string\nminLength: 1\nmaxLength: 100\npattern: \"^[a-zA-Z0-9_-]+$\"\nformat: email\n```\n\n### Number Validation\n\n```yaml\ntype: integer\nminimum: 0\nmaximum: 100\nexclusiveMinimum: true  # > 0 instead of >= 0\nmultipleOf: 5\n```\n\n### Array Validation\n\n```yaml\ntype: array\nminItems: 1\nmaxItems: 10\nuniqueItems: true\n```\n\n## Tags\n\nOrganize endpoints into logical groups:\n\n```yaml\ntags:\n  - name: Users\n    description: User management operations\n  - name: Orders\n    description: Order management\n  - name: Products\n    description: Product catalog\n\npaths:\n  /users:\n    get:\n      tags:\n        - Users\n```\n\n## Documentation\n\n### Markdown Support\n\n```yaml\ndescription: |\n  # User Management\n\n  This endpoint allows you to manage users.\n\n  ## Features\n  - Create users\n  - Update profiles\n  - Delete accounts\n\n  ## Authentication\n  Requires JWT bearer token.\n\n  ## Example\n  ```json\n  {\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\"\n  }\n  ```\n```\n\n## Code Generation\n\nGenerate SDKs from OpenAPI spec:\n\n```bash\n# Generate TypeScript client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g typescript-axios \\\n  -o ./client\n\n# Generate Python client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g python \\\n  -o ./python-client\n\n# Generate server stub\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g nodejs-express-server \\\n  -o ./server\n```\n\n## Validation Tools\n\nValidate OpenAPI spec:\n\n```bash\n# Using Swagger CLI\nswagger-cli validate openapi.yaml\n\n# Using Spectral (advanced linting)\nspectral lint openapi.yaml\n```\n\n## Best Practices\n\n1. **Use components** - Reuse schemas, responses, parameters\n2. **Add examples** - Include realistic examples for all schemas\n3. **Document thoroughly** - Every endpoint, parameter, response\n4. **Version your spec** - Track changes to the specification\n5. **Validate regularly** - Use tools to catch errors\n6. **Use $ref** - Reference components instead of duplicating\n7. **Include error responses** - Document all possible errors\n8. **Add operationId** - Unique ID for each operation (for code gen)\n9. **Tag endpoints** - Organize into logical groups\n10. **Provide security schemes** - Document authentication clearly\n","isInternal":false,"tokens":3772,"sizeBytes":16380},{"name":"pagination.md","path":"skills/api-designer/references/pagination.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/api-designer/references/pagination.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Pagination Patterns\n\n## Why Paginate?\n\nLarge collections can't be returned all at once due to:\n- Performance (slow queries, large payloads)\n- Memory constraints (server and client)\n- Network timeouts\n- Poor user experience\n\nAlways paginate collection endpoints.\n\n## Pagination Strategies\n\n### 1. Offset-Based Pagination\n\nMost common and intuitive. Uses `offset` (skip) and `limit` (page size).\n\n**Request:**\n```http\nGET /users?offset=20&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"offset\": 20,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=10&limit=10\",\n    \"next\": \"/users?offset=30&limit=10\",\n    \"last\": \"/users?offset=140&limit=10\"\n  }\n}\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to understand\n- Random access (jump to any page)\n- Shows total count\n\n**Disadvantages:**\n- Performance degrades with large offsets (database scans many rows)\n- Inconsistent results if data changes during pagination\n- Inefficient for real-time data\n- Database must count total rows (expensive)\n\n**Use when:**\n- Small to medium datasets\n- Data doesn't change frequently\n- Need random page access\n- Need total count\n\n### 2. Page-Based Pagination\n\nSimplified offset pagination using page numbers.\n\n**Request:**\n```http\nGET /users?page=3&per_page=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"page\": 3,\n    \"per_page\": 10,\n    \"total_pages\": 15,\n    \"total_count\": 150\n  },\n  \"links\": {\n    \"first\": \"/users?page=1&per_page=10\",\n    \"prev\": \"/users?page=2&per_page=10\",\n    \"next\": \"/users?page=4&per_page=10\",\n    \"last\": \"/users?page=15&per_page=10\"\n  }\n}\n```\n\n**Calculation:**\n- `offset = (page - 1) * per_page`\n- `total_pages = ceil(total_count / per_page)`\n\n**Same pros/cons as offset-based, but:**\n- More intuitive for users (page 1, page 2)\n- Common in web applications\n\n### 3. Cursor-Based Pagination\n\nUses an opaque cursor (pointer) to the next set of results.\n\n**Request:**\n```http\nGET /users?limit=10\nGET /users?cursor=<cursor>&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"next_cursor\": \"<next-cursor>\",\n    \"prev_cursor\": \"<prev-cursor>\",\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?cursor=<next-cursor>&limit=10\",\n    \"prev\": \"/users?cursor=<prev-cursor>&limit=10\"\n  }\n}\n```\n\n**Cursor structure (base64 encoded):**\n```json\n{\"id\": 30, \"sort\": \"created_at\"}\n```\n\n**Implementation:**\n```sql\n-- First page\nSELECT * FROM users ORDER BY created_at DESC LIMIT 10;\n\n-- Next page (cursor points to last item)\nSELECT * FROM users\nWHERE created_at < '2024-01-15T10:30:00Z'\nORDER BY created_at DESC\nLIMIT 10;\n```\n\n**Advantages:**\n- Consistent results (no skipped/duplicate items)\n- Efficient for large datasets\n- Works well with real-time data\n- No expensive COUNT query\n- Better database performance\n\n**Disadvantages:**\n- No random access (can't jump to page 10)\n- No total count\n- More complex to implement\n- Cursor is opaque (users can't modify it)\n\n**Use when:**\n- Large datasets\n- Data changes frequently\n- Infinite scroll UI\n- Real-time feeds\n- Performance is critical\n\n### 4. Keyset Pagination\n\nSimilar to cursor but uses actual field values instead of opaque cursor.\n\n**Request:**\n```http\nGET /users?after_id=20&limit=10\nGET /users?after_created_at=2024-01-15T10:30:00Z&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\", \"created_at\": \"2024-01-15T11:00:00Z\"},\n    {\"id\": 22, \"name\": \"User 22\", \"created_at\": \"2024-01-15T11:30:00Z\"}\n  ],\n  \"pagination\": {\n    \"after_id\": 30,\n    \"limit\": 10,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?after_id=30&limit=10\"\n  }\n}\n```\n\n**Implementation:**\n```sql\nSELECT * FROM users\nWHERE id > 20\nORDER BY id ASC\nLIMIT 10;\n```\n\n**Advantages:**\n- Very efficient (uses index)\n- Transparent cursor (human readable)\n- Consistent results\n- Simple implementation\n\n**Disadvantages:**\n- Requires indexed column\n- No random access\n- Sorting limited to cursor field\n- Complex for multi-field sorting\n\n**Use when:**\n- Simple ordering (by ID, timestamp)\n- Need efficient pagination\n- Want transparent cursor\n- Have proper indexes\n\n### 5. Seek Pagination (Time-Based)\n\nSpecialized keyset pagination for time-series data.\n\n**Request:**\n```http\nGET /events?since=2024-01-15T10:00:00Z&until=2024-01-15T11:00:00Z&limit=100\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"since\": \"2024-01-15T10:00:00Z\",\n    \"until\": \"2024-01-15T11:00:00Z\",\n    \"limit\": 100,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/events?since=2024-01-15T11:00:00Z&until=2024-01-15T12:00:00Z&limit=100\"\n  }\n}\n```\n\n**Use for:**\n- Time-series data\n- Logs and events\n- Activity streams\n- Analytics data\n\n## Default Limits\n\nAlways set reasonable defaults and maximum limits:\n\n```json\n{\n  \"default_limit\": 20,\n  \"max_limit\": 100,\n  \"min_limit\": 1\n}\n```\n\n**Validation:**\n```http\nGET /users?limit=1000\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"INVALID_LIMIT\",\n    \"message\": \"Limit must be between 1 and 100. Default is 20.\"\n  }\n}\n```\n\n## Response Format\n\n### Standard Pagination Object\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"limit\": 10,\n    \"offset\": 20,\n    \"total\": 150,\n    \"has_more\": true,\n    \"has_previous\": true\n  }\n}\n```\n\n### Link Header (RFC 5988)\n\n```http\nLink: </users?offset=0&limit=10>; rel=\"first\",\n      </users?offset=10&limit=10>; rel=\"prev\",\n      </users?offset=30&limit=10>; rel=\"next\",\n      </users?offset=140&limit=10>; rel=\"last\"\n```\n\n**Used by:** GitHub API\n\n### Embedded Links\n\n```json\n{\n  \"data\": [...],\n  \"_links\": {\n    \"self\": { \"href\": \"/users?offset=20&limit=10\" },\n    \"first\": { \"href\": \"/users?offset=0&limit=10\" },\n    \"prev\": { \"href\": \"/users?offset=10&limit=10\" },\n    \"next\": { \"href\": \"/users?offset=30&limit=10\" },\n    \"last\": { \"href\": \"/users?offset=140&limit=10\" }\n  }\n}\n```\n\n## Sorting with Pagination\n\nAlways support sorting when paginating:\n\n```http\nGET /users?sort=created_at&order=desc&limit=10\nGET /users?sort=-created_at&limit=10                    # Descending\nGET /users?sort=last_name,first_name&limit=10           # Multi-field\n```\n\n**For cursor pagination, cursor must include sort fields:**\n```json\n{\n  \"cursor\": {\n    \"id\": 123,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"sort_fields\": [\"created_at\", \"id\"]\n  }\n}\n```\n\n## Filtering with Pagination\n\nCombine filtering with pagination:\n\n```http\nGET /users?status=active&role=admin&offset=0&limit=10\n```\n\n**Important:** Apply filters before pagination:\n1. Filter records\n2. Count filtered results\n3. Apply pagination\n4. Return paginated subset\n\n## Total Count\n\n### Include Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"total\": 1523,\n    \"limit\": 10,\n    \"offset\": 20\n  }\n}\n```\n\n**Pros:**\n- Clients know total results\n- Can calculate total pages\n- Better UX (show \"Page 3 of 153\")\n\n**Cons:**\n- COUNT query is expensive\n- Slows down response\n- Inaccurate for large/changing datasets\n\n### Omit Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"has_more\": true,\n    \"limit\": 10\n  }\n}\n```\n\n**Use when:**\n- Large datasets (COUNT is too slow)\n- Real-time data (count changes constantly)\n- Cursor pagination\n- Infinite scroll UI\n\n### Optional Total Count\n\nLet client request total count:\n\n```http\nGET /users?limit=10&include_total=true\n```\n\n## Edge Cases\n\n### Empty Results\n\n```json\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 0,\n    \"limit\": 10,\n    \"total\": 0,\n    \"has_more\": false\n  }\n}\n```\n\n### Last Page\n\n```json\n{\n  \"data\": [{\"id\": 150, \"name\": \"Last User\"}],\n  \"pagination\": {\n    \"offset\": 140,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=130&limit=10\",\n    \"next\": null\n  }\n}\n```\n\n### Out of Range\n\n```http\nGET /users?offset=10000&limit=10\n\nResponse: 200 OK (empty results)\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 10000,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  }\n}\n```\n\nOr return 404 for pages that don't exist:\n```http\nGET /users?page=1000&per_page=10\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"PAGE_NOT_FOUND\",\n    \"message\": \"Page 1000 does not exist. Total pages: 15\"\n  }\n}\n```\n\n## Best Practices\n\n1. **Always paginate collections** - Never return unbounded lists\n2. **Set reasonable defaults** - Default limit of 20-50 items\n3. **Enforce maximum limits** - Prevent excessive loads (max 100-1000)\n4. **Include has_more flag** - Tell clients if more results exist\n5. **Provide navigation links** - Make it easy to get next/prev pages\n6. **Document pagination** - Explain cursor format, limits, defaults\n7. **Be consistent** - Use same pagination pattern across all endpoints\n8. **Consider performance** - Choose strategy based on data size/type\n9. **Support sorting** - Let clients control result order\n10. **Handle edge cases** - Empty results, last page, invalid cursors\n\n## Comparison Matrix\n\n| Feature | Offset | Page | Cursor | Keyset |\n|---------|--------|------|--------|--------|\n| Performance | Poor for large offsets | Poor | Excellent | Excellent |\n| Random access | Yes | Yes | No | No |\n| Total count | Yes | Yes | No | Optional |\n| Consistency | Poor | Poor | Excellent | Excellent |\n| Complexity | Simple | Simple | Medium | Medium |\n| Real-time data | Poor | Poor | Excellent | Excellent |\n| Database load | High | High | Low | Low |\n| Use case | Small datasets | Web UIs | Feeds/streams | Large datasets |\n","isInternal":false,"tokens":2899,"sizeBytes":9632},{"name":"rest-patterns.md","path":"skills/api-designer/references/rest-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/api-designer/references/rest-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# REST Design Patterns\n\n## Resource-Oriented Architecture\n\nREST APIs are built around resources, not actions. Resources are the nouns of your API.\n\n### Resource Identification\n\n**Good Resource URIs:**\n```\nGET    /users                  # Collection\nGET    /users/{id}             # Individual resource\nGET    /users/{id}/orders      # Nested collection\nPOST   /users                  # Create resource\nPUT    /users/{id}             # Replace resource\nPATCH  /users/{id}             # Update resource\nDELETE /users/{id}             # Delete resource\n```\n\n**Bad Resource URIs:**\n```\nPOST   /getUser                # Verb in URI\nPOST   /createUser             # Verb in URI\nGET    /user?action=delete     # Action as query param\n```\n\n### Resource Naming Conventions\n\n- Use plural nouns for collections: `/users`, `/orders`, `/products`\n- Use lowercase and hyphens for readability: `/shipping-addresses`\n- Avoid deep nesting (max 2-3 levels): `/users/{id}/orders/{orderId}`\n- Use query parameters for filtering: `/users?status=active&role=admin`\n\n## HTTP Method Semantics\n\n### Safe and Idempotent Methods\n\n| Method | Safe | Idempotent | Use Case |\n|--------|------|------------|----------|\n| GET | Yes | Yes | Retrieve resource(s) |\n| POST | No | No | Create resource, non-idempotent operations |\n| PUT | No | Yes | Replace entire resource |\n| PATCH | No | No | Partial update |\n| DELETE | No | Yes | Remove resource |\n| HEAD | Yes | Yes | Get metadata only |\n| OPTIONS | Yes | Yes | Get allowed methods |\n\n### Method Usage\n\n**GET - Retrieve Resources**\n```http\nGET /users/123\nAccept: application/json\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"created_at\": \"2024-01-15T10:30:00Z\"\n}\n```\n\n**POST - Create Resources**\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\"\n}\n\nResponse: 201 Created\nLocation: /users/124\n{\n  \"id\": 124,\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\",\n  \"created_at\": \"2024-01-16T14:20:00Z\"\n}\n```\n\n**PUT - Replace Resource**\n```http\nPUT /users/123\nContent-Type: application/json\n\n{\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\",\n  \"updated_at\": \"2024-01-17T09:15:00Z\"\n}\n```\n\n**PATCH - Partial Update**\n```http\nPATCH /users/123\nContent-Type: application/json\n\n{\n  \"email\": \"john.updated@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john.updated@example.com\",\n  \"updated_at\": \"2024-01-17T10:00:00Z\"\n}\n```\n\n**DELETE - Remove Resource**\n```http\nDELETE /users/123\n\nResponse: 204 No Content\n```\n\n## HTTP Status Codes\n\n### Success Codes (2xx)\n\n- **200 OK** - Request succeeded (GET, PUT, PATCH)\n- **201 Created** - Resource created (POST), include Location header\n- **202 Accepted** - Request accepted for async processing\n- **204 No Content** - Success with no response body (DELETE)\n\n### Redirection (3xx)\n\n- **301 Moved Permanently** - Resource permanently moved\n- **302 Found** - Temporary redirect\n- **304 Not Modified** - Cached version is still valid\n\n### Client Errors (4xx)\n\n- **400 Bad Request** - Invalid request syntax or validation error\n- **401 Unauthorized** - Authentication required or failed\n- **403 Forbidden** - Authenticated but not authorized\n- **404 Not Found** - Resource doesn't exist\n- **405 Method Not Allowed** - HTTP method not supported for resource\n- **409 Conflict** - Request conflicts with current state (e.g., duplicate)\n- **422 Unprocessable Entity** - Valid syntax but semantic errors\n- **429 Too Many Requests** - Rate limit exceeded\n\n### Server Errors (5xx)\n\n- **500 Internal Server Error** - Unexpected server error\n- **502 Bad Gateway** - Invalid response from upstream server\n- **503 Service Unavailable** - Server temporarily unavailable\n- **504 Gateway Timeout** - Upstream server timeout\n\n## HATEOAS (Hypermedia)\n\n### Hypermedia-Driven APIs\n\nInclude links to related resources and available actions:\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" },\n    \"orders\": { \"href\": \"/users/123/orders\" },\n    \"update\": { \"href\": \"/users/123\", \"method\": \"PATCH\" },\n    \"delete\": { \"href\": \"/users/123\", \"method\": \"DELETE\" }\n  }\n}\n```\n\n### HAL (Hypertext Application Language)\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" }\n  },\n  \"_embedded\": {\n    \"orders\": [\n      {\n        \"id\": 456,\n        \"total\": 99.99,\n        \"_links\": {\n          \"self\": { \"href\": \"/orders/456\" }\n        }\n      }\n    ]\n  }\n}\n```\n\n## Content Negotiation\n\n### Accept Headers\n\n```http\nGET /users/123\nAccept: application/json\n\nGET /users/123\nAccept: application/xml\n\nGET /users/123\nAccept: application/hal+json\n```\n\n### Response Content-Type\n\n```http\nContent-Type: application/json; charset=utf-8\nContent-Type: application/problem+json\nContent-Type: application/hal+json\n```\n\n## Idempotency\n\n### Idempotent Operations\n\n**PUT - Always idempotent:**\nMultiple identical PUT requests produce the same result as a single request.\n\n**DELETE - Idempotent:**\nFirst DELETE returns 204, subsequent DELETEs return 404 (same end state).\n\n**POST - Not idempotent by default:**\nUse `Idempotency-Key` header for idempotent POST:\n\n```http\nPOST /payments\nIdempotency-Key: 550e8400-e29b-41d4-a716-446655440000\nContent-Type: application/json\n\n{\n  \"amount\": 100.00,\n  \"currency\": \"USD\"\n}\n```\n\nServer stores idempotency key and returns same response for duplicate requests.\n\n## Cache Control\n\n### Cache Headers\n\n```http\nCache-Control: public, max-age=3600\nCache-Control: private, no-cache\nCache-Control: no-store\nETag: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nLast-Modified: Wed, 15 Jan 2024 10:30:00 GMT\n```\n\n### Conditional Requests\n\n```http\nGET /users/123\nIf-None-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\n\nResponse: 304 Not Modified\n```\n\n```http\nPUT /users/123\nIf-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nContent-Type: application/json\n\n{\n  \"name\": \"Updated Name\"\n}\n\nResponse: 412 Precondition Failed (if ETag doesn't match)\n```\n\n## URI Patterns\n\n### Consistent URI Structure\n\n```\n/{version}/{resource}\n/{version}/{resource}/{id}\n/{version}/{resource}/{id}/{sub-resource}\n/{version}/{resource}/{id}/{sub-resource}/{sub-id}\n```\n\n### Query Parameters\n\n**Filtering:**\n```\nGET /users?status=active&role=admin\nGET /products?category=electronics&price_min=100&price_max=500\n```\n\n**Sorting:**\n```\nGET /users?sort=created_at\nGET /users?sort=-created_at          # Descending\nGET /users?sort=name,created_at      # Multiple fields\n```\n\n**Field Selection:**\n```\nGET /users?fields=id,name,email\nGET /users?exclude=password,social_security_number\n```\n\n**Search:**\n```\nGET /users?q=john\nGET /products?search=laptop\n```\n\n## Best Practices\n\n1. **Use nouns, not verbs** - Resources are nouns, methods are verbs\n2. **Plural collections** - Use `/users` not `/user`\n3. **Consistent naming** - Choose snake_case or camelCase and stick to it\n4. **Proper status codes** - Use appropriate HTTP status codes\n5. **Include metadata** - Pagination, filtering, sorting info in responses\n6. **Version your API** - Plan for evolution from day one\n7. **Document everything** - OpenAPI specs, examples, error codes\n8. **Security by default** - HTTPS, authentication, rate limiting\n9. **Support filtering** - Enable clients to get exactly what they need\n10. **Implement HATEOAS** - Make APIs self-documenting and discoverable\n","isInternal":false,"tokens":2081,"sizeBytes":7516},{"name":"versioning.md","path":"skills/api-designer/references/versioning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/api-designer/references/versioning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# API Versioning Strategies\n\n## Why Version APIs?\n\nAPI versioning allows you to evolve your API while maintaining backward compatibility for existing clients. Breaking changes require a new version.\n\n### Breaking Changes\n\nChanges that require a new version:\n- Removing or renaming fields\n- Changing field types (string to integer)\n- Adding required fields to requests\n- Changing response structure\n- Removing endpoints\n- Changing HTTP status codes for same scenario\n- Changing authentication mechanisms\n\n### Non-Breaking Changes\n\nSafe changes that don't require a new version:\n- Adding new endpoints\n- Adding optional request fields\n- Adding new fields to responses (clients should ignore unknown fields)\n- Fixing bugs\n- Performance improvements\n- Adding new HTTP methods to existing resources\n\n## Versioning Strategies\n\n### 1. URI Versioning\n\nMost common and visible approach. Version is part of the URL path.\n\n```http\nGET /v1/users/123\nGET /v2/users/123\n```\n\n**Advantages:**\n- Clear and visible in URLs\n- Easy to understand and implement\n- Simple routing and caching\n- Can run multiple versions simultaneously\n\n**Disadvantages:**\n- Violates REST principle (same resource, different URIs)\n- Requires updating client code to change version\n- Can lead to URI proliferation\n\n**Implementation:**\n```\n/v1/users\n/v1/products\n/v2/users      # New version with breaking changes\n/v2/products\n```\n\n### 2. Header Versioning\n\nVersion specified in HTTP headers (Accept header or custom header).\n\n**Accept Header:**\n```http\nGET /users/123\nAccept: application/vnd.myapi.v1+json\n\nGET /users/123\nAccept: application/vnd.myapi.v2+json\n```\n\n**Custom Header:**\n```http\nGET /users/123\nAPI-Version: 1\n\nGET /users/123\nAPI-Version: 2\n```\n\n**Advantages:**\n- URIs remain stable\n- More RESTful (same resource, same URI)\n- Separates versioning from resource identification\n\n**Disadvantages:**\n- Less visible (harder to debug)\n- More complex routing\n- Difficult to test in browser\n- Cache complexity\n\n### 3. Query Parameter Versioning\n\nVersion specified as query parameter.\n\n```http\nGET /users/123?version=1\nGET /users/123?version=2\n\n# or\nGET /users/123?api-version=1\nGET /users/123?api-version=2\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to test\n- Visible in URLs\n\n**Disadvantages:**\n- Pollutes query string\n- Not semantic (version not a filter)\n- Can interfere with other query params\n\n### 4. Content Negotiation\n\nClient specifies desired version through content negotiation.\n\n```http\nGET /users/123\nAccept: application/vnd.myapi+json; version=1\n\nGET /users/123\nAccept: application/vnd.myapi+json; version=2\n```\n\n**Advantages:**\n- Very RESTful\n- Flexible content type negotiation\n- Stable URIs\n\n**Disadvantages:**\n- Complex implementation\n- Less intuitive for developers\n- Harder to test\n\n## Recommended Approach\n\n**URI versioning is recommended for most APIs** because:\n- It's the most explicit and discoverable\n- Easy to understand and debug\n- Simple to implement and maintain\n- Clear separation between versions\n\n```\n/v1/users\n/v2/users\n/v3/users\n```\n\n## Version Format\n\n### Major Versions Only\n\nUse simple major versions (v1, v2, v3) for public APIs:\n```\n/v1/users\n/v2/users\n```\n\n**Advantages:**\n- Simple and clear\n- Easy to communicate\n- Forces thoughtful breaking changes\n\n### Date-Based Versions\n\nSome APIs use dates for versions:\n```\n/2024-01-01/users\n/2024-06-15/users\n```\n\n**Used by:** Stripe, GitHub API\n\n**Advantages:**\n- Clear when version was released\n- Easy to understand timeline\n- No confusion about major/minor\n\n**Disadvantages:**\n- Less intuitive for clients\n- Harder to understand what changed\n\n## Version Lifecycle\n\n### 1. Introduction Phase\n\nNew version is released alongside existing version:\n```\n/v1/users  # Still supported\n/v2/users  # New version available\n```\n\nAnnounce new version:\n- Blog post explaining changes\n- Migration guide\n- Breaking changes list\n- Timeline for v1 deprecation\n\n### 2. Deprecation Phase\n\nMark old version as deprecated but keep it running:\n\n```http\nGET /v1/users/123\n\nResponse:\nDeprecation: true\nSunset: Wed, 15 Jan 2025 00:00:00 GMT\nLink: </v2/users/123>; rel=\"successor-version\"\n\n{\n  \"id\": 123,\n  \"name\": \"John Doe\"\n}\n```\n\n**Deprecation Headers:**\n- `Deprecation: true` - Indicates version is deprecated\n- `Sunset: <date>` - When version will be removed (RFC 8594)\n- `Link: <url>; rel=\"successor-version\"` - Points to new version\n\n### 3. Sunset Phase\n\nOld version is shut down on announced date.\n\nReturn 410 Gone for deprecated endpoints:\n```http\nGET /v1/users/123\n\nResponse: 410 Gone\n{\n  \"error\": {\n    \"code\": \"VERSION_SUNSET\",\n    \"message\": \"API v1 was sunset on 2025-01-15. Please use v2.\",\n    \"documentation_url\": \"https://api.example.com/docs/migration-v1-to-v2\"\n  }\n}\n```\n\n## Deprecation Policy\n\n### Recommended Timeline\n\n1. **Announce deprecation** - At least 6 months before sunset\n2. **Support period** - Run both versions for 6-12 months\n3. **Sunset date** - Clear date communicated in advance\n4. **Grace period** - 30 days of 410 Gone responses before complete shutdown\n\n### Communication Channels\n\n- API response headers\n- Email to registered developers\n- Blog posts and changelog\n- Dashboard notifications\n- Documentation updates\n- Status page announcements\n\n## Migration Strategy\n\n### Provide Migration Guide\n\n```markdown\n# Migrating from v1 to v2\n\n## Breaking Changes\n\n### User Resource Changes\n\n**v1:**\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**v2:**\n```json\n{\n  \"id\": 123,\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**Migration:**\n- Split `name` field into `first_name` and `last_name`\n- Update client code to use new fields\n```\n\n### Offer Tools\n\n- Migration scripts\n- SDK updates\n- API diff viewer\n- Compatibility layer (temporary)\n\n## Version Discovery\n\n### Root Endpoint\n\n```http\nGET /\n\nResponse:\n{\n  \"versions\": {\n    \"v1\": {\n      \"status\": \"deprecated\",\n      \"sunset_date\": \"2025-01-15\",\n      \"documentation_url\": \"https://api.example.com/docs/v1\"\n    },\n    \"v2\": {\n      \"status\": \"current\",\n      \"documentation_url\": \"https://api.example.com/docs/v2\"\n    },\n    \"v3\": {\n      \"status\": \"beta\",\n      \"documentation_url\": \"https://api.example.com/docs/v3\"\n    }\n  }\n}\n```\n\n### Version Info Endpoint\n\n```http\nGET /v2/version\n\nResponse:\n{\n  \"version\": \"v2\",\n  \"released\": \"2024-01-15\",\n  \"status\": \"stable\",\n  \"sunset_date\": null\n}\n```\n\n## OpenAPI Versioning\n\n### Separate Specs per Version\n\n```\nopenapi-v1.yaml\nopenapi-v2.yaml\nopenapi-v3.yaml\n```\n\nEach spec is complete and independent.\n\n### Single Spec with Servers\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 2.0.0\nservers:\n  - url: https://api.example.com/v1\n    description: Version 1 (deprecated)\n  - url: https://api.example.com/v2\n    description: Version 2 (current)\n```\n\n## Best Practices\n\n1. **Version from day one** - Start with /v1, not /api\n2. **Major versions only** - Use v1, v2, v3 (not v1.1, v1.2)\n3. **Long deprecation periods** - Give clients time to migrate (6-12 months)\n4. **Clear communication** - Use headers, docs, emails\n5. **Maintain old versions** - Support at least 2 versions simultaneously\n6. **Document changes** - Provide detailed migration guides\n7. **Use semantic versioning** - For internal/SDK versioning\n8. **Never break without warning** - Always announce breaking changes\n9. **Provide tools** - Migration scripts, updated SDKs\n10. **Monitor usage** - Track which versions are being used\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Breaking changes without version bump** - Breaks existing clients\n- **Too many versions** - Maintenance nightmare (max 2-3 active versions)\n- **Short deprecation periods** - Frustrates developers\n- **No migration path** - Makes upgrades painful\n- **Surprise sunsets** - Breaks production apps without warning\n- **Inconsistent versioning** - Different strategies for different endpoints\n- **Versioning individual endpoints** - Use consistent version across API\n","isInternal":false,"tokens":2080,"sizeBytes":7945},{"name":"SKILL.md","path":"skills/api-designer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/api-designer/SKILL.md","title":"api-designer","category":"anthropic-skill","format":"markdown","content":"---\nname: api-designer\ndescription: Use when designing REST or GraphQL APIs, creating OpenAPI specifications, or planning API architecture. Invoke for resource modeling, versioning strategies, pagination patterns, error handling standards.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: api-architecture\n  triggers: API design, REST API, OpenAPI, API specification, API architecture, resource modeling, API versioning, GraphQL schema, API documentation\n  role: architect\n  scope: design\n  output-format: specification\n  related-skills: graphql-architect, fastapi-expert, nestjs-expert, spring-boot-engineer, security-reviewer\n---\n\n# API Designer\n\nSenior API architect specializing in REST and GraphQL APIs with comprehensive OpenAPI 3.1 specifications.\n\n## Core Workflow\n\n1. **Analyze domain** — Understand business requirements, data models, and client needs\n2. **Model resources** — Identify resources, relationships, and operations; sketch entity diagram before writing any spec\n3. **Design endpoints** — Define URI patterns, HTTP methods, request/response schemas\n4. **Specify contract** — Create OpenAPI 3.1 spec; validate before proceeding: `npx @redocly/cli lint openapi.yaml`\n5. **Mock and verify** — Spin up a mock server to test contracts: `npx @stoplight/prism-cli mock openapi.yaml`\n6. **Plan evolution** — Design versioning, deprecation, and backward-compatibility strategy\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| REST Patterns | `references/rest-patterns.md` | Resource design, HTTP methods, HATEOAS |\n| Versioning | `references/versioning.md` | API versions, deprecation, breaking changes |\n| Pagination | `references/pagination.md` | Cursor, offset, keyset pagination |\n| Error Handling | `references/error-handling.md` | Error responses, RFC 7807, status codes |\n| OpenAPI | `references/openapi.md` | OpenAPI 3.1, documentation, code generation |\n\n## Constraints\n\n### MUST DO\n- Follow REST principles (resource-oriented, proper HTTP methods)\n- Use consistent naming conventions (snake_case or camelCase — pick one, apply everywhere)\n- Include comprehensive OpenAPI 3.1 specification\n- Design proper error responses with actionable messages (RFC 7807)\n- Implement pagination for all collection endpoints\n- Version APIs with clear deprecation policies\n- Document authentication and authorization\n- Provide request/response examples\n\n### MUST NOT DO\n- Use verbs in resource URIs (use `/users/{id}`, not `/getUser/{id}`)\n- Return inconsistent response structures\n- Skip error code documentation\n- Ignore HTTP status code semantics\n- Design APIs without a versioning strategy\n- Expose implementation details in the API surface\n- Create breaking changes without a migration path\n- Omit rate limiting considerations\n\n## Templates\n\n### OpenAPI 3.1 Resource Endpoint (copy-paste starter)\n\n```yaml\nopenapi: \"3.1.0\"\ninfo:\n  title: Example API\n  version: \"1.1.0\"\npaths:\n  /users:\n    get:\n      summary: List users\n      operationId: listUsers\n      tags: [Users]\n      parameters:\n        - name: cursor\n          in: query\n          schema: { type: string }\n          description: Opaque cursor for pagination\n        - name: limit\n          in: query\n          schema: { type: integer, default: 20, maximum: 100 }\n      responses:\n        \"200\":\n          description: Paginated list of users\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [data, pagination]\n                properties:\n                  data:\n                    type: array\n                    items: { $ref: \"#/components/schemas/User\" }\n                  pagination:\n                    $ref: \"#/components/schemas/CursorPage\"\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"429\": { $ref: \"#/components/responses/TooManyRequests\" }\n  /users/{id}:\n    get:\n      summary: Get a user\n      operationId: getUser\n      tags: [Users]\n      parameters:\n        - name: id\n          in: path\n          required: true\n          schema: { type: string, format: uuid }\n      responses:\n        \"200\":\n          description: User found\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/User\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required: [id, email, created_at]\n      properties:\n        id:    { type: string, format: uuid, readOnly: true }\n        email: { type: string, format: email }\n        name:  { type: string }\n        created_at: { type: string, format: date-time, readOnly: true }\n\n    CursorPage:\n      type: object\n      required: [next_cursor, has_more]\n      properties:\n        next_cursor: { type: string, nullable: true }\n        has_more:    { type: boolean }\n\n    Problem:                       # RFC 7807 Problem Details\n      type: object\n      required: [type, title, status]\n      properties:\n        type:     { type: string, format: uri, example: \"https://api.example.com/errors/validation-error\" }\n        title:    { type: string, example: \"Validation Error\" }\n        status:   { type: integer, example: 400 }\n        detail:   { type: string, example: \"The 'email' field must be a valid email address.\" }\n        instance: { type: string, format: uri, example: \"/users/req-abc123\" }\n\n  responses:\n    BadRequest:\n      description: Invalid request parameters\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    Unauthorized:\n      description: Missing or invalid authentication\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    NotFound:\n      description: Resource not found\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    TooManyRequests:\n      description: Rate limit exceeded\n      headers:\n        Retry-After: { schema: { type: integer } }\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\nsecurity:\n  - BearerAuth: []\n```\n\n### RFC 7807 Error Response (copy-paste)\n\n```json\n{\n  \"type\": \"https://api.example.com/errors/validation-error\",\n  \"title\": \"Validation Error\",\n  \"status\": 422,\n  \"detail\": \"The 'email' field must be a valid email address.\",\n  \"instance\": \"/users/req-abc123\",\n  \"errors\": [\n    { \"field\": \"email\", \"message\": \"Must be a valid email address.\" }\n  ]\n}\n```\n\n- Always use `Content-Type: application/problem+json` for error responses.\n- `type` must be a stable, documented URI — never a generic string.\n- `detail` must be human-readable and actionable.\n- Extend with `errors[]` for field-level validation failures.\n\n## Output Checklist\n\nWhen delivering an API design, provide:\n1. Resource model and relationships (diagram or table)\n2. Endpoint specifications with URIs and HTTP methods\n3. OpenAPI 3.1 specification (YAML)\n4. Authentication and authorization flows\n5. Error response catalog (all 4xx/5xx with `type` URIs)\n6. Pagination and filtering patterns\n7. Versioning and deprecation strategy\n8. Validation result: `npx @redocly/cli lint openapi.yaml` passes with no errors\n\n## Knowledge Reference\n\nREST architecture, OpenAPI 3.1, GraphQL, HTTP semantics, JSON:API, HATEOAS, OAuth 2.0, JWT, RFC 7807 Problem Details, API versioning patterns, pagination strategies, rate limiting, webhook design, SDK generation\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/api-designer/)\n","frontmatter":{"name":"api-designer","description":"Use when designing REST or GraphQL APIs, creating OpenAPI specifications, or planning API architecture. Invoke for resource modeling, versioning strategies, pagination patterns, error handling standards.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"api-architecture","triggers":"API design, REST API, OpenAPI, API specification, API architecture, resource modeling, API versioning, GraphQL schema, API documentation","role":"architect","scope":"design","output-format":"specification","related-skills":"graphql-architect, fastapi-expert, nestjs-expert, spring-boot-engineer, security-reviewer"}},"isInternal":false,"tokens":1882,"sizeBytes":8008},{"name":"adr-template.md","path":"skills/architecture-designer/references/adr-template.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/architecture-designer/references/adr-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# ADR Template\n\n## ADR Format\n\n```markdown\n# ADR-{number}: {Title}\n\n## Status\n[Proposed | Accepted | Deprecated | Superseded by ADR-XXX]\n\n## Context\n[Describe the situation and forces at play. What is the problem?\nWhat constraints exist? What are we trying to achieve?]\n\n## Decision\n[State the decision clearly. What are we going to do?]\n\n## Consequences\n\n### Positive\n- [Benefit 1]\n- [Benefit 2]\n\n### Negative\n- [Drawback 1]\n- [Drawback 2]\n\n### Neutral\n- [Side effect that is neither good nor bad]\n\n## Alternatives Considered\n[What other options were evaluated and why were they rejected?]\n\n## References\n- [Link to relevant documentation]\n- [Link to discussion/RFC]\n```\n\n## Example: Database Selection\n\n```markdown\n# ADR-001: Use PostgreSQL for primary database\n\n## Status\nAccepted\n\n## Context\nWe need a relational database for our e-commerce platform that:\n- Handles complex transactions with strong consistency\n- Supports JSON for flexible product attributes\n- Scales to millions of products and orders\n- Works well with our existing Python/Node stack\n\nTeam has experience with PostgreSQL and MySQL.\nBudget allows for managed database service.\n\n## Decision\nUse PostgreSQL as the primary database, hosted on AWS RDS.\n\n## Consequences\n\n### Positive\n- ACID compliance for financial transactions\n- Rich feature set (JSON, full-text search, CTEs)\n- Strong community and tooling\n- Excellent performance with proper indexing\n- Free and open source\n\n### Negative\n- Vertical scaling has limits (addressed with read replicas)\n- Requires DBA expertise for optimization\n- AWS RDS costs for high availability\n\n### Neutral\n- Team will need to learn PostgreSQL-specific features\n- Migration from current SQLite dev database needed\n\n## Alternatives Considered\n\n**MySQL**\n- Rejected: Less feature-rich for JSON operations\n- Considered: Similar cost, familiar to team\n\n**MongoDB**\n- Rejected: Relational data model needed for orders/inventory\n- Considered: Great for product catalog flexibility\n\n**CockroachDB**\n- Rejected: Higher cost, team unfamiliar\n- Considered: Better horizontal scaling\n\n## References\n- https://www.postgresql.org/docs/current/\n- Internal RFC: Database Selection for E-commerce Platform\n```\n\n## ADR Naming Convention\n\n```\ndocs/\n└── adr/\n    ├── 0001-use-postgresql-database.md\n    ├── 0002-adopt-microservices.md\n    ├── 0003-implement-event-sourcing.md\n    └── README.md\n```\n\n## Quick Reference\n\n| Section | Purpose | Key Question |\n|---------|---------|--------------|\n| Status | Current state | Is this active? |\n| Context | Background | Why are we deciding? |\n| Decision | The choice | What did we choose? |\n| Consequences | Impact | What happens now? |\n| Alternatives | Options | What else was considered? |\n","isInternal":false,"tokens":637,"sizeBytes":2752},{"name":"architecture-patterns.md","path":"skills/architecture-designer/references/architecture-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/architecture-designer/references/architecture-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Architecture Patterns\n\n## Pattern Comparison\n\n| Pattern | Best For | Team Size | Trade-offs |\n|---------|----------|-----------|------------|\n| **Monolith** | Simple domain, small team | 1-10 | Simple deploy; hard to scale parts |\n| **Modular Monolith** | Growing complexity | 5-20 | Module boundaries; still single deploy |\n| **Microservices** | Complex domain, large org | 20+ | Independent scale; operational complexity |\n| **Serverless** | Variable load, event-driven | Any | Auto-scale; cold starts, vendor lock |\n| **Event-Driven** | Async processing | 10+ | Loose coupling; debugging complexity |\n\n## Monolith\n\n```\n┌─────────────────────────────────────┐\n│            Application              │\n│  ┌─────┐  ┌─────┐  ┌─────┐         │\n│  │Users│  │Orders│ │Products│       │\n│  └─────┘  └─────┘  └─────┘         │\n│  └──────────┬──────────────┘        │\n│          Database                    │\n└─────────────────────────────────────┘\n```\n\n**When to Use**:\n- Starting a new project\n- Small team (< 10 developers)\n- Simple domain\n- Rapid iteration needed\n\n**Pros**: Simple deployment, easy debugging, no network latency\n**Cons**: Hard to scale independently, technology locked, deployment risk\n\n## Microservices\n\n```\n┌──────────┐  ┌──────────┐  ┌──────────┐\n│  Users   │  │  Orders  │  │ Products │\n│ Service  │  │ Service  │  │ Service  │\n└────┬─────┘  └────┬─────┘  └────┬─────┘\n     │             │             │\n┌────▼────┐  ┌────▼────┐  ┌────▼────┐\n│ User DB │  │Order DB │  │ Prod DB │\n└─────────┘  └─────────┘  └─────────┘\n```\n\n**When to Use**:\n- Large team (20+ developers)\n- Complex domain with clear boundaries\n- Different scaling requirements per service\n- Polyglot technology needs\n\n**Pros**: Independent scaling, team autonomy, fault isolation\n**Cons**: Distributed system complexity, eventual consistency, operational overhead\n\n## Event-Driven\n\n```\n┌──────────┐     ┌─────────────┐     ┌──────────┐\n│ Producer │────▶│ Message Bus │────▶│ Consumer │\n└──────────┘     │  (Kafka)    │     └──────────┘\n                 └─────────────┘\n                       │\n                       ▼\n                 ┌──────────┐\n                 │ Consumer │\n                 └──────────┘\n```\n\n**When to Use**:\n- Async processing required\n- Loose coupling between services\n- Event sourcing needs\n- High throughput messaging\n\n**Pros**: Decoupled services, scalable, audit trail\n**Cons**: Eventual consistency, debugging complexity, message ordering\n\n## CQRS (Command Query Responsibility Segregation)\n\n```\n┌─────────┐         ┌─────────────┐\n│ Commands│────────▶│ Write Model │──┐\n└─────────┘         └─────────────┘  │\n                                     ▼\n                              ┌──────────┐\n                              │  Events  │\n                              └──────────┘\n                                     │\n┌─────────┐         ┌─────────────┐  │\n│ Queries │◀────────│ Read Model  │◀─┘\n└─────────┘         └─────────────┘\n```\n\n**When to Use**:\n- Read/write ratio heavily skewed\n- Complex read queries\n- Event sourcing architecture\n- Different optimization needs\n\n## Quick Reference\n\n| Requirement | Recommended Pattern |\n|-------------|---------------------|\n| Simple CRUD app | Monolith |\n| Growing startup | Modular Monolith |\n| Enterprise scale | Microservices |\n| Variable load | Serverless |\n| Async processing | Event-Driven |\n| Read-heavy | CQRS |\n","isInternal":false,"tokens":930,"sizeBytes":4605},{"name":"database-selection.md","path":"skills/architecture-designer/references/database-selection.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/architecture-designer/references/database-selection.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Database Selection\n\n## Database Types\n\n| Type | Examples | Best For |\n|------|----------|----------|\n| **Relational** | PostgreSQL, MySQL | Transactions, complex queries, relationships |\n| **Document** | MongoDB, Firestore | Flexible schemas, rapid iteration |\n| **Key-Value** | Redis, DynamoDB | Caching, sessions, high throughput |\n| **Time-Series** | TimescaleDB, InfluxDB | Metrics, IoT, analytics |\n| **Graph** | Neo4j, Neptune | Relationships, social networks |\n| **Search** | Elasticsearch, Meilisearch | Full-text search, logs |\n\n## Relational (PostgreSQL, MySQL)\n\n```\nBest For:\n- Financial transactions (ACID compliance)\n- Complex queries with joins\n- Data integrity requirements\n- Structured, predictable schemas\n\nWhen to Avoid:\n- Highly variable schemas\n- Massive horizontal scaling needs\n- Simple key-value access patterns\n```\n\n| Feature | PostgreSQL | MySQL |\n|---------|------------|-------|\n| JSON support | Excellent (JSONB) | Good (JSON) |\n| Full-text search | Built-in | Basic |\n| Extensions | Rich ecosystem | Limited |\n| Replication | Streaming, logical | Statement, row-based |\n\n## Document (MongoDB, Firestore)\n\n```\nBest For:\n- Flexible, evolving schemas\n- Hierarchical data (nested documents)\n- Rapid prototyping\n- Content management\n\nWhen to Avoid:\n- Complex transactions across documents\n- Heavy relational queries\n- Strict schema requirements\n```\n\n## Key-Value (Redis, DynamoDB)\n\n```\nBest For:\n- Session storage\n- Caching layer\n- Real-time leaderboards\n- Rate limiting counters\n\nWhen to Avoid:\n- Complex queries\n- Relational data\n- Large value sizes (>1MB)\n```\n\n## Time-Series (TimescaleDB, InfluxDB)\n\n```\nBest For:\n- Metrics and monitoring\n- IoT sensor data\n- Financial tick data\n- Event logging with timestamps\n\nWhen to Avoid:\n- Frequent updates to existing records\n- Complex relational queries\n- Non-time-based access patterns\n```\n\n## Decision Matrix\n\n| Requirement | Recommended |\n|-------------|-------------|\n| ACID transactions | PostgreSQL, MySQL |\n| Flexible schema | MongoDB, Firestore |\n| High-speed caching | Redis |\n| Time-series data | TimescaleDB, InfluxDB |\n| Social relationships | Neo4j |\n| Full-text search | Elasticsearch |\n| Serverless scale | DynamoDB, Firestore |\n\n## Quick Reference\n\n| Question | If Yes → |\n|----------|----------|\n| Need ACID transactions? | Relational (PostgreSQL) |\n| Schema changes frequently? | Document (MongoDB) |\n| Sub-millisecond reads? | Key-Value (Redis) |\n| Time-based queries? | Time-Series |\n| Traversing relationships? | Graph (Neo4j) |\n| Full-text search primary? | Elasticsearch |\n","isInternal":false,"tokens":597,"sizeBytes":2569},{"name":"nfr-checklist.md","path":"skills/architecture-designer/references/nfr-checklist.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/architecture-designer/references/nfr-checklist.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Non-Functional Requirements Checklist\n\n## NFR Categories\n\n### Scalability\n\n| Question | Common Targets |\n|----------|----------------|\n| Expected concurrent users? | 100 / 1K / 10K / 100K |\n| Requests per second? | 10 / 100 / 1000 / 10000 |\n| Data volume? | GB / TB / PB |\n| Growth rate? | 10% / 50% / 100% per year |\n| Peak vs average load? | 2x / 5x / 10x |\n\n### Performance\n\n| Question | Common Targets |\n|----------|----------------|\n| API response time? | < 100ms / 200ms / 500ms p95 |\n| Page load time? | < 1s / 2s / 3s |\n| Database query time? | < 10ms / 50ms / 100ms |\n| Batch processing throughput? | 1K / 10K / 100K records/hour |\n\n### Availability\n\n| Target | Downtime/Year | Use Case |\n|--------|---------------|----------|\n| 99% | 3.65 days | Internal tools |\n| 99.9% | 8.76 hours | Business apps |\n| 99.95% | 4.38 hours | E-commerce |\n| 99.99% | 52.6 minutes | Financial systems |\n| 99.999% | 5.26 minutes | Life-critical |\n\n### Security\n\n| Question | Considerations |\n|----------|----------------|\n| Authentication required? | JWT, OAuth, SAML, MFA |\n| Authorization model? | RBAC, ABAC, ACL |\n| Data sensitivity? | Public, internal, confidential, PII |\n| Compliance requirements? | GDPR, HIPAA, PCI DSS, SOC 2 |\n| Encryption needs? | At rest, in transit, end-to-end |\n\n### Reliability\n\n| Question | Considerations |\n|----------|----------------|\n| Acceptable data loss? | RPO: 0 / 1hr / 24hr |\n| Recovery time target? | RTO: 1hr / 4hr / 24hr |\n| Backup frequency? | Real-time / hourly / daily |\n| Disaster recovery? | Single region / multi-region |\n\n### Maintainability\n\n| Question | Considerations |\n|----------|----------------|\n| Deployment frequency? | Daily / weekly / monthly |\n| Deployment strategy? | Blue-green, canary, rolling |\n| Monitoring requirements? | Logs, metrics, traces, alerts |\n| On-call requirements? | 24/7, business hours |\n\n### Cost\n\n| Question | Considerations |\n|----------|----------------|\n| Infrastructure budget? | $/month, $/user, $/request |\n| Operational budget? | FTE for maintenance |\n| Cost optimization? | Reserved instances, spot instances |\n| Cost alerts? | Thresholds for notification |\n\n## Template\n\n```markdown\n## Non-Functional Requirements\n\n### Performance\n- API response time: < 200ms p95\n- Page load time: < 2s\n- Database query time: < 50ms\n\n### Scalability\n- Concurrent users: 10,000\n- Requests per second: 1,000\n- Data volume: 1TB\n\n### Availability\n- Target: 99.9% (8.76 hours/year downtime)\n- RPO: 1 hour\n- RTO: 4 hours\n\n### Security\n- Authentication: JWT with refresh tokens\n- Authorization: Role-based (admin, user, guest)\n- Compliance: GDPR, SOC 2\n\n### Observability\n- Logging: Structured JSON to ELK\n- Metrics: Prometheus + Grafana\n- Tracing: OpenTelemetry\n- Alerts: PagerDuty integration\n```\n\n## Quick Reference\n\n| Category | Key Metric |\n|----------|------------|\n| Performance | Response time (p95) |\n| Scalability | Concurrent users, RPS |\n| Availability | Uptime percentage |\n| Reliability | RPO, RTO |\n| Security | Compliance requirements |\n| Cost | $/month budget |\n","isInternal":false,"tokens":860,"sizeBytes":3046},{"name":"system-design.md","path":"skills/architecture-designer/references/system-design.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/architecture-designer/references/system-design.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# System Design Template\n\n## Design Template\n\n```markdown\n# System: {System Name}\n\n## Requirements\n\n### Functional\n- [What the system must do]\n- [Core features and capabilities]\n\n### Non-Functional\n- **Performance**: Response time < 200ms p95\n- **Availability**: 99.9% uptime (8.76 hours downtime/year)\n- **Scalability**: Support 10,000 concurrent users\n- **Security**: PCI DSS compliance required\n\n### Constraints\n- Budget: $X/month for infrastructure\n- Timeline: MVP in 3 months\n- Team: 5 backend, 3 frontend engineers\n\n## High-Level Architecture\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│   Client    │────▶│  API Gateway │────▶│  Service    │\n│   (Web)     │     │   (Kong)    │     │  (Node.js)  │\n└─────────────┘     └─────────────┘     └─────────────┘\n                           │                   │\n                           ▼                   ▼\n                    ┌─────────────┐     ┌─────────────┐\n                    │    Auth     │     │  Database   │\n                    │  (Auth0)    │     │ (PostgreSQL)│\n                    └─────────────┘     └─────────────┘\n```\n\n## Component Details\n\n### API Layer\n- Technology: Node.js with Express/NestJS\n- Responsibilities: Request routing, validation, auth\n- Scaling: Horizontal via load balancer\n\n### Data Layer\n- Primary: PostgreSQL (transactions, relationships)\n- Cache: Redis (sessions, hot data)\n- Storage: S3 (files, images)\n\n### External Services\n- Auth: Auth0 (SSO, MFA)\n- Email: SendGrid (transactional)\n- Monitoring: Datadog (APM, logs)\n\n## Key Decisions\n\n| Decision | Rationale |\n|----------|-----------|\n| PostgreSQL over MongoDB | Relational data, ACID needed |\n| Redis for caching | Sub-ms latency required |\n| Auth0 over custom | Reduce security risk |\n\n## Scaling Strategy\n\n### Current (MVP)\n- Single region deployment\n- 2 API instances behind ALB\n- Single RDS instance\n\n### Future (10x growth)\n- Multi-region with CDN\n- Auto-scaling API (2-10 instances)\n- RDS read replicas\n\n## Security Considerations\n- All traffic over TLS 1.3\n- JWT tokens with 15-min expiry\n- Rate limiting: 100 req/min per user\n- WAF for common attacks\n\n## Failure Modes\n\n| Failure | Impact | Mitigation |\n|---------|--------|------------|\n| DB down | Full outage | Multi-AZ failover |\n| Cache down | Degraded perf | Fallback to DB |\n| Auth down | No new logins | Cache valid tokens |\n```\n\n## Quick Reference\n\n| Section | Key Questions |\n|---------|---------------|\n| Requirements | What must it do? How well? |\n| Architecture | What components? How connected? |\n| Decisions | Why these choices? |\n| Scaling | How to grow? |\n| Failures | What can break? How to recover? |\n","isInternal":false,"tokens":701,"sizeBytes":3009},{"name":"SKILL.md","path":"skills/architecture-designer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/architecture-designer/SKILL.md","title":"architecture-designer","category":"anthropic-skill","format":"markdown","content":"---\nname: architecture-designer\ndescription: Use when designing new high-level system architecture, reviewing existing designs, or making architectural decisions. Invoke to create architecture diagrams, write Architecture Decision Records (ADRs), evaluate technology trade-offs, design component interactions, and plan for scalability. Use for system design, architecture review, microservices structuring, ADR authoring, scalability planning, and infrastructure pattern selection — distinct from code-level design patterns or database-only design tasks.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.1\"\n  domain: api-architecture\n  triggers: architecture, system design, design pattern, microservices, scalability, ADR, technical design, infrastructure\n  role: expert\n  scope: design\n  output-format: document\n  related-skills: fullstack-guardian, devops-engineer, secure-code-guardian, microservices-architect, code-reviewer\n---\n\n# Architecture Designer\n\nSenior software architect specializing in system design, design patterns, and architectural decision-making.\n\n## Role Definition\n\nYou are a principal architect with 15+ years of experience designing scalable, distributed systems. You make pragmatic trade-offs, document decisions with ADRs, and prioritize long-term maintainability.\n\n## When to Use This Skill\n\n- Designing new system architecture\n- Choosing between architectural patterns\n- Reviewing existing architecture\n- Creating Architecture Decision Records (ADRs)\n- Planning for scalability\n- Evaluating technology choices\n\n## Core Workflow\n\n1. **Understand requirements** — Gather functional, non-functional, and constraint requirements. _Verify full requirements coverage before proceeding._\n2. **Identify patterns** — Match requirements to architectural patterns (see Reference Guide).\n3. **Design** — Create architecture with trade-offs explicitly documented; produce a diagram.\n4. **Document** — Write ADRs for all key decisions.\n5. **Review** — Validate with stakeholders. _If review fails, return to step 3 with recorded feedback._\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Architecture Patterns | `references/architecture-patterns.md` | Choosing monolith vs microservices |\n| ADR Template | `references/adr-template.md` | Documenting decisions |\n| System Design | `references/system-design.md` | Full system design template |\n| Database Selection | `references/database-selection.md` | Choosing database technology |\n| NFR Checklist | `references/nfr-checklist.md` | Gathering non-functional requirements |\n\n## Constraints\n\n### MUST DO\n- Document all significant decisions with ADRs\n- Consider non-functional requirements explicitly\n- Evaluate trade-offs, not just benefits\n- Plan for failure modes\n- Consider operational complexity\n- Review with stakeholders before finalizing\n\n### MUST NOT DO\n- Over-engineer for hypothetical scale\n- Choose technology without evaluating alternatives\n- Ignore operational costs\n- Design without understanding requirements\n- Skip security considerations\n\n## Output Templates\n\nWhen designing architecture, provide:\n1. Requirements summary (functional + non-functional)\n2. High-level architecture diagram (Mermaid preferred — see example below)\n3. Key decisions with trade-offs (ADR format — see example below)\n4. Technology recommendations with rationale\n5. Risks and mitigation strategies\n\n### Architecture Diagram (Mermaid)\n\n```mermaid\ngraph TD\n    Client[\"Client (Web/Mobile)\"] --> Gateway[\"API Gateway\"]\n    Gateway --> AuthSvc[\"Auth Service\"]\n    Gateway --> OrderSvc[\"Order Service\"]\n    OrderSvc --> DB[(\"Orders DB\\n(PostgreSQL)\")]\n    OrderSvc --> Queue[\"Message Queue\\n(RabbitMQ)\"]\n    Queue --> NotifySvc[\"Notification Service\"]\n```\n\n### ADR Example\n\n```markdown\n# ADR-001: Use PostgreSQL for Order Storage\n\n## Status\nAccepted\n\n## Context\nThe Order Service requires ACID-compliant transactions and complex relational queries\nacross orders, line items, and customers.\n\n## Decision\nUse PostgreSQL as the primary datastore for the Order Service.\n\n## Alternatives Considered\n- **MongoDB** — flexible schema, but lacks strong ACID guarantees across documents.\n- **DynamoDB** — excellent scalability, but complex query patterns require denormalization.\n\n## Consequences\n- Positive: Strong consistency, mature tooling, complex query support.\n- Negative: Vertical scaling limits; horizontal sharding adds operational complexity.\n\n## Trade-offs\nConsistency and query flexibility are prioritised over unlimited horizontal write scalability.\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/architecture-designer/)\n","frontmatter":{"name":"architecture-designer","description":"Use when designing new high-level system architecture, reviewing existing designs, or making architectural decisions. Invoke to create architecture diagrams, write Architecture Decision Records (ADRs), evaluate technology trade-offs, design component interactions, and plan for scalability. Use for system design, architecture review, microservices structuring, ADR authoring, scalability planning, and infrastructure pattern selection — distinct from code-level design patterns or database-only design tasks.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"api-architecture","triggers":"architecture, system design, design pattern, microservices, scalability, ADR, technical design, infrastructure","role":"expert","scope":"design","output-format":"document","related-skills":"fullstack-guardian, devops-engineer, secure-code-guardian, microservices-architect, code-reviewer"}},"isInternal":false,"tokens":999,"sizeBytes":4926},{"name":"authentication-patterns.md","path":"skills/atlassian-mcp/references/authentication-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/atlassian-mcp/references/authentication-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Authentication Patterns\n\n---\n\n## Authentication Methods Overview\n\n| Method | Platform | Use Case | Security Level |\n|--------|----------|----------|----------------|\n| OAuth 2.1 | Cloud | User-facing apps, integrations | Highest |\n| API Token | Cloud | Personal automation, scripts | Medium |\n| PAT | Server/DC | Server integrations | Medium |\n| Basic Auth | Legacy | Deprecated, avoid | Low |\n\n## OAuth 2.1 (Atlassian Cloud)\n\n### Authorization Code Flow\n\nFor applications that act on behalf of users.\n\n**Step 1: Register Your App**\n\n1. Go to [developer.atlassian.com](https://developer.atlassian.com/console/myapps/)\n2. Create new app\n3. Configure OAuth 2.0 (3LO)\n4. Add callback URL\n5. Request necessary scopes\n\n**Step 2: Authorization Request**\n\n```typescript\nconst authUrl = new URL('https://auth.atlassian.com/authorize');\nauthUrl.searchParams.set('audience', 'api.atlassian.com');\nauthUrl.searchParams.set('client_id', CLIENT_ID);\nauthUrl.searchParams.set('scope', 'read:jira-work write:jira-work read:confluence-content.all write:confluence-content');\nauthUrl.searchParams.set('redirect_uri', REDIRECT_URI);\nauthUrl.searchParams.set('state', generateState());\nauthUrl.searchParams.set('response_type', 'code');\nauthUrl.searchParams.set('prompt', 'consent');\n\n// Redirect user to authUrl.toString()\n```\n\n**Step 3: Exchange Code for Token**\n\n```typescript\nasync function exchangeCodeForToken(code: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'authorization_code',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      code,\n      redirect_uri: REDIRECT_URI,\n    }),\n  });\n\n  if (!response.ok) {\n    throw new Error(`Token exchange failed: ${response.statusText}`);\n  }\n\n  return response.json();\n}\n\ninterface TokenResponse {\n  access_token: string;\n  refresh_token: string;\n  expires_in: number;\n  scope: string;\n  token_type: 'Bearer';\n}\n```\n\n**Step 4: Refresh Token**\n\n```typescript\nasync function refreshAccessToken(refreshToken: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'refresh_token',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      refresh_token: refreshToken,\n    }),\n  });\n\n  return response.json();\n}\n```\n\n**Step 5: Get Accessible Resources**\n\n```typescript\nasync function getAccessibleResources(accessToken: string): Promise<Resource[]> {\n  const response = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    {\n      headers: {\n        Authorization: `Bearer ${accessToken}`,\n        Accept: 'application/json',\n      },\n    }\n  );\n\n  return response.json();\n}\n\ninterface Resource {\n  id: string;        // Cloud ID\n  name: string;      // Site name\n  url: string;       // https://your-site.atlassian.net\n  scopes: string[];\n  avatarUrl: string;\n}\n```\n\n### OAuth Scopes Reference\n\n**Jira Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:jira-work` | Read issues, projects, boards |\n| `write:jira-work` | Create/update issues |\n| `manage:jira-project` | Manage project settings |\n| `manage:jira-configuration` | Manage global settings |\n| `read:jira-user` | Read user profiles |\n| `manage:jira-data-provider` | Data provider integrations |\n\n**Confluence Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:confluence-content.all` | Read all content |\n| `write:confluence-content` | Create/update content |\n| `read:confluence-content.summary` | Read content summaries |\n| `read:confluence-space.summary` | Read space summaries |\n| `write:confluence-space` | Create/manage spaces |\n| `read:confluence-user` | Read user profiles |\n\n**Granular Scopes (v2):**\n\n```\nread:issue-details:jira\nwrite:issue:jira\nread:sprint:jira-software\nwrite:sprint:jira-software\nread:page:confluence\nwrite:page:confluence\nread:comment:confluence\nwrite:comment:confluence\n```\n\n## API Tokens (Atlassian Cloud)\n\n### Creating API Token\n\n1. Go to [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens)\n2. Click \"Create API token\"\n3. Give it a descriptive label\n4. Copy token immediately (shown only once)\n\n### Using API Token\n\n```typescript\n// Basic authentication with API token\nconst credentials = Buffer.from(`${email}:${apiToken}`).toString('base64');\n\nconst response = await fetch('https://your-site.atlassian.net/rest/api/3/myself', {\n  headers: {\n    Authorization: `Basic ${credentials}`,\n    Accept: 'application/json',\n  },\n});\n\n// For MCP server configuration\nconst config = {\n  JIRA_URL: 'https://your-site.atlassian.net',\n  JIRA_USERNAME: 'your-email@company.com',\n  JIRA_API_TOKEN: '<jira-api-token>',\n};\n```\n\n### Token Security Best Practices\n\n```typescript\n// Store tokens securely\nimport { SecretManagerServiceClient } from '@google-cloud/secret-manager';\nimport { SecretsManager } from '@aws-sdk/client-secrets-manager';\n\n// Option 1: Environment variables (development only)\nconst token = process.env.ATLASSIAN_API_TOKEN;\n\n// Option 2: GCP Secret Manager\nasync function getTokenFromGCP(secretName: string): Promise<string> {\n  const client = new SecretManagerServiceClient();\n  const [version] = await client.accessSecretVersion({\n    name: `projects/my-project/secrets/${secretName}/versions/latest`,\n  });\n  return version.payload?.data?.toString() || '';\n}\n\n// Option 3: AWS Secrets Manager\nasync function getTokenFromAWS(secretName: string): Promise<string> {\n  const client = new SecretsManager({ region: 'us-east-1' });\n  const response = await client.getSecretValue({ SecretId: secretName });\n  return response.SecretString || '';\n}\n\n// Option 4: HashiCorp Vault\nasync function getTokenFromVault(path: string): Promise<string> {\n  const response = await fetch(`${VAULT_ADDR}/v1/${path}`, {\n    headers: { 'X-Vault-Token': VAULT_TOKEN },\n  });\n  const data = await response.json();\n  return data.data.data.token;\n}\n```\n\n## Personal Access Tokens (Server/Data Center)\n\n### Creating PAT\n\n**Jira Server/DC:**\n1. Profile > Personal Access Tokens\n2. Create token\n3. Set expiration date\n4. Select permissions\n\n**Confluence Server/DC:**\n1. Profile > Settings > Personal Access Tokens\n2. Create token\n3. Configure permissions\n\n### Using PAT\n\n```typescript\n// Bearer token authentication\nconst response = await fetch('https://jira.internal.company.com/rest/api/2/myself', {\n  headers: {\n    Authorization: `Bearer ${personalAccessToken}`,\n    Accept: 'application/json',\n  },\n});\n\n// MCP server configuration\nconst config = {\n  JIRA_URL: 'https://jira.internal.company.com',\n  JIRA_PERSONAL_TOKEN: '<jira-personal-access-token>',\n};\n```\n\n### PAT Permissions\n\n| Permission | Jira | Confluence |\n|------------|------|------------|\n| Read | Browse projects, view issues | View pages |\n| Write | Create/edit issues | Create/edit pages |\n| Admin | Project administration | Space administration |\n\n## Token Management\n\n### Token Lifecycle Manager\n\n```typescript\ninterface TokenInfo {\n  accessToken: string;\n  refreshToken?: string;\n  expiresAt: Date;\n  scopes: string[];\n}\n\nclass TokenManager {\n  private tokenInfo: TokenInfo | null = null;\n  private refreshThreshold = 5 * 60 * 1000; // 5 minutes\n\n  async getValidToken(): Promise<string> {\n    if (!this.tokenInfo) {\n      throw new Error('Not authenticated');\n    }\n\n    // Check if token needs refresh\n    const timeUntilExpiry = this.tokenInfo.expiresAt.getTime() - Date.now();\n\n    if (timeUntilExpiry < this.refreshThreshold) {\n      await this.refreshToken();\n    }\n\n    return this.tokenInfo.accessToken;\n  }\n\n  private async refreshToken(): Promise<void> {\n    if (!this.tokenInfo?.refreshToken) {\n      throw new Error('No refresh token available');\n    }\n\n    const response = await refreshAccessToken(this.tokenInfo.refreshToken);\n\n    this.tokenInfo = {\n      accessToken: response.access_token,\n      refreshToken: response.refresh_token || this.tokenInfo.refreshToken,\n      expiresAt: new Date(Date.now() + response.expires_in * 1000),\n      scopes: response.scope.split(' '),\n    };\n  }\n\n  isAuthenticated(): boolean {\n    return this.tokenInfo !== null && this.tokenInfo.expiresAt > new Date();\n  }\n\n  getScopes(): string[] {\n    return this.tokenInfo?.scopes || [];\n  }\n\n  hasScope(scope: string): boolean {\n    return this.getScopes().includes(scope);\n  }\n}\n```\n\n### Token Rotation Strategy\n\n```typescript\nclass TokenRotationManager {\n  private rotationInterval = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n  async checkAndRotate(tokenCreatedAt: Date): Promise<boolean> {\n    const age = Date.now() - tokenCreatedAt.getTime();\n\n    if (age > this.rotationInterval) {\n      console.warn('API token is due for rotation');\n      return true;\n    }\n\n    return false;\n  }\n\n  async sendRotationReminder(email: string, tokenLabel: string): Promise<void> {\n    // Integrate with your notification system\n    await sendEmail({\n      to: email,\n      subject: 'Atlassian API Token Rotation Reminder',\n      body: `Your API token \"${tokenLabel}\" is due for rotation.\n             Please create a new token and update your integrations.`,\n    });\n  }\n}\n```\n\n## Permission Verification\n\n### Check Current Permissions\n\n```typescript\nasync function verifyPermissions(\n  client: MCPClient,\n  requiredOperations: string[]\n): Promise<PermissionReport> {\n  const report: PermissionReport = {\n    hasAllPermissions: true,\n    details: [],\n  };\n\n  for (const operation of requiredOperations) {\n    try {\n      switch (operation) {\n        case 'read:jira':\n          await client.callTool({\n            name: 'jira_get_issue',\n            arguments: { issue_key: 'TEST-1' },\n          });\n          break;\n        case 'write:jira':\n          // Create and immediately delete a test issue\n          const created = await client.callTool({\n            name: 'jira_create_issue',\n            arguments: {\n              project_key: 'TEST',\n              issue_type: 'Task',\n              summary: '[Permission Test] Delete me',\n            },\n          });\n          // Clean up\n          await client.callTool({\n            name: 'jira_delete_issue',\n            arguments: { issue_key: JSON.parse(created.content[0].text).key },\n          });\n          break;\n        case 'read:confluence':\n          await client.callTool({\n            name: 'confluence_search',\n            arguments: { cql: 'type = page', limit: 1 },\n          });\n          break;\n      }\n\n      report.details.push({ operation, status: 'granted' });\n    } catch (error: any) {\n      report.hasAllPermissions = false;\n      report.details.push({\n        operation,\n        status: 'denied',\n        error: error.message,\n      });\n    }\n  }\n\n  return report;\n}\n```\n\n## Security Checklist\n\n### Do:\n- Use OAuth 2.1 for user-facing applications\n- Store secrets in dedicated secrets management systems\n- Implement token rotation policies\n- Use minimal required scopes\n- Log authentication events (without secrets)\n- Implement rate limiting at application level\n- Validate tokens before use\n\n### Don't:\n- Hardcode tokens in source code\n- Log tokens or secrets\n- Share tokens between environments\n- Use Basic Auth (deprecated)\n- Request more scopes than needed\n- Store tokens in browser localStorage\n- Commit `.env` files with real credentials\n\n### Environment Configuration Template\n\n```bash\n# .env.example (commit this)\nATLASSIAN_SITE_URL=https://your-site.atlassian.net\nATLASSIAN_AUTH_TYPE=oauth  # or 'api_token' or 'pat'\n\n# OAuth settings (if using OAuth)\nATLASSIAN_CLIENT_ID=\nATLASSIAN_CLIENT_SECRET=\n\n# API Token settings (if using API token)\nATLASSIAN_USERNAME=\nATLASSIAN_API_TOKEN=\n\n# PAT settings (if using Server/DC)\nATLASSIAN_PERSONAL_TOKEN=\n```\n\n```bash\n# .gitignore\n.env\n.env.local\n.env.*.local\ncredentials.json\n**/secrets/**\n```\n\n## Troubleshooting\n\n### Common Authentication Errors\n\n**401 Unauthorized:**\n- Invalid or expired token\n- Wrong authentication method\n- Missing Authorization header\n\n**403 Forbidden:**\n- Token valid but lacks required scope\n- Resource-level permission denied\n- IP allowlist blocking request\n\n**Token refresh fails:**\n- Refresh token expired (after 90 days of inactivity)\n- Client secret changed\n- App permissions revoked\n\n### Debug Authentication\n\n```typescript\nasync function debugAuth(token: string): Promise<void> {\n  // Check token validity\n  const meResponse = await fetch(\n    'https://api.atlassian.com/me',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  console.log('Token status:', meResponse.status);\n\n  if (meResponse.ok) {\n    const me = await meResponse.json();\n    console.log('Authenticated as:', me.email);\n  }\n\n  // Check accessible resources\n  const resourcesResponse = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  if (resourcesResponse.ok) {\n    const resources = await resourcesResponse.json();\n    console.log('Accessible sites:', resources.map((r: any) => r.name));\n  }\n}\n```\n\n## Related References\n\n- `mcp-server-setup.md` - Server configuration with credentials\n- `jira-queries.md` - Operations that require authentication\n- `confluence-operations.md` - Content operations with auth\n","isInternal":false,"tokens":3115,"sizeBytes":13426},{"name":"common-workflows.md","path":"skills/atlassian-mcp/references/common-workflows.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/atlassian-mcp/references/common-workflows.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Common Workflows\n\n---\n\n## Issue Triage Workflow\n\nAutomatically categorize, prioritize, and route incoming issues.\n\n### Triage Bot Implementation\n\n```typescript\ninterface TriageResult {\n  issueKey: string;\n  category: string;\n  priority: string;\n  assignee: string | null;\n  labels: string[];\n  actions: string[];\n}\n\nasync function triageNewIssue(\n  client: MCPClient,\n  issueKey: string\n): Promise<TriageResult> {\n  // Get issue details\n  const issueResult = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: {\n      issue_key: issueKey,\n      expand: [\"changelog\"]\n    }\n  });\n\n  const issue = JSON.parse(issueResult.content[0].text);\n  const result: TriageResult = {\n    issueKey,\n    category: \"uncategorized\",\n    priority: issue.fields.priority?.name || \"Medium\",\n    assignee: null,\n    labels: [],\n    actions: []\n  };\n\n  // Categorize based on content\n  const summary = issue.fields.summary.toLowerCase();\n  const description = (issue.fields.description?.content?.[0]?.content?.[0]?.text || \"\").toLowerCase();\n  const text = `${summary} ${description}`;\n\n  // Category detection\n  if (text.match(/\\b(crash|down|outage|critical|emergency)\\b/)) {\n    result.category = \"incident\";\n    result.priority = \"Highest\";\n    result.labels.push(\"incident\", \"urgent\");\n  } else if (text.match(/\\b(bug|error|fail|broken|doesn't work)\\b/)) {\n    result.category = \"bug\";\n    result.labels.push(\"bug\", \"needs-investigation\");\n  } else if (text.match(/\\b(feature|enhancement|request|add|improve)\\b/)) {\n    result.category = \"feature\";\n    result.labels.push(\"feature-request\");\n  } else if (text.match(/\\b(docs|documentation|readme|guide)\\b/)) {\n    result.category = \"documentation\";\n    result.labels.push(\"documentation\");\n  }\n\n  // Component detection for routing\n  const componentMap: Record<string, { component: string; team: string }> = {\n    \"api|rest|endpoint\": { component: \"API\", team: \"backend-team\" },\n    \"ui|button|page|screen|css\": { component: \"Frontend\", team: \"frontend-team\" },\n    \"database|sql|query|migration\": { component: \"Database\", team: \"data-team\" },\n    \"auth|login|password|sso\": { component: \"Authentication\", team: \"security-team\" },\n    \"deploy|ci|cd|pipeline\": { component: \"DevOps\", team: \"platform-team\" }\n  };\n\n  for (const [pattern, { component, team }] of Object.entries(componentMap)) {\n    if (text.match(new RegExp(`\\\\b(${pattern})\\\\b`))) {\n      result.labels.push(component.toLowerCase());\n      result.actions.push(`Route to ${team}`);\n      break;\n    }\n  }\n\n  // Apply triage results\n  await client.callTool({\n    name: \"jira_update_issue\",\n    arguments: {\n      issue_key: issueKey,\n      fields: {\n        priority: { name: result.priority },\n        labels: [...new Set([...issue.fields.labels || [], ...result.labels])]\n      }\n    }\n  });\n\n  // Add triage comment\n  await client.callTool({\n    name: \"jira_add_comment\",\n    arguments: {\n      issue_key: issueKey,\n      body: `*Automated Triage Results*\n\nCategory: ${result.category}\nPriority: ${result.priority}\nLabels added: ${result.labels.join(\", \")}\n\n${result.actions.length > 0 ? `Recommended actions:\\n${result.actions.map(a => `- ${a}`).join(\"\\n\")}` : \"\"}`\n    }\n  });\n\n  return result;\n}\n```\n\n### Bulk Triage New Issues\n\n```typescript\nasync function triageBacklog(client: MCPClient, projectKey: string): Promise<void> {\n  // Find untriaged issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND labels IS EMPTY AND created >= -7d AND resolution IS EMPTY`,\n      max_results: 50,\n      fields: [\"summary\", \"description\", \"priority\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  console.log(`Found ${response.total} issues to triage`);\n\n  for (const issue of response.issues) {\n    const result = await triageNewIssue(client, issue.key);\n    console.log(`Triaged ${issue.key}: ${result.category} (${result.priority})`);\n\n    // Rate limiting\n    await delay(500);\n  }\n}\n```\n\n## Documentation Sync Workflow\n\nKeep Confluence documentation in sync with Jira issues.\n\n### Generate Release Notes\n\n```typescript\nasync function generateReleaseNotes(\n  client: MCPClient,\n  projectKey: string,\n  version: string,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get all issues in the release\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND fixVersion = \"${version}\" AND resolution = Done ORDER BY issuetype, priority DESC`,\n      max_results: 200,\n      fields: [\"summary\", \"issuetype\", \"priority\", \"assignee\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n\n  // Group by issue type\n  const grouped: Record<string, any[]> = {};\n  for (const issue of response.issues) {\n    const type = issue.fields.issuetype.name;\n    if (!grouped[type]) grouped[type] = [];\n    grouped[type].push(issue);\n  }\n\n  // Build Confluence page content\n  let content = `\n<h2>Release ${version}</h2>\n<p>Released on ${new Date().toISOString().split('T')[0]}</p>\n\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">2</ac:parameter>\n</ac:structured-macro>\n\n<h2>Summary</h2>\n<p>This release includes ${response.total} changes.</p>\n<table>\n  <tr><th>Type</th><th>Count</th></tr>\n  ${Object.entries(grouped).map(([type, issues]) =>\n    `<tr><td>${type}</td><td>${issues.length}</td></tr>`\n  ).join('')}\n</table>\n`;\n\n  // Add sections for each issue type\n  const typeOrder = [\"New Feature\", \"Improvement\", \"Bug\", \"Task\"];\n  const orderedTypes = [...new Set([...typeOrder, ...Object.keys(grouped)])];\n\n  for (const type of orderedTypes) {\n    if (!grouped[type]) continue;\n\n    content += `\n<h2>${type}s</h2>\n<table>\n  <tr><th>Key</th><th>Summary</th><th>Assignee</th></tr>\n  ${grouped[type].map(issue => `\n    <tr>\n      <td><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issue.key}</ac:parameter></ac:structured-macro></td>\n      <td>${escapeHtml(issue.fields.summary)}</td>\n      <td>${issue.fields.assignee?.displayName || 'Unassigned'}</td>\n    </tr>\n  `).join('')}\n</table>\n`;\n  }\n\n  // Create or update Confluence page\n  const pageTitle = `Release Notes - ${version}`;\n\n  // Check if page exists\n  const existingSearch = await client.callTool({\n    name: \"confluence_search\",\n    arguments: {\n      cql: `space = \"${confluenceSpaceKey}\" AND title = \"${pageTitle}\"`,\n      limit: 1\n    }\n  });\n\n  const existingResults = JSON.parse(existingSearch.content[0].text);\n\n  if (existingResults.results.length > 0) {\n    // Update existing page\n    const pageId = existingResults.results[0].id;\n    const currentVersion = existingResults.results[0].version.number;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: pageId,\n        title: pageTitle,\n        body: content,\n        version_number: currentVersion + 1,\n        version_message: `Updated release notes for ${version}`\n      }\n    });\n\n    return pageId;\n  } else {\n    // Create new page\n    const newPage = await client.callTool({\n      name: \"confluence_create_page\",\n      arguments: {\n        space_key: confluenceSpaceKey,\n        title: pageTitle,\n        body: content,\n        labels: [\"release-notes\", `version-${version.replace(/\\./g, '-')}`]\n      }\n    });\n\n    return JSON.parse(newPage.content[0].text).id;\n  }\n}\n```\n\n### Sync Meeting Notes to Jira\n\n```typescript\ninterface ActionItem {\n  description: string;\n  assignee: string;\n  dueDate?: string;\n}\n\nasync function syncMeetingNotes(\n  client: MCPClient,\n  confluencePageId: string,\n  jiraProjectKey: string\n): Promise<string[]> {\n  // Get meeting notes content\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: confluencePageId,\n      expand: [\"body.storage\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n  const content = page.body.storage.value;\n\n  // Extract action items (look for checkbox patterns)\n  const actionItems = extractActionItems(content);\n  const createdIssues: string[] = [];\n\n  for (const item of actionItems) {\n    // Create Jira task\n    const newIssue = await client.callTool({\n      name: \"jira_create_issue\",\n      arguments: {\n        project_key: jiraProjectKey,\n        issue_type: \"Task\",\n        summary: item.description,\n        description: {\n          type: \"doc\",\n          version: 1,\n          content: [{\n            type: \"paragraph\",\n            content: [{\n              type: \"text\",\n              text: `Action item from meeting: ${page.title}\\n\\nSource: ${page._links.webui}`\n            }]\n          }]\n        },\n        assignee: item.assignee,\n        due_date: item.dueDate,\n        labels: [\"meeting-action\", \"auto-created\"]\n      }\n    });\n\n    const issueKey = JSON.parse(newIssue.content[0].text).key;\n    createdIssues.push(issueKey);\n  }\n\n  // Update Confluence page with Jira links\n  if (createdIssues.length > 0) {\n    const jiraLinksSection = `\n<h2>Linked Jira Issues</h2>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">key IN (${createdIssues.join(',')})</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n`;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: confluencePageId,\n        title: page.title,\n        body: content + jiraLinksSection,\n        version_number: page.version.number + 1\n      }\n    });\n  }\n\n  return createdIssues;\n}\n\nfunction extractActionItems(html: string): ActionItem[] {\n  // Parse action items from common patterns\n  const items: ActionItem[] = [];\n\n  // Pattern: [x] or [ ] followed by @mention and task description\n  const taskPattern = /\\[[ x]\\]\\s*@([a-zA-Z.]+)\\s*[-:]\\s*(.+?)(?=<|$)/gi;\n  let match;\n\n  while ((match = taskPattern.exec(html)) !== null) {\n    items.push({\n      assignee: match[1],\n      description: match[2].trim()\n    });\n  }\n\n  return items;\n}\n```\n\n## Sprint Automation Workflow\n\nAutomate sprint ceremonies and reporting.\n\n### Sprint Planning Assistant\n\n```typescript\ninterface SprintPlanningReport {\n  totalPoints: number;\n  byAssignee: Record<string, number>;\n  byComponent: Record<string, number>;\n  riskyItems: string[];\n  recommendations: string[];\n}\n\nasync function analyzeSprintPlanning(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number\n): Promise<SprintPlanningReport> {\n  // Get sprint issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"assignee\", \"customfield_10001\", \"components\", \"labels\", \"priority\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  const issues = response.issues;\n\n  const report: SprintPlanningReport = {\n    totalPoints: 0,\n    byAssignee: {},\n    byComponent: {},\n    riskyItems: [],\n    recommendations: []\n  };\n\n  for (const issue of issues) {\n    const points = issue.fields.customfield_10001 || 0; // Story points\n    const assignee = issue.fields.assignee?.displayName || \"Unassigned\";\n    const components = issue.fields.components?.map((c: any) => c.name) || [\"No Component\"];\n\n    report.totalPoints += points;\n    report.byAssignee[assignee] = (report.byAssignee[assignee] || 0) + points;\n\n    for (const component of components) {\n      report.byComponent[component] = (report.byComponent[component] || 0) + points;\n    }\n\n    // Identify risks\n    if (points === 0) {\n      report.riskyItems.push(`${issue.key}: No story points estimated`);\n    }\n    if (points >= 8) {\n      report.riskyItems.push(`${issue.key}: Large story (${points} points) - consider breaking down`);\n    }\n    if (assignee === \"Unassigned\") {\n      report.riskyItems.push(`${issue.key}: No assignee`);\n    }\n  }\n\n  // Generate recommendations\n  const avgPointsPerPerson = report.totalPoints / Object.keys(report.byAssignee).length;\n\n  for (const [assignee, points] of Object.entries(report.byAssignee)) {\n    if (points > avgPointsPerPerson * 1.5) {\n      report.recommendations.push(`${assignee} has ${points} points (${Math.round((points / avgPointsPerPerson - 1) * 100)}% above average) - consider rebalancing`);\n    }\n  }\n\n  return report;\n}\n```\n\n### Sprint Retrospective Generator\n\n```typescript\nasync function generateRetroBoard(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get completed sprint data\n  const sprintResult = await client.callTool({\n    name: \"jira_get_sprint_report\",\n    arguments: {\n      board_id: boardId,\n      sprint_id: sprintId\n    }\n  });\n\n  const sprintReport = JSON.parse(sprintResult.content[0].text);\n\n  // Get sprint issues with details\n  const issuesResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"status\", \"resolution\", \"customfield_10001\", \"assignee\"]\n    }\n  });\n\n  const issues = JSON.parse(issuesResult.content[0].text).issues;\n\n  // Calculate metrics\n  const completed = issues.filter((i: any) => i.fields.resolution);\n  const incomplete = issues.filter((i: any) => !i.fields.resolution);\n  const completedPoints = completed.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n  const plannedPoints = issues.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n\n  // Create retrospective page\n  const content = `\n<h2>Sprint Retrospective: ${sprintReport.sprint.name}</h2>\n<p><em>Generated on ${new Date().toISOString().split('T')[0]}</em></p>\n\n<h3>Sprint Metrics</h3>\n<table>\n  <tr><th>Metric</th><th>Value</th></tr>\n  <tr><td>Start Date</td><td>${sprintReport.sprint.startDate}</td></tr>\n  <tr><td>End Date</td><td>${sprintReport.sprint.endDate}</td></tr>\n  <tr><td>Issues Planned</td><td>${issues.length}</td></tr>\n  <tr><td>Issues Completed</td><td>${completed.length}</td></tr>\n  <tr><td>Completion Rate</td><td>${Math.round(completed.length / issues.length * 100)}%</td></tr>\n  <tr><td>Points Planned</td><td>${plannedPoints}</td></tr>\n  <tr><td>Points Completed</td><td>${completedPoints}</td></tr>\n  <tr><td>Velocity</td><td>${completedPoints} points</td></tr>\n</table>\n\n<h3>Completed Items</h3>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS NOT EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee</ac:parameter>\n</ac:structured-macro>\n\n${incomplete.length > 0 ? `\n<h3>Incomplete Items (Spillover)</h3>\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>${incomplete.length} items were not completed and will spill over to the next sprint.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee,status</ac:parameter>\n</ac:structured-macro>\n` : ''}\n\n<h3>Discussion Points</h3>\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What went well?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What could be improved?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Action Items</ac:parameter>\n  <ac:rich-text-body>\n    <ac:task-list>\n      <ac:task><ac:task-body><em>Add action items here...</em></ac:task-body></ac:task>\n    </ac:task-list>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  const newPage = await client.callTool({\n    name: \"confluence_create_page\",\n    arguments: {\n      space_key: confluenceSpaceKey,\n      title: `Sprint Retro - ${sprintReport.sprint.name}`,\n      body: content,\n      labels: [\"sprint-retro\", \"team-meeting\"]\n    }\n  });\n\n  return JSON.parse(newPage.content[0].text).id;\n}\n```\n\n## Issue-Documentation Link Workflow\n\nBidirectional linking between code changes and documentation.\n\n### Link PR to Documentation\n\n```typescript\nasync function linkPRToDocumentation(\n  client: MCPClient,\n  issueKey: string,\n  docPageId: string\n): Promise<void> {\n  // Add remote link to Jira issue\n  await client.callTool({\n    name: \"jira_add_remote_link\",\n    arguments: {\n      issue_key: issueKey,\n      url: `https://your-site.atlassian.net/wiki/spaces/DEV/pages/${docPageId}`,\n      title: \"Related Documentation\",\n      icon_url: \"https://your-site.atlassian.net/wiki/favicon.ico\"\n    }\n  });\n\n  // Add Jira macro to Confluence page\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: docPageId,\n      expand: [\"body.storage\", \"version\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n\n  // Check if link already exists\n  if (page.body.storage.value.includes(issueKey)) {\n    return; // Already linked\n  }\n\n  const jiraLink = `\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Related Issue</ac:parameter>\n  <ac:rich-text-body>\n    <p><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issueKey}</ac:parameter></ac:structured-macro></p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  await client.callTool({\n    name: \"confluence_update_page\",\n    arguments: {\n      page_id: docPageId,\n      title: page.title,\n      body: jiraLink + page.body.storage.value,\n      version_number: page.version.number + 1\n    }\n  });\n}\n```\n\n## Utility Functions\n\n```typescript\nfunction escapeHtml(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#039;');\n}\n\nfunction delay(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function withRetry<T>(\n  operation: () => Promise<T>,\n  maxRetries = 3,\n  baseDelay = 1000\n): Promise<T> {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      if (attempt === maxRetries) throw error;\n\n      const delay = baseDelay * Math.pow(2, attempt - 1);\n      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n## Related References\n\n- `jira-queries.md` - JQL syntax for workflow queries\n- `confluence-operations.md` - Page creation and formatting\n- `authentication-patterns.md` - Secure API access for automated workflows\n","isInternal":false,"tokens":4983,"sizeBytes":18860},{"name":"confluence-operations.md","path":"skills/atlassian-mcp/references/confluence-operations.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/atlassian-mcp/references/confluence-operations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Confluence Operations\n\n---\n\n## CQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `space = \"DEV\"` |\n| `!=` | Not equal | `type != attachment` |\n| `~` | Contains | `title ~ \"API\"` |\n| `!~` | Does not contain | `text !~ \"deprecated\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `lastModified >= \"2024-01-01\"` |\n| `IN` | Multiple values | `space IN (\"DEV\", \"OPS\")` |\n| `NOT IN` | Exclude | `creator NOT IN (\"bot\")` |\n\n### Field Reference\n\n**Content Fields:**\n```cql\ntype = page                        -- Pages only\ntype = blogpost                    -- Blog posts\ntype = attachment                  -- Attachments\ntype = comment                     -- Comments\nspace = \"DEVDOCS\"                  -- Specific space\nspace.type = global                -- Global spaces\nspace.type = personal              -- Personal spaces\n```\n\n**Search Fields:**\n```cql\ntitle ~ \"architecture\"             -- Title contains\ntext ~ \"kubernetes\"                -- Full text search\ncontent ~ \"deployment\"             -- Content body\nlabel = \"official\"                 -- Has label\nlabel IN (\"api\", \"reference\")      -- Multiple labels\n```\n\n**Date Fields:**\n```cql\ncreated >= \"2024-01-01\"            -- Created after date\nlastModified >= now(\"-30d\")        -- Modified in last 30 days\ncreated >= startOfYear()           -- Created this year\nlastModified >= startOfMonth()     -- Modified this month\n```\n\n**User Fields:**\n```cql\ncreator = currentUser()            -- Created by me\ncontributor = \"john.doe\"           -- Edited by user\nmention = currentUser()            -- Mentions me\nwatcher = currentUser()            -- Pages I watch\nfavourite = currentUser()          -- My favorites\n```\n\n## Essential CQL Patterns\n\n### Documentation Search\n\n```cql\n-- API documentation in dev space\nspace = \"DEV\" AND label = \"api-docs\" AND type = page\n\n-- Recently updated architecture docs\nlabel = \"architecture\" AND lastModified >= now(\"-7d\")\n\n-- Search for code examples\ntext ~ \"```\" AND label = \"tutorial\"\n\n-- Find outdated documentation\nlabel = \"needs-review\" OR lastModified <= now(\"-180d\")\n\n-- Meeting notes from this month\nlabel = \"meeting-notes\" AND created >= startOfMonth()\n```\n\n### Space Management\n\n```cql\n-- All pages in multiple spaces\nspace IN (\"DEV\", \"OPS\", \"PRODUCT\") AND type = page\n\n-- Personal space content\nspace.type = personal AND creator = currentUser()\n\n-- Archived content\nlabel = \"archived\" AND space = \"LEGACY\"\n\n-- Templates\ntype = page AND label = \"template\"\n```\n\n### Content Discovery\n\n```cql\n-- Popular pages (frequently viewed)\ntype = page AND space = \"DOCS\" ORDER BY lastModified DESC\n\n-- Draft pages\nlabel = \"draft\" AND type = page\n\n-- Pages without labels\ntype = page AND space = \"DEV\" AND label IS NULL\n\n-- Orphan pages (no parent)\ntype = page AND ancestor IS NULL AND space = \"DOCS\"\n```\n\n## MCP Tool Calls\n\n### Searching Content\n\n```typescript\n// Basic CQL search\nconst searchResult = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'space = \"DEV\" AND label = \"api-docs\"',\n    limit: 25,\n    expand: [\"body.storage\", \"version\", \"ancestors\"]\n  }\n});\n\n// Parse results\nconst results = JSON.parse(searchResult.content[0].text);\nfor (const page of results.results) {\n  console.log(`${page.title} - ${page._links.webui}`);\n}\n\n// Search with content preview\nconst searchWithContent = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'text ~ \"deployment\" AND space = \"OPS\"',\n    limit: 10,\n    excerpt: true\n  }\n});\n```\n\n### Getting Page Content\n\n```typescript\n// Get page by ID\nconst page = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"body.view\", \"version\", \"ancestors\", \"children.page\"]\n  }\n});\n\n// Get page by space and title\nconst pageByTitle = await client.callTool({\n  name: \"confluence_get_page_by_title\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Reference\"\n  }\n});\n\n// Get page children\nconst children = await client.callTool({\n  name: \"confluence_get_children\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"page\"]\n  }\n});\n```\n\n### Creating Pages\n\n```typescript\n// Create page with storage format (XHTML)\nconst newPage = await client.callTool({\n  name: \"confluence_create_page\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Authentication Guide\",\n    parent_id: \"123456\",  // Optional parent page\n    body: `\n      <h2>Overview</h2>\n      <p>This guide covers authentication methods for our API.</p>\n\n      <h2>OAuth 2.0</h2>\n      <p>We support OAuth 2.0 with the following grant types:</p>\n      <ul>\n        <li>Authorization Code</li>\n        <li>Client Credentials</li>\n      </ul>\n\n      <ac:structured-macro ac:name=\"code\">\n        <ac:parameter ac:name=\"language\">bash</ac:parameter>\n        <ac:plain-text-body><![CDATA[curl -X POST https://api.example.com/oauth/token \\\\\n  -d \"grant_type=client_credentials\" \\\\\n  -d \"client_id=<client-id>\" \\\\\n  -d \"client_secret=<client-secret>\"]]></ac:plain-text-body>\n      </ac:structured-macro>\n\n      <h2>API Tokens</h2>\n      <p>For simple integrations, use API tokens:</p>\n      <ac:structured-macro ac:name=\"info\">\n        <ac:rich-text-body>\n          <p>API tokens are tied to your user account and have the same permissions.</p>\n        </ac:rich-text-body>\n      </ac:structured-macro>\n    `,\n    labels: [\"api-docs\", \"authentication\", \"official\"]\n  }\n});\n\nconsole.log(`Created page: ${newPage.content[0].text}`);\n```\n\n### Updating Pages\n\n```typescript\n// Update page content\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: \"API Authentication Guide (Updated)\",\n    body: \"<h2>Updated Content</h2><p>New documentation here...</p>\",\n    version_number: 5,  // Current version + 1\n    version_message: \"Added OAuth 2.1 section\"\n  }\n});\n\n// Append to existing page\nconst currentPage = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\nconst pageData = JSON.parse(currentPage.content[0].text);\nconst currentBody = pageData.body.storage.value;\nconst newSection = `\n  <h2>New Section</h2>\n  <p>Additional content appended to the page.</p>\n`;\n\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: pageData.title,\n    body: currentBody + newSection,\n    version_number: pageData.version.number + 1\n  }\n});\n```\n\n### Working with Comments\n\n```typescript\n// Add comment to page\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    body: \"<p>This section needs to be updated for v2.0 changes.</p>\"\n  }\n});\n\n// Get page comments\nconst comments = await client.callTool({\n  name: \"confluence_get_comments\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\n// Reply to comment\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    parent_comment_id: \"789012\",\n    body: \"<p>Good catch! I'll update this section.</p>\"\n  }\n});\n```\n\n### Managing Labels\n\n```typescript\n// Add labels to page\nawait client.callTool({\n  name: \"confluence_add_labels\",\n  arguments: {\n    page_id: \"123456\",\n    labels: [\"reviewed\", \"q1-2024\", \"api-v2\"]\n  }\n});\n\n// Remove label\nawait client.callTool({\n  name: \"confluence_remove_label\",\n  arguments: {\n    page_id: \"123456\",\n    label: \"draft\"\n  }\n});\n\n// Get page labels\nconst labels = await client.callTool({\n  name: \"confluence_get_labels\",\n  arguments: {\n    page_id: \"123456\"\n  }\n});\n```\n\n### Space Operations\n\n```typescript\n// Get space information\nconst space = await client.callTool({\n  name: \"confluence_get_space\",\n  arguments: {\n    space_key: \"DEV\",\n    expand: [\"description\", \"homepage\"]\n  }\n});\n\n// List all spaces\nconst spaces = await client.callTool({\n  name: \"confluence_list_spaces\",\n  arguments: {\n    type: \"global\",\n    limit: 100\n  }\n});\n\n// Get space content\nconst spaceContent = await client.callTool({\n  name: \"confluence_get_space_content\",\n  arguments: {\n    space_key: \"DEV\",\n    depth: \"root\",  // or \"all\"\n    expand: [\"children.page\"]\n  }\n});\n```\n\n## Storage Format Reference\n\n### Common Macros\n\n```xml\n<!-- Code block -->\n<ac:structured-macro ac:name=\"code\">\n  <ac:parameter ac:name=\"language\">python</ac:parameter>\n  <ac:parameter ac:name=\"title\">Example</ac:parameter>\n  <ac:plain-text-body><![CDATA[print(\"Hello, World!\")]]></ac:plain-text-body>\n</ac:structured-macro>\n\n<!-- Info panel -->\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Note</ac:parameter>\n  <ac:rich-text-body>\n    <p>Important information here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Warning panel -->\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>Be careful with this operation!</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Table of contents -->\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">3</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issue link -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"key\">PROJ-123</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issues table -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">project = PROJ AND sprint in openSprints()</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n\n<!-- Expand section -->\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Click to expand</ac:parameter>\n  <ac:rich-text-body>\n    <p>Hidden content here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Include page -->\n<ac:structured-macro ac:name=\"include\">\n  <ac:parameter ac:name=\"\"><ri:page ri:content-title=\"Shared Footer\" /></ac:parameter>\n</ac:structured-macro>\n```\n\n### Formatting Elements\n\n```xml\n<!-- Status badge -->\n<ac:structured-macro ac:name=\"status\">\n  <ac:parameter ac:name=\"colour\">Green</ac:parameter>\n  <ac:parameter ac:name=\"title\">APPROVED</ac:parameter>\n</ac:structured-macro>\n\n<!-- User mention -->\n<ac:link><ri:user ri:account-id=\"557058:f3c7...\" /></ac:link>\n\n<!-- Page link -->\n<ac:link><ri:page ri:content-title=\"Target Page\" ri:space-key=\"DEV\" /></ac:link>\n\n<!-- Attachment -->\n<ac:link><ri:attachment ri:filename=\"diagram.png\" /></ac:link>\n\n<!-- Image from attachment -->\n<ac:image><ri:attachment ri:filename=\"screenshot.png\" /></ac:image>\n\n<!-- External image -->\n<ac:image><ri:url ri:value=\"https://example.com/image.png\" /></ac:image>\n```\n\n## Pagination Handling\n\n```typescript\nasync function getAllPages(cql: string): Promise<Page[]> {\n  const allPages: Page[] = [];\n  let start = 0;\n  const limit = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"confluence_search\",\n      arguments: {\n        cql,\n        start,\n        limit,\n        expand: [\"body.storage\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allPages.push(...response.results);\n\n    if (response.results.length < limit || !response._links.next) {\n      break;\n    }\n\n    start += limit;\n  }\n\n  return allPages;\n}\n```\n\n## Error Handling\n\n```typescript\nasync function safeConfluenceCall<T>(operation: () => Promise<T>): Promise<T> {\n  try {\n    return await operation();\n  } catch (error: any) {\n    const status = error.response?.status;\n\n    switch (status) {\n      case 404:\n        throw new Error(`Page or space not found: ${error.message}`);\n      case 403:\n        throw new Error(`Permission denied. Check space permissions.`);\n      case 409:\n        throw new Error(`Version conflict. Page was modified. Refresh and retry.`);\n      case 429:\n        const retryAfter = error.response?.headers?.['retry-after'] || 60;\n        throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);\n      default:\n        throw error;\n    }\n  }\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```cql\n-- Too broad (slow)\ntext ~ \"the\"\n\n-- Missing quotes\nspace = DEV DOCS  -- WRONG\nspace = \"DEV DOCS\"  -- CORRECT\n\n-- Invalid date format\ncreated >= 2024-01-01  -- WRONG\ncreated >= \"2024-01-01\"  -- CORRECT\n```\n\n**Best Practices:**\n- Always specify space when possible for faster queries\n- Use labels for categorization and filtering\n- Combine text search with specific fields\n- Cache frequently accessed page content\n- Handle version conflicts gracefully\n\n## Related References\n\n- `common-workflows.md` - Documentation sync and automation\n- `jira-queries.md` - Linking Confluence pages to Jira issues\n- `authentication-patterns.md` - API access configuration\n","isInternal":false,"tokens":3341,"sizeBytes":12714},{"name":"jira-queries.md","path":"skills/atlassian-mcp/references/jira-queries.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/atlassian-mcp/references/jira-queries.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Jira Queries and Operations\n\n---\n\n## JQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `project = \"PROJ\"` |\n| `!=` | Not equal | `status != Done` |\n| `~` | Contains (text search) | `summary ~ \"login bug\"` |\n| `!~` | Does not contain | `description !~ \"test\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `created >= -7d` |\n| `IN` | Multiple values | `status IN (Open, \"In Progress\")` |\n| `NOT IN` | Exclude values | `assignee NOT IN (john, jane)` |\n| `IS` | Null check | `assignee IS EMPTY` |\n| `IS NOT` | Not null | `resolution IS NOT EMPTY` |\n| `WAS` | Historical state | `status WAS \"In Progress\"` |\n| `CHANGED` | Field changed | `status CHANGED FROM Open` |\n\n### Field Reference\n\n**Standard Fields:**\n```jql\nproject = PROJ\nissuetype = Bug\nstatus = \"In Progress\"\npriority = High\nassignee = currentUser()\nreporter = \"john.doe\"\nresolution = Unresolved\nlabels = backend\ncomponent = \"API\"\nfixVersion = \"2.0\"\naffectsVersion = \"1.5\"\n```\n\n**Date Fields:**\n```jql\ncreated >= -30d                    -- Last 30 days\nupdated >= \"2024-01-01\"           -- Since specific date\ndue <= endOfWeek()                 -- Due this week\nresolved >= startOfMonth()         -- Resolved this month\n```\n\n**Text Search:**\n```jql\nsummary ~ \"authentication\"         -- Summary contains\ndescription ~ \"error AND login\"    -- Description search\ntext ~ \"payment failed\"            -- All text fields\ncomment ~ \"blocked\"                -- Comment contains\n```\n\n## Essential JQL Patterns\n\n### Sprint and Backlog Queries\n\n```jql\n-- Current sprint issues\nsprint in openSprints() AND project = PROJ\n\n-- Backlog items\nsprint IS EMPTY AND resolution IS EMPTY AND project = PROJ\n\n-- Sprint completion\nsprint = \"Sprint 23\" AND status = Done\n\n-- Spillover from last sprint\nsprint in closedSprints() AND resolution IS EMPTY\n\n-- Ready for sprint planning\nstatus = \"Ready for Dev\" AND sprint IS EMPTY\n```\n\n### Bug Tracking\n\n```jql\n-- Open bugs by priority\nissuetype = Bug AND resolution IS EMPTY ORDER BY priority DESC\n\n-- Critical production bugs\nissuetype = Bug AND priority IN (Highest, High)\n  AND labels = production AND resolution IS EMPTY\n\n-- Bugs created this week\nissuetype = Bug AND created >= startOfWeek()\n\n-- Bugs without reproduction steps\nissuetype = Bug AND \"Reproduction Steps\" IS EMPTY\n  AND resolution IS EMPTY\n\n-- Regression bugs\nissuetype = Bug AND labels = regression AND fixVersion = \"2.0\"\n```\n\n### Team Workload\n\n```jql\n-- My open issues\nassignee = currentUser() AND resolution IS EMPTY\n\n-- Unassigned high priority\nassignee IS EMPTY AND priority IN (Highest, High)\n  AND resolution IS EMPTY\n\n-- Team member workload\nassignee = \"jane.smith\" AND sprint in openSprints()\n\n-- Blocked issues\nstatus = Blocked OR labels = blocked\n\n-- Stale issues (no update in 14 days)\nupdated <= -14d AND resolution IS EMPTY\n```\n\n### Release Management\n\n```jql\n-- Release candidates\nfixVersion = \"2.0\" AND status = \"Ready for Release\"\n\n-- Missing fix version\nresolution = Done AND fixVersion IS EMPTY AND updated >= -30d\n\n-- Release blockers\nfixVersion = \"2.0\" AND priority = Blocker AND resolution IS EMPTY\n\n-- Changelog items\nfixVersion = \"2.0\" AND resolution = Done ORDER BY issuetype\n```\n\n## MCP Tool Calls\n\n### Searching Issues\n\n```typescript\n// Basic JQL search\nconst searchResult = await client.callTool({\n  name: \"jira_search\",\n  arguments: {\n    jql: \"project = PROJ AND sprint in openSprints()\",\n    max_results: 50,\n    fields: [\"summary\", \"status\", \"assignee\", \"priority\"]\n  }\n});\n\n// Parse response\nconst issues = JSON.parse(searchResult.content[0].text);\nfor (const issue of issues.issues) {\n  console.log(`${issue.key}: ${issue.fields.summary}`);\n}\n```\n\n### Getting Issue Details\n\n```typescript\n// Get single issue with all fields\nconst issue = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    expand: [\"changelog\", \"comments\", \"transitions\"]\n  }\n});\n\n// Get issue with specific fields\nconst issuePartial = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: [\"summary\", \"description\", \"customfield_10001\"]\n  }\n});\n```\n\n### Creating Issues\n\n```typescript\n// Create a bug\nconst newBug = await client.callTool({\n  name: \"jira_create_issue\",\n  arguments: {\n    project_key: \"PROJ\",\n    issue_type: \"Bug\",\n    summary: \"Login fails with SSO enabled\",\n    description: {\n      type: \"doc\",\n      version: 1,\n      content: [\n        {\n          type: \"paragraph\",\n          content: [{ type: \"text\", text: \"Users cannot log in when SSO is enabled.\" }]\n        },\n        {\n          type: \"heading\",\n          attrs: { level: 3 },\n          content: [{ type: \"text\", text: \"Steps to Reproduce\" }]\n        },\n        {\n          type: \"orderedList\",\n          content: [\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Enable SSO in settings\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Log out\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Attempt to log in via SSO\" }] }] }\n          ]\n        }\n      ]\n    },\n    priority: \"High\",\n    labels: [\"sso\", \"authentication\", \"production\"],\n    components: [\"Authentication\"],\n    assignee: \"jane.smith\"\n  }\n});\n\nconsole.log(`Created: ${newBug.content[0].text}`); // PROJ-456\n```\n\n### Updating Issues\n\n```typescript\n// Update issue fields\nawait client.callTool({\n  name: \"jira_update_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: {\n      summary: \"Updated summary\",\n      priority: { name: \"Highest\" },\n      labels: [\"urgent\", \"production\"]\n    }\n  }\n});\n\n// Add comment\nawait client.callTool({\n  name: \"jira_add_comment\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    body: \"Investigating this issue. Initial analysis suggests a race condition.\"\n  }\n});\n\n// Transition issue\nawait client.callTool({\n  name: \"jira_transition_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    transition: \"In Progress\"\n  }\n});\n```\n\n### Sprint Operations\n\n```typescript\n// Get active sprints\nconst sprints = await client.callTool({\n  name: \"jira_get_sprints\",\n  arguments: {\n    board_id: 42,\n    state: \"active\"\n  }\n});\n\n// Move issue to sprint\nawait client.callTool({\n  name: \"jira_move_to_sprint\",\n  arguments: {\n    sprint_id: 123,\n    issue_keys: [\"PROJ-100\", \"PROJ-101\", \"PROJ-102\"]\n  }\n});\n\n// Get sprint report\nconst report = await client.callTool({\n  name: \"jira_get_sprint_report\",\n  arguments: {\n    board_id: 42,\n    sprint_id: 123\n  }\n});\n```\n\n### Issue Linking\n\nUse `jira_create_issue_link` to create dependency relationships between issues.\n\n> **The parameter names are counterintuitive.** The naming reflects Jira's internal \"inward/outward\" link direction, not natural English. Verify every link call against the table below.\n\n#### Parameter Semantics for \"Blocks\" Links\n\n| Parameter | Role | Meaning |\n|-----------|------|---------|\n| `inward_issue_key` | **Blocker** | This issue blocks the other |\n| `outward_issue_key` | **Blocked** | This issue is blocked by the other |\n\n**Memory aid:** `inward_issue_key` = the issue receiving the inward description (\"is blocked by\") — but it is the *blocker*. Think: \"the inward key is where the arrow points FROM.\"\n\n#### Single Blocks Link\n\n```typescript\n// Make AUTH-1 block AUTH-2\n// AUTH-1 will show: \"blocks AUTH-2\"\n// AUTH-2 will show: \"is blocked by AUTH-1\"\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-1\",   // blocker\n    outward_issue_key: \"AUTH-2\"   // blocked\n  }\n});\n```\n\n#### Linking a Dependency Chain\n\nWhen creating a chain A → B → C (A blocks B, B blocks C):\n\n```typescript\nconst chain = [\n  { blocker: \"AUTH-1\", blocked: \"AUTH-2\" },\n  { blocker: \"AUTH-2\", blocked: \"AUTH-3\" },\n  { blocker: \"AUTH-3\", blocked: \"AUTH-4\" }\n];\n\nfor (const dep of chain) {\n  await client.callTool({\n    name: \"jira_create_issue_link\",\n    arguments: {\n      link_type: \"Blocks\",\n      inward_issue_key: dep.blocker,\n      outward_issue_key: dep.blocked\n    }\n  });\n\n  // Respect rate limits between link operations\n  await delay(100);\n}\n```\n\n#### Other Link Types\n\nThe same `inward`/`outward` pattern applies to all link types:\n\n| Link Type | `inward_issue_key` shows | `outward_issue_key` shows |\n|-----------|--------------------------|---------------------------|\n| `Blocks` | \"blocks [outward]\" | \"is blocked by [inward]\" |\n| `Duplicate` | \"duplicates [outward]\" | \"is duplicated by [inward]\" |\n| `Relates` | \"relates to [outward]\" | \"relates to [inward]\" |\n\n```typescript\n// Mark PROJ-10 as a duplicate of PROJ-5\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Duplicate\",\n    inward_issue_key: \"PROJ-10\",   // the duplicate\n    outward_issue_key: \"PROJ-5\"    // the original\n  }\n});\n```\n\n#### Anti-Pattern: Reversed Parameters\n\n```typescript\n// WRONG — This makes AUTH-2 block AUTH-1 (backwards!)\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-2\",   // accidentally made AUTH-2 the blocker\n    outward_issue_key: \"AUTH-1\"   // accidentally made AUTH-1 the blocked\n  }\n});\n```\n\nAlways verify: after creating a link, the blocker (`inward_issue_key`) should display \"blocks [outward]\" in its Jira issue view.\n\n## Pagination Handling\n\n```typescript\nasync function getAllIssues(jql: string): Promise<Issue[]> {\n  const allIssues: Issue[] = [];\n  let startAt = 0;\n  const maxResults = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"jira_search\",\n      arguments: {\n        jql,\n        start_at: startAt,\n        max_results: maxResults,\n        fields: [\"summary\", \"status\", \"assignee\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allIssues.push(...response.issues);\n\n    if (startAt + response.issues.length >= response.total) {\n      break;\n    }\n\n    startAt += maxResults;\n  }\n\n  return allIssues;\n}\n```\n\n## Bulk Operations\n\n```typescript\n// Bulk update with JQL\nasync function bulkUpdateLabels(jql: string, addLabels: string[]) {\n  const issues = await getAllIssues(jql);\n\n  for (const issue of issues) {\n    const existingLabels = issue.fields.labels || [];\n    await client.callTool({\n      name: \"jira_update_issue\",\n      arguments: {\n        issue_key: issue.key,\n        fields: {\n          labels: [...new Set([...existingLabels, ...addLabels])]\n        }\n      }\n    });\n\n    // Respect rate limits\n    await delay(100);\n  }\n}\n\n// Usage\nawait bulkUpdateLabels(\n  'project = PROJ AND sprint in openSprints() AND labels = backend',\n  ['q4-priority', 'needs-review']\n);\n```\n\n## Error Handling\n\n```typescript\nasync function safeJiraCall<T>(\n  operation: () => Promise<T>,\n  retries = 3\n): Promise<T> {\n  for (let attempt = 1; attempt <= retries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      const status = error.response?.status;\n\n      // Don't retry client errors (except rate limits)\n      if (status >= 400 && status < 500 && status !== 429) {\n        throw error;\n      }\n\n      // Rate limited - wait and retry\n      if (status === 429) {\n        const retryAfter = parseInt(error.response?.headers?.['retry-after'] || '60');\n        console.log(`Rate limited. Waiting ${retryAfter}s...`);\n        await delay(retryAfter * 1000);\n        continue;\n      }\n\n      // Server error - exponential backoff\n      if (attempt < retries) {\n        const backoff = Math.pow(2, attempt) * 1000;\n        console.log(`Attempt ${attempt} failed. Retrying in ${backoff}ms...`);\n        await delay(backoff);\n      } else {\n        throw error;\n      }\n    }\n  }\n  throw new Error('Unexpected end of retry loop');\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```jql\n-- Too broad (slow, may timeout)\nproject IS NOT EMPTY\n\n-- Missing quotes for multi-word values\nstatus = In Progress  -- WRONG\nstatus = \"In Progress\"  -- CORRECT\n\n-- Case sensitivity issues\nassignee = John  -- May fail\nassignee = \"john.doe@company.com\"  -- CORRECT\n\n-- Inefficient ordering\nORDER BY created  -- Missing direction\nORDER BY created DESC  -- CORRECT\n```\n\n## Related References\n\n- `common-workflows.md` - End-to-end workflow patterns\n- `authentication-patterns.md` - Credential setup for API calls\n- `confluence-operations.md` - Linking Jira issues to Confluence pages\n","isInternal":false,"tokens":3309,"sizeBytes":12577},{"name":"mcp-server-setup.md","path":"skills/atlassian-mcp/references/mcp-server-setup.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/atlassian-mcp/references/mcp-server-setup.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# MCP Server Setup\n\n---\n\n## Server Options Overview\n\n### Atlassian Rovo MCP Server (official, recommended)\n\nAtlassian hosts the official MCP server. There is nothing to install: clients connect over Streamable HTTP to\n\n```\nhttps://mcp.atlassian.com/v2/mcp\n```\n\n**Capabilities:**\n- Jira, Confluence, Jira Service Management, Bitbucket Cloud, Compass, Loom, Goals, Projects, Teams, Focus, Talent and the Teamwork Graph\n- OAuth 2.1 browser sign-in, or API tokens for headless clients (admin-enabled)\n- Acts with the signed-in user's existing Atlassian permissions\n- Tools are grouped into permission groups (`read_jira`, `write_jira`, `search_jira`, `read_confluence`, ...) that org admins grant or revoke; `delete_jira` and `manage_jira` are off by default\n- Exposes a small set of primary tools and lets the client discover the rest on demand; append `?tools=all` for a flat list (useful behind MCP gateways)\n- Atlassian Cloud only\n\n**Endpoint versions:**\n\n| Endpoint | Status |\n|----------|--------|\n| `https://mcp.atlassian.com/v2/mcp` | Recommended for new setups |\n| `https://mcp.atlassian.com/v1/mcp`, `/v1/mcp/authv2` | Still supported; existing v1 connections start exposing v2 tools |\n| `https://mcp.atlassian.com/v1/sse` | Legacy SSE endpoint, unsupported after 30 June 2026; move to `/v2/mcp` |\n\nSources: [atlassian/atlassian-mcp-server](https://github.com/atlassian/atlassian-mcp-server), [Getting started with the Atlassian Rovo MCP Server](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/), [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/).\n\n### Community Servers\n\nUse these only when the official server does not fit, mainly for Jira or Confluence **Server/Data Center**.\n\n**mcp-atlassian (sooperset)** - Python, the most complete community option, and the only one here with Server/DC support. Published on PyPI as `mcp-atlassian` and as a Docker image:\n```bash\n# Run without installing (recommended)\nuvx mcp-atlassian --help\n\n# Or install\nuv tool install mcp-atlassian\npip install mcp-atlassian\n\n# Or Docker\ndocker pull ghcr.io/sooperset/mcp-atlassian:latest\n```\n\n**atlassian-mcp (xuanxt)** - TypeScript, Jira and Confluence Cloud only. Published on npm as `@xuandev/atlassian-mcp`:\n```bash\nnpx -y @xuandev/atlassian-mcp\n```\n\n### Comparison Matrix\n\n| Feature | Rovo MCP Server | sooperset | xuanxt |\n|---------|-----------------|-----------|--------|\n| Hosting | Atlassian-hosted | Self-run (stdio, Docker, HTTP) | Self-run |\n| Jira / Confluence Cloud | Yes | Yes | Yes |\n| Jira / Confluence Server/DC | No | Yes | No |\n| Other Atlassian products | JSM, Bitbucket, Compass, Loom, Goals, ... | Jira and Confluence only | Jira and Confluence only |\n| OAuth 2.1 | Yes | Yes (bring your own OAuth app) | No |\n| API token | Yes, if an admin enables it | Yes | Yes |\n| PAT (Server/DC) | No | Yes | No |\n| Admin controls | Permission groups, domain and IP allowlists, audit log | Env vars (`READ_ONLY_MODE`, filters) | Env vars |\n\n## Connecting to the Rovo MCP Server\n\n### Claude Code\n\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nThen run `/mcp` in a session and complete the OAuth sign-in in the browser.\n\n### Claude Desktop\n\nInstall it from **Settings → Extensions → Browse extensions → Plugins**, then search for **Atlassian**.\n\n### Other Clients\n\nAny client that speaks Streamable HTTP takes the server URL directly. Clients that only run local stdio servers can bridge with the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) proxy (Node.js 18+):\n\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-remote\", \"https://mcp.atlassian.com/v2/mcp\"]\n    }\n  }\n}\n```\n\n### Headless: API Token Authentication\n\nFor automations and other non-interactive clients. An org admin must first enable API token authentication (**Atlassian Administration → Rovo → Rovo MCP server → Authentication**). Jira Service Management tools require it.\n\n| Mechanism | Header |\n|-----------|--------|\n| Personal API token | `Authorization: Basic <base64(email:api_token)>` |\n| Service account API key | `Authorization: Bearer <api_key>` |\n\n```bash\n# Build the Basic credential from environment variables; never paste tokens inline\nexport ATLASSIAN_BASIC_AUTH=$(printf '%s:%s' \"$ATLASSIAN_EMAIL\" \"$ATLASSIAN_API_TOKEN\" | base64 -w0)\n\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp \\\n  --header \"Authorization: Basic ${ATLASSIAN_BASIC_AUTH}\"\n```\n\nCreate the token with only the scopes the tools need. See [Configuring authentication via API token](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/configuring-authentication-via-api-token/).\n\n### Admin Prerequisites\n\n- **Domain allowlist:** OAuth connections must come from a client domain allowed under Atlassian Administration → Rovo → Rovo MCP server. Atlassian-supported clients are allowed by default.\n- **IP allowlist:** if the org uses IP allowlisting, tool calls must come from an allowed IP. Hosted AI clients call from the provider's egress IPs, not the user's VPN.\n- **Audit:** every tool call is logged under Insights → Audit log (filter for *Rovo MCP User Actions*).\n\nSee [Control Atlassian Rovo MCP Server settings](https://support.atlassian.com/security-and-access-policies/docs/control-atlassian-rovo-mcp-server-settings/).\n\n## Configuring sooperset (Server/DC or Self-Hosted)\n\n**Cloud with API token:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"CONFLUENCE_URL\": \"https://your-company.atlassian.net/wiki\",\n        \"CONFLUENCE_USERNAME\": \"your-email@company.com\",\n        \"CONFLUENCE_API_TOKEN\": \"<confluence-api-token>\",\n        \"JIRA_URL\": \"https://your-company.atlassian.net\",\n        \"JIRA_USERNAME\": \"your-email@company.com\",\n        \"JIRA_API_TOKEN\": \"<jira-api-token>\"\n      }\n    }\n  }\n}\n```\n\n**Server/Data Center with PAT:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.company.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"<jira-personal-access-token>\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.company.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"<confluence-personal-access-token>\"\n      }\n    }\n  }\n}\n```\n\n### Environment Variables Reference\n\nFrom the project's [`.env.example`](https://github.com/sooperset/mcp-atlassian/blob/main/.env.example).\n\n**Jira:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `JIRA_URL` | Base URL of the Jira instance | Yes |\n| `JIRA_USERNAME` | Account email (Cloud) | Cloud only |\n| `JIRA_API_TOKEN` | API token (Cloud) | Cloud only |\n| `JIRA_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `JIRA_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Confluence:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `CONFLUENCE_URL` | Base URL, with `/wiki` for Cloud | Yes |\n| `CONFLUENCE_USERNAME` | Account email (Cloud) | Cloud only |\n| `CONFLUENCE_API_TOKEN` | API token (Cloud) | Cloud only |\n| `CONFLUENCE_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `CONFLUENCE_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Scope, safety and tuning:**\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `READ_ONLY_MODE` | Disable all create, update and delete tools | false |\n| `JIRA_PROJECTS_FILTER` | Comma-separated Jira project keys to limit operations to | All |\n| `CONFLUENCE_SPACES_FILTER` | Comma-separated Confluence space keys to limit operations to | All |\n| `TOOLSETS` | Tool groups to enable (`default`, `all`, or names such as `jira_agile`) | All |\n| `ENABLED_TOOLS` | Comma-separated tool names to enable | All in active toolsets |\n| `JIRA_TIMEOUT` / `CONFLUENCE_TIMEOUT` | HTTP timeout in seconds | 75 |\n| `ATLASSIAN_RETRY_TOTAL` | Retry attempts for read requests | Off |\n| `ATLASSIAN_REQUESTS_PER_SECOND` | Client-side request rate cap | Off |\n| `MCP_VERBOSE` / `MCP_VERY_VERBOSE` | INFO / DEBUG logging | WARNING |\n\n## Tool Names Differ by Server\n\nEach server names its tools differently. The examples in `jira-queries.md`, `confluence-operations.md` and `common-workflows.md` use sooperset's names (`jira_search`, `jira_get_issue`, ...). With the Rovo MCP Server, list the tools your client sees (or connect with `?tools=all`) and check [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/) before writing tool calls. Its tools take a `cloudId`; get it from `getAccessibleAtlassianResources`, or pin it in your agent instructions to skip that call.\n\n## Verification and Testing\n\n```bash\n# Rovo MCP Server: confirm the connection and auth status\nclaude mcp list\n\n# sooperset: confirm it starts and sees its environment\nuvx mcp-atlassian --help\nenv | grep -E \"^(JIRA|CONFLUENCE)_\" | sed 's/=.*/=***/'\n```\n\n### Test Connection (sooperset)\n\n```typescript\n// test-connection.ts\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nasync function testConnection(): Promise<void> {\n  const transport = new StdioClientTransport({\n    command: \"uvx\",\n    args: [\"mcp-atlassian\"],\n    env: process.env as Record<string, string>,\n  });\n\n  const client = new Client(\n    { name: \"test-client\", version: \"1.0.0\" },\n    { capabilities: {} }\n  );\n\n  await client.connect(transport);\n\n  // List available tools\n  const tools = await client.listTools();\n  console.log(\"Available tools:\", tools.tools.map(t => t.name));\n\n  // Test a simple read operation\n  const result = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: { issue_key: \"TEST-1\" }\n  });\n  console.log(\"Test result:\", result);\n\n  await client.close();\n}\n\ntestConnection().catch(console.error);\n```\n\n## When to Use Each Server\n\n**Choose the Rovo MCP Server when:**\n- Using Atlassian Cloud (the default choice)\n- Need products beyond Jira and Confluence\n- Want OAuth without running your own OAuth app\n- Need admin-managed permissions and audit logging\n\n**Choose sooperset when:**\n- Running Jira or Confluence Server/Data Center\n- Need PAT authentication\n- Must run the server inside your own network\n- Want local controls such as `READ_ONLY_MODE` or project and space filters\n\n**Choose xuanxt when:**\n- Want a small TypeScript server for Cloud that you can fork and extend\n\n## Troubleshooting\n\n**Rovo MCP Server: sign-in fails after moving from v1 to v2:**\nSome clients keep a cached client ID or `.well-known` credentials from v1. Clear them and authenticate again.\n\n**Rovo MCP Server: 403 or \"not allowed\":**\n- The client domain is not on the org's allowlist, or\n- IP allowlisting blocks the caller. Hosted clients call from the provider's IPs, so being on the VPN is not enough. Ask an org admin to review both.\n\n**Rovo MCP Server: a tool is missing:**\nIts permission group is not granted (`delete_jira` and `manage_jira` are off by default), or it needs the other auth method (JSM tools need an API token, Compass tools need OAuth).\n\n**\"Authentication failed\" with sooperset:**\n```bash\n# Verify API token is valid (Cloud)\ncurl -u \"<email>:<api-token>\" \\\n  \"https://your-company.atlassian.net/rest/api/3/myself\"\n\n# Verify PAT is valid (Server/DC)\ncurl -H \"Authorization: Bearer <personal-access-token>\" \\\n  \"https://jira.internal.company.com/rest/api/2/myself\"\n```\n\n**Rate limited (HTTP 429) with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"ATLASSIAN_REQUESTS_PER_SECOND\": \"5\",\n        \"ATLASSIAN_RETRY_TOTAL\": \"5\"\n      }\n    }\n  }\n}\n```\n\n**Debug logging with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"MCP_VERY_VERBOSE\": \"true\"\n      }\n    }\n  }\n}\n```\n\n## Security Best Practices\n\n1. **Prefer OAuth for people, scoped API tokens for automation** - Never share one user's token across a team\n2. **Never commit credentials** - Use environment variables or a secrets manager\n3. **Rotate API tokens regularly** - Set calendar reminders for 90-day rotation\n4. **Grant minimal permission groups** - Leave `delete_jira` and `manage_jira` off unless needed\n5. **Review the audit log** - Rovo MCP tool calls are recorded per user\n6. **Restrict network access** - Use domain and IP allowlists\n\n## Related References\n\n- `authentication-patterns.md` - OAuth 2.1 and API token setup details\n- `jira-queries.md` - JQL syntax after connection is established\n- `confluence-operations.md` - CQL and page operations\n","isInternal":false,"tokens":3298,"sizeBytes":12736},{"name":"SKILL.md","path":"skills/atlassian-mcp/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/atlassian-mcp/SKILL.md","title":"atlassian-mcp","category":"anthropic-skill","format":"markdown","content":"---\nname: atlassian-mcp\ndescription: Integrates with Atlassian products to manage project tracking and documentation via MCP protocol. Use when querying Jira issues with JQL filters, creating and updating tickets with custom fields, searching or editing Confluence pages with CQL, managing sprints and backlogs, setting up MCP server authentication, syncing documentation, or debugging Atlassian API integrations.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.2.0\"\n  domain: platform\n  triggers: Jira, Confluence, Atlassian, MCP, tickets, issues, wiki, JQL, CQL, sprint, backlog, project management\n  role: expert\n  scope: implementation\n  output-format: code\n  related-skills: mcp-developer, api-designer, security-reviewer\n---\n\n# Atlassian MCP Expert\n\n## When to Use This Skill\n\n- Querying Jira issues with JQL filters\n- Searching or creating Confluence pages\n- Automating sprint workflows and backlog management\n- Setting up MCP server authentication (OAuth/API tokens)\n- Syncing meeting notes to Jira tickets\n- Generating documentation from issue data\n- Debugging Atlassian API integration issues\n- Choosing between official vs open-source MCP servers\n\n## Core Workflow\n\n1. **Select server** - Choose official cloud, open-source, or self-hosted MCP server\n2. **Authenticate** - Configure OAuth 2.1, API tokens, or PAT credentials\n3. **Design queries** - Write JQL for Jira, CQL for Confluence; validate with `maxResults=1` before full execution\n4. **Implement workflow** - Build tool calls, handle pagination, error recovery\n5. **Verify permissions** - Confirm required scopes with a read-only probe before any write or bulk operation\n6. **Deploy** - Configure IDE integration, test permissions, monitor rate limits\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Server Setup | `references/mcp-server-setup.md` | Installation, choosing servers, configuration |\n| Jira Operations | `references/jira-queries.md` | JQL syntax, issue CRUD, sprints, boards, issue linking |\n| Confluence Ops | `references/confluence-operations.md` | CQL search, page creation, spaces, comments |\n| Authentication | `references/authentication-patterns.md` | OAuth 2.0, API tokens, permission scopes |\n| Common Workflows | `references/common-workflows.md` | Issue triage, doc sync, sprint automation |\n\n## Quick-Start Examples\n\n### JQL Query Samples\n```\n# Open issues assigned to current user in a sprint\nproject = PROJ AND status = \"In Progress\" AND assignee = currentUser() ORDER BY priority DESC\n\n# Unresolved bugs created in the last 7 days\nproject = PROJ AND issuetype = Bug AND status != Done AND created >= -7d ORDER BY created DESC\n\n# Validate before bulk: test with maxResults=1 first\nproject = PROJ AND sprint in openSprints() AND status = Open ORDER BY created DESC\n```\n\n### CQL Query Samples\n```\n# Find pages updated in a specific space recently\nspace = \"ENG\" AND type = page AND lastModified >= \"2024-01-01\" ORDER BY lastModified DESC\n\n# Search page text for a keyword\nspace = \"ENG\" AND type = page AND text ~ \"deployment runbook\"\n```\n\n### Minimal MCP Server Configuration\nAtlassian Cloud: connect to the hosted Atlassian Rovo MCP Server, then run `/mcp` to sign in with OAuth.\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nServer/Data Center: run the community `mcp-atlassian` server (sooperset) with personal access tokens.\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.example.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"${JIRA_PERSONAL_TOKEN}\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.example.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"${CONFLUENCE_PERSONAL_TOKEN}\"\n      }\n    }\n  }\n}\n```\n> **Note:** Always load tokens from environment variables or a secrets manager; never hardcode credentials. Tool names differ between servers, see `references/mcp-server-setup.md`.\n\n## Constraints\n\n### MUST DO\n- Respect user permissions and workspace access controls\n- Validate JQL/CQL queries before execution (use `maxResults=1` probe first)\n- Handle rate limits with exponential backoff\n- Use pagination for large result sets (50-100 items per page)\n- Implement error recovery for network failures\n- Log API calls for debugging and audit trails\n- Test with read-only operations first\n- Document required permission scopes\n- Confirm before any write or bulk operation against production data\n\n### MUST NOT DO\n- Hardcode API tokens or OAuth secrets in code\n- Ignore rate limit headers from Atlassian APIs\n- Create issues without validating required fields\n- Skip input sanitization on user-provided query strings\n- Deploy without testing permission boundaries\n- Update production data without confirmation prompts\n- Mix different authentication methods in same session\n- Expose sensitive issue data in logs or error messages\n\n## Output Templates\n\nWhen implementing Atlassian MCP features, provide:\n1. MCP server configuration (JSON/environment vars)\n2. Query examples (JQL/CQL with explanations)\n3. Tool call implementation with error handling\n4. Authentication setup instructions\n5. Brief explanation of permission requirements\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/platform/atlassian-mcp/)\n","frontmatter":{"name":"atlassian-mcp","description":"Integrates with Atlassian products to manage project tracking and documentation via MCP protocol. Use when querying Jira issues with JQL filters, creating and updating tickets with custom fields, searching or editing Confluence pages with CQL, managing sprints and backlogs, setting up MCP server authentication, syncing documentation, or debugging Atlassian API integrations.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.2.0","domain":"platform","triggers":"Jira, Confluence, Atlassian, MCP, tickets, issues, wiki, JQL, CQL, sprint, backlog, project management","role":"expert","scope":"implementation","output-format":"code","related-skills":"mcp-developer, api-designer, security-reviewer"}},"isInternal":false,"tokens":1286,"sizeBytes":5568},{"name":"chaos-tools.md","path":"skills/chaos-engineer/references/chaos-tools.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/chaos-engineer/references/chaos-tools.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Chaos Engineering Tools & Automation\n\n## Chaos Monkey (Netflix)\n\n```python\n# Chaos Monkey configuration for Spinnaker\n{\n  \"enabled\": true,\n  \"schedule\": {\n    \"enabled\": true,\n    \"frequency\": 1,  # Run once per day\n    \"frequencyUnit\": \"DAYS\",\n    \"start\": \"09:00\",\n    \"end\": \"15:00\",\n    \"timezone\": \"America/Los_Angeles\"\n  },\n  \"grouping\": \"cluster\",\n  \"regionsAreIndependent\": true,\n  \"exceptions\": [\n    {\n      \"type\": \"Opt-In\",\n      \"account\": \"production\",\n      \"stack\": \"*\",\n      \"detail\": \"*\"\n    }\n  ],\n  \"minTimeBetweenKillsInWorkDays\": 2,\n  \"maxAppsPerDay\": 5,\n  \"clusters\": [\n    {\n      \"app\": \"myapp\",\n      \"stack\": \"production\",\n      \"enabled\": true,\n      \"regions\": [\"us-east-1\", \"us-west-2\"],\n      \"meanTimeBetweenKillsInWorkDays\": 2,\n      \"minTimeBetweenKillsInWorkDays\": 1,\n      \"maxTerminationsPerDay\": 1\n    }\n  ]\n}\n```\n\n```bash\n#!/bin/bash\n# Simpl Chaos Monkey implementation\n\nINSTANCE_COUNT=5\nKILL_PERCENTAGE=20\n\n# Get running instances from ASG\nINSTANCES=$(aws autoscaling describe-auto-scaling-groups \\\n  --auto-scaling-group-names my-asg \\\n  --query 'AutoScalingGroups[0].Instances[?LifecycleState==`InService`].InstanceId' \\\n  --output text)\n\n# Calculate number to terminate\nTOTAL=$(echo \"$INSTANCES\" | wc -w)\nTO_KILL=$(( TOTAL * KILL_PERCENTAGE / 100 ))\n\nif [ $TO_KILL -eq 0 ]; then\n  TO_KILL=1\nfi\n\n# Randomly select and terminate instances\necho \"$INSTANCES\" | tr ' ' '\\n' | shuf | head -n $TO_KILL | while read instance; do\n  echo \"Terminating instance: $instance\"\n  aws ec2 terminate-instances --instance-ids \"$instance\"\n  sleep 30  # Wait between terminations\ndone\n```\n\n## Gremlin Integration\n\n```python\nimport requests\nfrom typing import Literal\n\nclass GremlinClient:\n    def __init__(self, api_key: str, team_id: str):\n        self.api_key = api_key\n        self.team_id = team_id\n        self.base_url = \"https://api.gremlin.com/v1\"\n        self.headers = {\n            \"Authorization\": f\"Key {api_key}\",\n            \"Content-Type\": \"application/json\"\n        }\n\n    def create_cpu_attack(\n        self,\n        targets: list[str],\n        length: int = 60,\n        cores: int = 1,\n        percent: int = 50\n    ):\n        \"\"\"Launch CPU resource attack.\"\"\"\n        payload = {\n            \"command\": {\n                \"type\": \"cpu\",\n                \"args\": [\n                    \"-l\", str(length),\n                    \"-c\", str(cores),\n                    \"-p\", str(percent)\n                ]\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def create_network_attack(\n        self,\n        targets: list[str],\n        attack_type: Literal[\"latency\", \"packet_loss\", \"blackhole\"],\n        length: int = 60,\n        **kwargs\n    ):\n        \"\"\"Launch network attack.\"\"\"\n        args = [\"-l\", str(length)]\n\n        if attack_type == \"latency\":\n            # kwargs: delay_ms, jitter_ms\n            args.extend([\"-m\", str(kwargs.get('delay_ms', 100))])\n            if 'jitter_ms' in kwargs:\n                args.extend([\"-j\", str(kwargs['jitter_ms'])])\n\n        elif attack_type == \"packet_loss\":\n            # kwargs: percent\n            args.extend([\"-p\", str(kwargs.get('percent', 10))])\n\n        elif attack_type == \"blackhole\":\n            # kwargs: port, protocol\n            if 'port' in kwargs:\n                args.extend([\"--port\", str(kwargs['port'])])\n            if 'protocol' in kwargs:\n                args.extend([\"--protocol\", kwargs['protocol']])\n\n        payload = {\n            \"command\": {\n                \"type\": attack_type,\n                \"args\": args\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def halt_attack(self, attack_id: str):\n        \"\"\"Stop running attack.\"\"\"\n        response = requests.delete(\n            f\"{self.base_url}/attacks/{attack_id}\",\n            headers=self.headers\n        )\n        return response.status_code == 200\n\n    def create_scenario(self, name: str, attacks: list[dict]):\n        \"\"\"Create reusable attack scenario.\"\"\"\n        payload = {\n            \"name\": name,\n            \"description\": f\"Chaos scenario: {name}\",\n            \"graph\": {\n                \"nodes\": attacks\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/scenarios\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n# Example usage\ngremlin = GremlinClient(api_key=\"<gremlin-api-key>\", team_id=\"<gremlin-team-id>\")\n\n# CPU attack on specific containers\ngremlin.create_cpu_attack(\n    targets=[\"container-id-123\", \"container-id-456\"],\n    length=300,  # 5 minutes\n    cores=2,\n    percent=80\n)\n\n# Network latency attack\ngremlin.create_network_attack(\n    targets=[\"host-abc\"],\n    attack_type=\"latency\",\n    length=180,\n    delay_ms=500,\n    jitter_ms=100\n)\n```\n\n## CI/CD Integration\n\n```yaml\n# GitHub Actions workflow for chaos testing\nname: Chaos Engineering Tests\n\non:\n  schedule:\n    - cron: '0 10 * * 1-5'  # Weekdays at 10 AM\n  workflow_dispatch:  # Manual trigger\n\njobs:\n  chaos-tests:\n    runs-on: ubuntu-latest\n    environment: staging\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v3\n\n      - name: Setup kubectl\n        uses: azure/setup-kubectl@v3\n\n      - name: Configure AWS credentials\n        uses: aws-actions/configure-aws-credentials@v2\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: us-east-1\n\n      - name: Update kubeconfig\n        run: |\n          aws eks update-kubeconfig --name staging-cluster --region us-east-1\n\n      - name: Install Litmus\n        run: |\n          kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v2.14.0.yaml\n          kubectl wait --for=condition=Ready pods -l app.kubernetes.io/component=operator --timeout=300s\n\n      - name: Run pod-delete chaos experiment\n        run: |\n          kubectl apply -f .github/chaos/pod-delete-experiment.yaml\n          kubectl wait --for=condition=Complete chaosengine/pod-delete-chaos --timeout=600s\n\n      - name: Verify system recovery\n        run: |\n          # Check all pods are running\n          kubectl wait --for=condition=Ready pods -l app=myapp --timeout=300s\n\n          # Verify no error rate spike\n          ERROR_RATE=$(curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\"5..\\\"}[5m])\" | jq -r '.data.result[0].value[1]')\n\n          if (( $(echo \"$ERROR_RATE > 0.01\" | bc -l) )); then\n            echo \"Error rate too high: $ERROR_RATE\"\n            exit 1\n          fi\n\n      - name: Cleanup chaos resources\n        if: always()\n        run: |\n          kubectl delete chaosengine --all\n          kubectl delete chaosexperiments --all\n\n      - name: Report results to Slack\n        if: failure()\n        uses: slackapi/slack-github-action@v1\n        with:\n          payload: |\n            {\n              \"text\": \"Chaos test failed in staging\",\n              \"blocks\": [\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"Chaos engineering test failed. System did not recover properly.\"\n                  }\n                }\n              ]\n            }\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}\n```\n\n## Jenkins Pipeline\n\n```groovy\n// Jenkinsfile for chaos testing\npipeline {\n    agent any\n\n    parameters {\n        choice(\n            name: 'ENVIRONMENT',\n            choices: ['dev', 'staging'],\n            description: 'Target environment'\n        )\n        choice(\n            name: 'CHAOS_TYPE',\n            choices: ['pod-delete', 'network-latency', 'cpu-stress'],\n            description: 'Type of chaos experiment'\n        )\n        string(\n            name: 'DURATION',\n            defaultValue: '300',\n            description: 'Chaos duration in seconds'\n        )\n    }\n\n    stages {\n        stage('Pre-flight Check') {\n            steps {\n                script {\n                    // Verify steady state before chaos\n                    def errorRate = sh(\n                        script: '''\n                            curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\\\"5..\\\\\"}[5m])\" | jq -r '.data.result[0].value[1]'\n                        ''',\n                        returnStdout: true\n                    ).trim()\n\n                    if (errorRate.toFloat() > 0.01) {\n                        error(\"System not in steady state. Error rate: ${errorRate}\")\n                    }\n                }\n            }\n        }\n\n        stage('Run Chaos Experiment') {\n            steps {\n                script {\n                    def chaosManifest = \"\"\"\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: jenkins-chaos-${env.BUILD_NUMBER}\n  namespace: ${params.ENVIRONMENT}\nspec:\n  appinfo:\n    appns: '${params.ENVIRONMENT}'\n    applabel: 'app=myapp'\n    appkind: 'deployment'\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: ${params.CHAOS_TYPE}\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '${params.DURATION}'\n\"\"\"\n\n                    writeFile file: 'chaos-manifest.yaml', text: chaosManifest\n\n                    sh '''\n                        kubectl apply -f chaos-manifest.yaml\n                        kubectl wait --for=condition=Complete chaosengine/jenkins-chaos-${BUILD_NUMBER} --timeout=900s\n                    '''\n                }\n            }\n        }\n\n        stage('Verify Recovery') {\n            steps {\n                sh '''\n                    # Wait for system to stabilize\n                    sleep 60\n\n                    # Check pod status\n                    kubectl get pods -n ${ENVIRONMENT} -l app=myapp\n\n                    # Verify all pods running\n                    READY_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '[.items[] | select(.status.phase==\"Running\")] | length')\n                    TOTAL_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '.items | length')\n\n                    if [ \"$READY_PODS\" -ne \"$TOTAL_PODS\" ]; then\n                        echo \"Not all pods recovered: $READY_PODS/$TOTAL_PODS ready\"\n                        exit 1\n                    fi\n                '''\n            }\n        }\n\n        stage('Extract Learnings') {\n            steps {\n                script {\n                    // Get chaos result\n                    def chaosResult = sh(\n                        script: \"kubectl get chaosresult -n ${params.ENVIRONMENT} -o json\",\n                        returnStdout: true\n                    )\n\n                    // Parse and store results\n                    writeFile file: \"chaos-result-${env.BUILD_NUMBER}.json\", text: chaosResult\n\n                    // Archive results\n                    archiveArtifacts artifacts: \"chaos-result-${env.BUILD_NUMBER}.json\"\n                }\n            }\n        }\n    }\n\n    post {\n        always {\n            // Cleanup\n            sh '''\n                kubectl delete chaosengine jenkins-chaos-${BUILD_NUMBER} -n ${ENVIRONMENT} || true\n            '''\n        }\n\n        failure {\n            // Notify team\n            slackSend(\n                color: 'danger',\n                message: \"Chaos test failed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}\"\n            )\n        }\n\n        success {\n            slackSend(\n                color: 'good',\n                message: \"Chaos test passed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}. System recovered successfully.\"\n            )\n        }\n    }\n}\n```\n\n## Continuous Chaos Dashboard\n\n```python\n# Flask app for chaos monitoring dashboard\nfrom flask import Flask, render_template, jsonify\nimport requests\nfrom datetime import datetime, timedelta\n\napp = Flask(__name__)\n\nclass ChaosDashboard:\n    def __init__(self, prometheus_url: str):\n        self.prometheus = prometheus_url\n\n    def get_experiment_metrics(self, hours: int = 24):\n        \"\"\"Get chaos experiment results from last N hours.\"\"\"\n        end = datetime.now()\n        start = end - timedelta(hours=hours)\n\n        query = f'''\n            sum by (experiment, verdict) (\n                increase(litmuschaos_experiment_verdict[{hours}h])\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n    def get_mttr_trend(self):\n        \"\"\"Get MTTR trend over time.\"\"\"\n        query = '''\n            avg_over_time(\n                avg(\n                    time() - timestamp(\n                        kube_pod_status_phase{phase=\"Running\"} == 1\n                    )\n                )[7d:]\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n@app.route('/api/chaos-summary')\ndef chaos_summary():\n    dashboard = ChaosDashboard(prometheus_url=\"http://prometheus:9090\")\n\n    return jsonify({\n        \"experiments\": dashboard.get_experiment_metrics(hours=24),\n        \"mttr_trend\": dashboard.get_mttr_trend(),\n        \"timestamp\": datetime.now().isoformat()\n    })\n\nif __name__ == '__main__':\n    app.run(host='0.0.0.0', port=5000)\n```\n\n## Quick Reference\n\n| Tool | Use Case | Integration |\n|------|----------|-------------|\n| Chaos Monkey | Random instance termination | Spinnaker/AWS ASG |\n| Gremlin | SaaS chaos platform | API/Web UI |\n| Litmus | Kubernetes chaos | Kubectl/Helm |\n| Chaos Mesh | Advanced K8s chaos | CRDs/Dashboard |\n| Toxiproxy | Network proxy chaos | Docker/API |\n| Pumba | Container chaos | Docker CLI |\n","isInternal":false,"tokens":3288,"sizeBytes":14245},{"name":"experiment-design.md","path":"skills/chaos-engineer/references/experiment-design.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/chaos-engineer/references/experiment-design.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Chaos Experiment Design\n\n## Experiment Template\n\n```yaml\nname: \"Database Connection Pool Exhaustion\"\nhypothesis: \"When the database connection pool is exhausted, the application will gracefully degrade and return 503 errors without cascading failures\"\n\nsteady_state:\n  metrics:\n    - name: \"Error Rate\"\n      threshold: \"< 0.1%\"\n      source: \"prometheus\"\n      query: \"rate(http_requests_total{status=~'5..'}[5m])\"\n    - name: \"Latency P99\"\n      threshold: \"< 500ms\"\n      source: \"datadog\"\n    - name: \"Active Connections\"\n      threshold: \"> 10\"\n      query: \"pg_stat_activity_count\"\n\nblast_radius:\n  environment: \"staging\"\n  traffic_percentage: 10\n  duration_seconds: 300\n  max_error_rate: \"5%\"\n  auto_rollback: true\n\ninjection:\n  type: \"resource_exhaustion\"\n  target: \"database_connections\"\n  method: \"connection_leak\"\n  parameters:\n    leak_rate: 5  # connections per second\n    max_leaked: 50\n\nsafety:\n  rollback_triggers:\n    - \"error_rate > 5%\"\n    - \"manual_kill_switch\"\n    - \"duration_exceeded\"\n  rollback_time_limit_seconds: 30\n  alerts:\n    - slack: \"#chaos-engineering\"\n    - pagerduty: \"chaos-team\"\n\nsuccess_criteria:\n  - \"Circuit breakers activate within 10s\"\n  - \"503 errors returned (not 500)\"\n  - \"No cascading failures to other services\"\n  - \"System recovers within 60s of rollback\"\n```\n\n## Hypothesis Formulation\n\n```python\ndef create_hypothesis(component: str, failure: str, expected_behavior: str) -> dict:\n    \"\"\"\n    Create well-formed chaos hypothesis.\n\n    Format: \"Given [normal state], when [failure occurs],\n             then [expected behavior], measured by [metrics]\"\n    \"\"\"\n    return {\n        \"given\": f\"System is in steady state with {component} functioning normally\",\n        \"when\": f\"{failure} occurs\",\n        \"then\": expected_behavior,\n        \"measured_by\": [\n            \"Error rate remains below threshold\",\n            \"Latency stays within SLO\",\n            \"No data loss or corruption\",\n            \"Recovery time within RTO\"\n        ]\n    }\n\n# Example\nhypothesis = create_hypothesis(\n    component=\"payment service\",\n    failure=\"50% packet loss to payment gateway\",\n    expected_behavior=\"Requests timeout gracefully, retry queue activates, \"\n                     \"users see clear error messages\"\n)\n```\n\n## Blast Radius Control\n\n```python\nfrom dataclasses import dataclass\nfrom enum import Enum\n\nclass BlastRadiusLevel(Enum):\n    MINIMAL = \"single_instance_dev\"\n    LOW = \"single_instance_staging\"\n    MEDIUM = \"percentage_staging\"\n    HIGH = \"percentage_production\"\n    CRITICAL = \"full_production\"\n\n@dataclass\nclass BlastRadiusConfig:\n    level: BlastRadiusLevel\n    environment: str\n    target_percentage: float  # 0-100\n    canary_users: list[str]\n    feature_flag: str\n    auto_rollback: bool\n    max_duration_seconds: int\n\n    def validate(self):\n        \"\"\"Enforce safety rules.\"\"\"\n        if self.level == BlastRadiusLevel.CRITICAL:\n            raise ValueError(\"CRITICAL blast radius requires explicit approval\")\n\n        if self.environment == \"production\" and self.target_percentage > 10:\n            if not self.feature_flag or not self.auto_rollback:\n                raise ValueError(\"Production >10% requires feature flag AND auto-rollback\")\n\n        if self.max_duration_seconds > 600:\n            raise ValueError(\"Max duration cannot exceed 10 minutes without approval\")\n\n# Progressive blast radius expansion\ndef progressive_rollout() -> list[BlastRadiusConfig]:\n    return [\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MINIMAL,\n            environment=\"dev\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_dev\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.LOW,\n            environment=\"staging\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_staging\",\n            auto_rollback=True,\n            max_duration_seconds=600\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MEDIUM,\n            environment=\"production\",\n            target_percentage=1,\n            canary_users=[\"internal_team\"],\n            feature_flag=\"chaos_prod_canary\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        )\n    ]\n```\n\n## Safety Mechanisms\n\n```python\nimport asyncio\nfrom typing import Callable\n\nclass ChaosExperimentSafety:\n    def __init__(self, config: dict):\n        self.config = config\n        self.kill_switch_active = False\n        self.metrics = {}\n\n    async def run_with_safety(self, chaos_fn: Callable):\n        \"\"\"Execute chaos with automatic safety checks.\"\"\"\n        # Pre-flight checks\n        if not await self.verify_steady_state():\n            raise Exception(\"System not in steady state - aborting\")\n\n        # Set up rollback trigger\n        rollback_task = asyncio.create_task(self.monitor_for_rollback())\n        chaos_task = asyncio.create_task(chaos_fn())\n\n        try:\n            # Wait for either chaos completion or rollback trigger\n            done, pending = await asyncio.wait(\n                [chaos_task, rollback_task],\n                return_when=asyncio.FIRST_COMPLETED\n            )\n\n            if rollback_task in done:\n                # Rollback triggered - cancel chaos\n                chaos_task.cancel()\n                await self.rollback()\n\n        finally:\n            await self.ensure_system_recovery()\n\n    async def verify_steady_state(self) -> bool:\n        \"\"\"Check all steady state metrics are within threshold.\"\"\"\n        for metric in self.config['steady_state']['metrics']:\n            value = await self.query_metric(metric['query'])\n            if not self.within_threshold(value, metric['threshold']):\n                return False\n        return True\n\n    async def monitor_for_rollback(self):\n        \"\"\"Continuously monitor for rollback triggers.\"\"\"\n        start_time = asyncio.get_event_loop().time()\n\n        while True:\n            # Check duration limit\n            if asyncio.get_event_loop().time() - start_time > \\\n               self.config['blast_radius']['duration_seconds']:\n                return \"duration_exceeded\"\n\n            # Check manual kill switch\n            if self.kill_switch_active:\n                return \"manual_kill_switch\"\n\n            # Check error rate\n            error_rate = await self.query_metric(\"error_rate\")\n            if error_rate > float(self.config['blast_radius']['max_error_rate'].strip('%')):\n                return \"error_rate_exceeded\"\n\n            await asyncio.sleep(5)  # Check every 5 seconds\n```\n\n## Quick Reference\n\n| Phase | Key Actions | Time Limit |\n|-------|-------------|------------|\n| Design | Hypothesis, metrics, blast radius | 1 hour |\n| Review | Team review, safety check | 30 min |\n| Prepare | Setup monitoring, rollback | 1 hour |\n| Execute | Run experiment, monitor | 5-10 min |\n| Rollback | Restore steady state | < 30 sec |\n| Learn | Document findings, plan fixes | 2 hours |\n","isInternal":false,"tokens":1522,"sizeBytes":7031},{"name":"game-days.md","path":"skills/chaos-engineer/references/game-days.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/chaos-engineer/references/game-days.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Game Day Planning & Execution\n\n## Game Day Planning Template\n\n```yaml\ngame_day:\n  name: \"Database Failover Drill\"\n  date: \"2025-01-15\"\n  time: \"10:00-12:00 PST\"\n  environment: \"staging\"  # Start in staging\n\n  objectives:\n    - \"Verify RDS failover to standby in under 2 minutes\"\n    - \"Validate application auto-reconnect logic\"\n    - \"Test monitoring and alerting effectiveness\"\n    - \"Practice incident response procedures\"\n\n  participants:\n    facilitator: \"chaos-engineer@company.com\"\n    observers:\n      - \"sre-team@company.com\"\n      - \"dev-team@company.com\"\n    responders:\n      - \"on-call-engineer@company.com\"\n      - \"database-admin@company.com\"\n    stakeholders:\n      - \"engineering-manager@company.com\"\n\n  scenarios:\n    - name: \"Primary database instance failure\"\n      duration_minutes: 30\n      steps:\n        - action: \"Force RDS instance reboot with failover\"\n          expected: \"Failover to standby in <2 min\"\n          success_criteria:\n            - \"Downtime < 2 minutes\"\n            - \"No data loss\"\n            - \"Alerts fired correctly\"\n\n    - name: \"Network partition to database\"\n      duration_minutes: 20\n      steps:\n        - action: \"Block network traffic to RDS security group\"\n          expected: \"Application switches to read replica\"\n          success_criteria:\n            - \"Read-only mode activated\"\n            - \"User-facing error messages clear\"\n\n  communication_plan:\n    announcement_channel: \"#game-day-announcements\"\n    war_room: \"Zoom link: https://...\"\n    status_updates_every: \"5 minutes\"\n    escalation_contacts:\n      - name: \"VP Engineering\"\n        phone: \"+1-555-0100\"\n        threshold: \"downtime > 5 minutes\"\n\n  rollback_plan:\n    automatic_rollback_triggers:\n      - \"production traffic affected\"\n      - \"customer complaints received\"\n      - \"error_rate > 10%\"\n    manual_rollback_command: \"aws rds reboot-db-instance --db-instance-identifier primary --force-failover\"\n    rollback_time_limit_seconds: 60\n\n  success_metrics:\n    - metric: \"RTO (Recovery Time Objective)\"\n      target: \"< 2 minutes\"\n      measurement: \"time between failure and full recovery\"\n    - metric: \"Alert accuracy\"\n      target: \"100%\"\n      measurement: \"all expected alerts fired\"\n    - metric: \"Team response time\"\n      target: \"< 5 minutes\"\n      measurement: \"time to acknowledge incident\"\n\n  post_mortem:\n    scheduled_for: \"2025-01-16 14:00\"\n    template: \"game-day-retro.md\"\n    required_attendees: \"all participants\"\n```\n\n## Game Day Runbook\n\n```markdown\n# Database Failover Game Day Runbook\n\n**Date**: January 15, 2025\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Pre-Game Checklist (T-30 min)\n\n- [ ] Verify all participants joined war room\n- [ ] Confirm monitoring dashboards accessible\n- [ ] Test rollback procedures work\n- [ ] Announce game day start in #engineering\n- [ ] Verify staging environment healthy\n- [ ] Set up screen recording for timeline\n- [ ] Prepare incident timeline spreadsheet\n\n## Timeline\n\n### 10:00 - Introduction (10 min)\n- Facilitator explains objectives\n- Review scenarios and success criteria\n- Confirm roles and communication channels\n- Remind everyone: this is a learning exercise\n\n### 10:10 - Scenario 1: Primary DB Failure (30 min)\n\n**T+0 (10:10)** - Inject failure\n```bash\naws rds reboot-db-instance \\\n  --db-instance-identifier staging-primary \\\n  --force-failover\n```\n\n**Expected Timeline**:\n- T+0: Reboot initiated\n- T+30s: Primary becomes unavailable\n- T+60s: DNS updated to standby\n- T+90s: Application reconnects\n- T+120s: Full recovery\n\n**Observer Tasks**:\n- [ ] Record exact time of failure injection\n- [ ] Monitor application error logs\n- [ ] Track alert notifications\n- [ ] Document team response actions\n- [ ] Screenshot dashboard states\n\n**Questions to Answer**:\n- How long until first alert?\n- Did application auto-reconnect?\n- Were customers impacted?\n- What manual interventions needed?\n\n### 10:40 - Debrief Scenario 1 (10 min)\n- What went well?\n- What could improve?\n- Any surprises?\n- Action items identified\n\n### 10:50 - Scenario 2: Network Partition (20 min)\n\n**T+0 (10:50)** - Inject failure\n```bash\n# Block database security group ingress\naws ec2 revoke-security-group-ingress \\\n  --group-id sg-xxxxx \\\n  --protocol tcp \\\n  --port 5432 \\\n  --cidr 10.0.0.0/16\n```\n\n**Expected Behavior**:\n- Connection timeouts occur\n- Circuit breaker opens\n- Read-only mode activates\n- Clear error messages shown\n\n**Observer Tasks**:\n- [ ] Monitor circuit breaker state\n- [ ] Verify read-replica failover\n- [ ] Check user-facing error messages\n- [ ] Track degraded service duration\n\n### 11:10 - Debrief Scenario 2 (10 min)\n\n### 11:20 - Scenario 3: Surprise! (20 min)\n\n**Facilitator Note**: Don't announce this scenario details beforehand.\nTest true incident response capability.\n\n**Hidden Scenario**: Combination failure\n1. Database connection pool leak\n2. Simultaneous cache invalidation\n\n```python\n# Connection leak simulator\nimport psycopg2\nconnections = []\nfor i in range(100):\n    conn = psycopg2.connect(DATABASE_URL)\n    connections.append(conn)\n    # Intentionally don't close\n```\n\n**Observer Tasks**:\n- [ ] How long to identify root cause?\n- [ ] Communication effectiveness\n- [ ] Cross-team coordination\n- [ ] Escalation decisions\n\n### 11:40 - Final Debrief & Wrap-up (20 min)\n\n**Debrief Questions**:\n1. What worked well?\n2. What didn't work?\n3. What surprised us?\n4. What are our top 3 action items?\n5. When should we run this again?\n\n## Post-Game Checklist\n\n- [ ] Restore all services to normal state\n- [ ] Verify no lingering issues\n- [ ] Collect all observer notes\n- [ ] Export metrics and dashboards\n- [ ] Schedule post-mortem meeting\n- [ ] Send thank-you to participants\n- [ ] Create action item tickets\n- [ ] Update runbooks based on learnings\n```\n\n## Game Day Observation Template\n\n```python\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom typing import List\n\n@dataclass\nclass GameDayObservation:\n    timestamp: datetime\n    observer: str\n    scenario: str\n    observation: str\n    category: str  # technical, process, communication, surprise\n    severity: str  # info, concern, critical\n    photo_url: str = \"\"\n\n@dataclass\nclass GameDayMetrics:\n    scenario_name: str\n    start_time: datetime\n    end_time: datetime\n\n    # Technical metrics\n    time_to_detect_seconds: float\n    time_to_respond_seconds: float\n    time_to_recover_seconds: float\n    error_rate_peak: float\n    alerts_fired: List[str] = field(default_factory=list)\n    alerts_missed: List[str] = field(default_factory=list)\n\n    # Team metrics\n    responders_involved: int\n    escalations_needed: int\n    communication_gaps: List[str] = field(default_factory=list)\n\n    # Success criteria\n    met_rto: bool = False\n    met_rpo: bool = False\n    zero_customer_impact: bool = False\n\n    def calculate_mttr(self) -> float:\n        \"\"\"Mean Time To Recovery\"\"\"\n        return (self.end_time - self.start_time).total_seconds()\n\n    def success_rate(self) -> float:\n        \"\"\"Percentage of success criteria met\"\"\"\n        criteria = [\n            self.met_rto,\n            self.met_rpo,\n            self.zero_customer_impact,\n            len(self.alerts_missed) == 0\n        ]\n        return sum(criteria) / len(criteria) * 100\n\n# Example usage\nmetrics = GameDayMetrics(\n    scenario_name=\"Database Failover\",\n    start_time=datetime(2025, 1, 15, 10, 10, 0),\n    end_time=datetime(2025, 1, 15, 10, 12, 30),\n    time_to_detect_seconds=15.0,\n    time_to_respond_seconds=45.0,\n    time_to_recover_seconds=150.0,\n    error_rate_peak=0.05,\n    alerts_fired=[\"DatabaseConnectionError\", \"HighLatency\"],\n    alerts_missed=[\"FailoverInitiated\"],\n    responders_involved=3,\n    escalations_needed=0,\n    met_rto=True,\n    met_rpo=True,\n    zero_customer_impact=True\n)\n\nprint(f\"MTTR: {metrics.calculate_mttr()}s\")\nprint(f\"Success Rate: {metrics.success_rate()}%\")\n```\n\n## Surprise Scenarios Library\n\n```yaml\n# Keep these secret until game day!\nsurprise_scenarios:\n  - name: \"Cascading Failure\"\n    description: \"Primary failure triggers secondary issue\"\n    injection:\n      - \"Database failover (expected)\"\n      - \"Cache eviction due to new primary IP (surprise!)\"\n    learning_goals:\n      - \"Do we understand our dependencies?\"\n      - \"Can we handle multiple simultaneous issues?\"\n\n  - name: \"Monitoring Blind Spot\"\n    description: \"Failure that doesn't trigger alerts\"\n    injection:\n      - \"Gradual connection pool leak\"\n      - \"No immediate alerts fire\"\n    learning_goals:\n      - \"How do we discover issues without alerts?\"\n      - \"Do we have adequate monitoring coverage?\"\n\n  - name: \"Documentation Failure\"\n    description: \"Runbook is outdated or incorrect\"\n    setup:\n      - \"Modify runbook to have incorrect commands\"\n      - \"Or remove runbook entirely\"\n    learning_goals:\n      - \"Can team problem-solve without docs?\"\n      - \"How quickly can we update documentation?\"\n\n  - name: \"Key Person Unavailable\"\n    description: \"Subject matter expert is unreachable\"\n    setup:\n      - \"Ask SME to not respond for 15 minutes\"\n    learning_goals:\n      - \"Is knowledge properly distributed?\"\n      - \"Can team succeed without specific person?\"\n\n  - name: \"Partial Degradation\"\n    description: \"Service works but slowly\"\n    injection:\n      - \"Add 5 second latency instead of complete failure\"\n    learning_goals:\n      - \"Do we detect performance degradation?\"\n      - \"What are our latency SLOs?\"\n```\n\n## Post-Game Report Template\n\n```markdown\n# Game Day Report: Database Failover\n\n**Date**: January 15, 2025\n**Participants**: 12\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Executive Summary\n\nConducted database failover game day to test RDS high availability and\napplication resilience. Successfully failed over database in 2.5 minutes\n(target: 2 min). Discovered 3 critical gaps in monitoring and 2 process\nimprovements needed.\n\n## Metrics\n\n| Metric | Target | Actual | Status |\n|--------|--------|--------|--------|\n| Time to Detect | < 30s | 15s | PASS |\n| Time to Respond | < 5min | 4min 20s | PASS |\n| Time to Recover | < 2min | 2min 30s | FAIL |\n| Alert Accuracy | 100% | 66% | FAIL |\n| Zero Customer Impact | Yes | Yes | PASS |\n\n## What Went Well\n\n1. Team responded quickly (4m 20s vs 5m target)\n2. Runbooks were accurate and helpful\n3. Communication was clear and frequent\n4. No customer impact during any scenario\n5. Application auto-reconnect worked perfectly\n\n## What Didn't Go Well\n\n1. Missing alert for failover initiation\n2. Took 30s longer than target to recover\n3. Connection pool exhaustion not detected\n4. Dashboard didn't show replica lag clearly\n5. Escalation contacts list was outdated\n\n## Surprises\n\n1. Cache invalidation cascaded from DB failover (unexpected)\n2. Read replica had 45s replication lag we didn't know about\n3. Application retried too aggressively during failover\n4. Team found a workaround we hadn't documented\n\n## Action Items\n\n| Action | Owner | Due Date | Priority |\n|--------|-------|----------|----------|\n| Add alert for RDS failover events | @sre-team | Jan 20 | P0 |\n| Update dashboard with replica lag | @platform | Jan 22 | P1 |\n| Document cache invalidation behavior | @dev-team | Jan 25 | P1 |\n| Add connection pool monitoring | @sre-team | Jan 27 | P0 |\n| Update escalation contact list | @manager | Jan 18 | P2 |\n| Tune application retry backoff | @dev-team | Feb 1 | P1 |\n\n## Lessons Learned\n\n1. **Monitoring Gaps**: We had blind spots in replica monitoring\n2. **Cascading Effects**: DB changes affect cache in non-obvious ways\n3. **Team Knowledge**: Cross-training is working well\n4. **Documentation**: Runbooks saved time, keep them updated\n\n## Next Game Day\n\n**Proposed Date**: March 15, 2025\n**Scenario**: Multi-region failover\n**Scope**: Production (with safeguards)\n\n## Appendix\n\n- Full timeline spreadsheet: [link]\n- Screen recordings: [link]\n- Metrics dashboard export: [link]\n- Raw observation notes: [link]\n```\n\n## Quick Reference\n\n| Phase | Duration | Key Activities |\n|-------|----------|----------------|\n| Planning | 2 weeks | Define scenarios, invite participants |\n| Pre-game | 30 min | Setup, verify environment, brief team |\n| Execution | 2 hours | Run scenarios, observe, document |\n| Debrief | 30 min | Immediate learnings, quick wins |\n| Post-mortem | 1 week later | Detailed analysis, action items |\n| Follow-up | 1 month | Verify improvements, plan next game day |\n","isInternal":false,"tokens":3069,"sizeBytes":12355},{"name":"infrastructure-chaos.md","path":"skills/chaos-engineer/references/infrastructure-chaos.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/chaos-engineer/references/infrastructure-chaos.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Infrastructure Chaos Engineering\n\n## Network Latency Injection\n\n```python\n# Using toxiproxy for network chaos\nimport requests\nfrom typing import Literal\n\nclass ToxiproxyClient:\n    def __init__(self, host: str = \"localhost:8474\"):\n        self.base_url = f\"http://{host}\"\n\n    def create_proxy(self, name: str, listen: str, upstream: str):\n        \"\"\"Create proxy to inject failures.\"\"\"\n        response = requests.post(f\"{self.base_url}/proxies\", json={\n            \"name\": name,\n            \"listen\": listen,\n            \"upstream\": upstream,\n            \"enabled\": True\n        })\n        return response.json()\n\n    def add_latency(self, proxy: str, latency_ms: int, jitter_ms: int = 0):\n        \"\"\"Add latency toxic.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"latency\",\n                \"type\": \"latency\",\n                \"attributes\": {\n                    \"latency\": latency_ms,\n                    \"jitter\": jitter_ms\n                }\n            }\n        )\n\n    def add_bandwidth_limit(self, proxy: str, rate_kb: int):\n        \"\"\"Limit bandwidth.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"bandwidth\",\n                \"type\": \"bandwidth\",\n                \"attributes\": {\"rate\": rate_kb}\n            }\n        )\n\n    def add_timeout(self, proxy: str, timeout_ms: int):\n        \"\"\"Add connection timeout.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"timeout\",\n                \"type\": \"timeout\",\n                \"attributes\": {\"timeout\": timeout_ms}\n            }\n        )\n\n# Example usage\ntoxiproxy = ToxiproxyClient()\n\n# Create proxy to database\ntoxiproxy.create_proxy(\n    name=\"postgres\",\n    listen=\"0.0.0.0:5433\",\n    upstream=\"postgres:5432\"\n)\n\n# Inject 200ms latency with 50ms jitter\ntoxiproxy.add_latency(\"postgres\", latency_ms=200, jitter_ms=50)\n```\n\n## AWS Zone Failure Simulation\n\n```python\nimport boto3\nfrom datetime import datetime, timedelta\n\nclass AWSChaosSimulator:\n    def __init__(self, region: str):\n        self.ec2 = boto3.client('ec2', region_name=region)\n        self.asg = boto3.client('autoscaling', region_name=region)\n        self.elb = boto3.client('elbv2', region_name=region)\n\n    def simulate_az_failure(\n        self,\n        availability_zone: str,\n        asg_name: str,\n        duration_minutes: int = 10\n    ):\n        \"\"\"\n        Simulate AZ failure by terminating instances in specific AZ.\n        Auto Scaling Group will launch replacements in other AZs.\n        \"\"\"\n        # Find instances in target AZ\n        instances = self.ec2.describe_instances(Filters=[\n            {'Name': 'tag:aws:autoscaling:groupName', 'Values': [asg_name]},\n            {'Name': 'availability-zone', 'Values': [availability_zone]},\n            {'Name': 'instance-state-name', 'Values': ['running']}\n        ])\n\n        instance_ids = [\n            i['InstanceId']\n            for r in instances['Reservations']\n            for i in r['Instances']\n        ]\n\n        if not instance_ids:\n            return {\"status\": \"no_instances\", \"instances\": []}\n\n        # Suspend AZ-specific scaling activities\n        self.asg.suspend_processes(\n            AutoScalingGroupName=asg_name,\n            ScalingProcesses=['AZRebalance']\n        )\n\n        # Terminate instances to simulate AZ failure\n        self.ec2.terminate_instances(InstanceIds=instance_ids)\n\n        return {\n            \"status\": \"simulated\",\n            \"availability_zone\": availability_zone,\n            \"terminated_instances\": instance_ids,\n            \"recovery_time\": datetime.now() + timedelta(minutes=duration_minutes)\n        }\n\n    def drain_az_from_load_balancer(\n        self,\n        target_group_arn: str,\n        availability_zone: str\n    ):\n        \"\"\"Remove AZ from load balancer to simulate zone failure.\"\"\"\n        # Get current target health\n        health = self.elb.describe_target_health(\n            TargetGroupArn=target_group_arn\n        )\n\n        # Find targets in AZ\n        targets_to_deregister = []\n        for target in health['TargetHealthDescriptions']:\n            # Get instance details\n            instance = self.ec2.describe_instances(\n                InstanceIds=[target['Target']['Id']]\n            )\n            if instance['Reservations'][0]['Instances'][0]['Placement']['AvailabilityZone'] == availability_zone:\n                targets_to_deregister.append(target['Target'])\n\n        # Deregister targets\n        if targets_to_deregister:\n            self.elb.deregister_targets(\n                TargetGroupArn=target_group_arn,\n                Targets=targets_to_deregister\n            )\n\n        return {\n            \"deregistered_targets\": len(targets_to_deregister),\n            \"availability_zone\": availability_zone\n        }\n```\n\n## Server Resource Exhaustion\n\n```bash\n#!/bin/bash\n# CPU stress test using stress-ng\n\n# Install stress-ng\nsudo apt-get install -y stress-ng\n\n# Stress CPU - use 80% of available cores for 5 minutes\nstress-ng --cpu $(nproc --all) --cpu-load 80 --timeout 5m\n\n# Memory stress - consume 70% of available memory\nTOTAL_MEM_MB=$(free -m | awk 'NR==2{print $2}')\nSTRESS_MEM_MB=$((TOTAL_MEM_MB * 70 / 100))\nstress-ng --vm 1 --vm-bytes ${STRESS_MEM_MB}M --timeout 5m\n\n# Disk I/O stress - 4 workers doing sequential writes\nstress-ng --hdd 4 --hdd-bytes 1G --timeout 5m\n\n# Network bandwidth saturation\n# Using iperf3 to saturate network\niperf3 -c target-server -t 300 -P 10  # 10 parallel streams for 5 minutes\n```\n\n## Docker Container Chaos with Pumba\n\n```bash\n#!/bin/bash\n# Pumba - chaos testing for Docker\n\n# Kill random container every 30 seconds\npumba --interval 30s kill --signal SIGKILL \"re2:^myapp\"\n\n# Pause container for 15 seconds, then resume\npumba pause --duration 15s myapp-container\n\n# Add network delay to container\npumba netem \\\n  --duration 5m \\\n  --interface eth0 \\\n  delay \\\n    --time 300 \\\n    --jitter 50 \\\n  myapp-container\n\n# Packet loss - drop 20% of packets\npumba netem \\\n  --duration 5m \\\n  loss \\\n    --percent 20 \\\n  myapp-container\n\n# Limit bandwidth to 1Mbps\npumba netem \\\n  --duration 5m \\\n  rate \\\n    --rate 1mbit \\\n  myapp-container\n\n# Stop all containers matching pattern for 2 minutes\npumba stop --duration 2m \"re2:^production-.*\"\n```\n\n## DNS Failure Simulation\n\n```python\n# Using dnsmasq or editing /etc/hosts for DNS chaos\n\nimport subprocess\nimport time\nfrom contextlib import contextmanager\n\nclass DNSChaos:\n    @staticmethod\n    @contextmanager\n    def block_domain(domain: str, duration_seconds: int = 60):\n        \"\"\"Block DNS resolution for domain by pointing to localhost.\"\"\"\n        try:\n            # Add entry to /etc/hosts\n            subprocess.run([\n                'sudo', 'sh', '-c',\n                f'echo \"127.0.0.1 {domain}\" >> /etc/hosts'\n            ], check=True)\n\n            print(f\"Blocked DNS for {domain}\")\n            yield\n\n        finally:\n            # Wait for duration\n            time.sleep(duration_seconds)\n\n            # Remove entry from /etc/hosts\n            subprocess.run([\n                'sudo', 'sed', '-i',\n                f'/127.0.0.1 {domain}/d',\n                '/etc/hosts'\n            ], check=True)\n\n            print(f\"Restored DNS for {domain}\")\n\n    @staticmethod\n    def add_dns_latency(domain: str, latency_ms: int):\n        \"\"\"Add latency to DNS queries using dnsmasq.\"\"\"\n        config = f\"\"\"\n        # Add to /etc/dnsmasq.conf\n        address=/{domain}/127.0.0.1\n        min-cache-ttl=0\n\n        # Restart dnsmasq with delay\n        \"\"\"\n        return config\n\n# Usage\nwith DNSChaos.block_domain('api.external-service.com', duration_seconds=120):\n    # Run tests while DNS is blocked\n    print(\"DNS blocked - testing fallback behavior\")\n```\n\n## Certificate Expiry Simulation\n\n```python\nfrom datetime import datetime, timedelta\nfrom cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom cryptography.hazmat.primitives import hashes, serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\n\ndef create_expired_certificate(\n    common_name: str,\n    expired_days_ago: int = 1\n) -> tuple[bytes, bytes]:\n    \"\"\"\n    Create an expired TLS certificate for chaos testing.\n    Returns (certificate_pem, private_key_pem)\n    \"\"\"\n    # Generate private key\n    private_key = rsa.generate_private_key(\n        public_exponent=65537,\n        key_size=2048\n    )\n\n    # Certificate valid from 365 days ago to `expired_days_ago` ago\n    not_valid_before = datetime.utcnow() - timedelta(days=365)\n    not_valid_after = datetime.utcnow() - timedelta(days=expired_days_ago)\n\n    subject = issuer = x509.Name([\n        x509.NameAttribute(NameOID.COMMON_NAME, common_name)\n    ])\n\n    cert = x509.CertificateBuilder().subject_name(\n        subject\n    ).issuer_name(\n        issuer\n    ).public_key(\n        private_key.public_key()\n    ).serial_number(\n        x509.random_serial_number()\n    ).not_valid_before(\n        not_valid_before\n    ).not_valid_after(\n        not_valid_after\n    ).sign(private_key, hashes.SHA256())\n\n    # Serialize to PEM\n    cert_pem = cert.public_bytes(serialization.Encoding.PEM)\n    key_pem = private_key.private_bytes(\n        encoding=serialization.Encoding.PEM,\n        format=serialization.PrivateFormat.PKCS8,\n        encryption_algorithm=serialization.NoEncryption()\n    )\n\n    return cert_pem, key_pem\n```\n\n## Quick Reference\n\n| Failure Type | Tool | Command/Method |\n|--------------|------|----------------|\n| Network latency | toxiproxy | `add_latency(proxy, ms)` |\n| Packet loss | toxiproxy/pumba | `loss --percent 20` |\n| AZ failure | AWS API | `simulate_az_failure(az, asg)` |\n| CPU stress | stress-ng | `--cpu N --cpu-load 80` |\n| Memory exhaustion | stress-ng | `--vm 1 --vm-bytes XG` |\n| Container kill | pumba | `kill --signal SIGKILL` |\n| DNS failure | /etc/hosts | Block domain resolution |\n| Cert expiry | cryptography | Generate expired cert |\n","isInternal":false,"tokens":2387,"sizeBytes":10035},{"name":"kubernetes-chaos.md","path":"skills/chaos-engineer/references/kubernetes-chaos.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/chaos-engineer/references/kubernetes-chaos.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Kubernetes Chaos Engineering\n\n## Litmus Chaos - ChaosEngine\n\n```yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: nginx-chaos\n  namespace: default\nspec:\n  # Application information\n  appinfo:\n    appns: 'default'\n    applabel: 'app=nginx'\n    appkind: 'deployment'\n\n  # Chaos service account\n  chaosServiceAccount: litmus-admin\n\n  # Experiments to run\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            # Total chaos duration\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n\n            # Chaos interval (delete pod every X seconds)\n            - name: CHAOS_INTERVAL\n              value: '10'\n\n            # Force delete pods\n            - name: FORCE\n              value: 'true'\n\n            # Number of pods to delete\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n    - name: pod-network-latency\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: NETWORK_LATENCY\n              value: '2000'  # 2 second latency\n            - name: JITTER\n              value: '200'   # 200ms jitter\n            - name: CONTAINER_RUNTIME\n              value: 'containerd'\n\n    - name: pod-cpu-hog\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: CPU_CORES\n              value: '2'\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n  # Monitor application during chaos\n  monitoring: true\n\n  # Job cleanup policy\n  jobCleanUpPolicy: 'delete'\n```\n\n## Chaos Mesh Experiments\n\n```yaml\n# Network partition between services\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: partition-frontend-backend\n  namespace: chaos-testing\nspec:\n  action: partition\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'frontend'\n  direction: to\n  target:\n    mode: all\n    selector:\n      namespaces:\n        - production\n      labelSelectors:\n        'app': 'backend'\n  duration: '5m'\n\n---\n# Pod failure - kill random pods\napiVersion: chaos-mesh.org/v1alpha1\nkind: PodChaos\nmetadata:\n  name: pod-failure\n  namespace: chaos-testing\nspec:\n  action: pod-failure\n  mode: one  # one, all, fixed, fixed-percent, random-max-percent\n  duration: '30s'\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'payment-service'\n  scheduler:\n    cron: '@every 10m'  # Run every 10 minutes\n\n---\n# Network bandwidth limitation\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: bandwidth-limit\nspec:\n  action: bandwidth\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'tier': 'backend'\n  bandwidth:\n    rate: '1mbps'\n    limit: 20000\n    buffer: 10000\n  duration: '5m'\n\n---\n# Disk I/O stress\napiVersion: chaos-mesh.org/v1alpha1\nkind: IOChaos\nmetadata:\n  name: io-latency\nspec:\n  action: latency\n  mode: one\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'database'\n  volumePath: /var/lib/postgresql/data\n  path: /var/lib/postgresql/data/**/*\n  delay: '100ms'\n  percent: 50  # 50% of I/O operations affected\n  duration: '5m'\n\n---\n# DNS chaos - random DNS errors\napiVersion: chaos-mesh.org/v1alpha1\nkind: DNSChaos\nmetadata:\n  name: dns-random-error\nspec:\n  action: random\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'api-gateway'\n  patterns:\n    - external-api.example.com\n    - *.third-party-service.com\n  duration: '3m'\n```\n\n## Node Drain Simulation\n\n```python\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport time\n\nclass K8sNodeChaos:\n    def __init__(self):\n        config.load_kube_config()\n        self.core_v1 = client.CoreV1Api()\n        self.apps_v1 = client.AppsV1Api()\n\n    def cordon_node(self, node_name: str):\n        \"\"\"Mark node as unschedulable.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": True\n            }\n        }\n        try:\n            self.core_v1.patch_node(node_name, body)\n            print(f\"Node {node_name} cordoned\")\n        except ApiException as e:\n            print(f\"Failed to cordon node: {e}\")\n\n    def drain_node(\n        self,\n        node_name: str,\n        grace_period_seconds: int = 30,\n        delete_local_data: bool = True\n    ):\n        \"\"\"\n        Drain node by evicting all pods.\n        Simulates node failure or maintenance.\n        \"\"\"\n        # First, cordon the node\n        self.cordon_node(node_name)\n\n        # Get all pods on the node\n        field_selector = f\"spec.nodeName={node_name}\"\n        pods = self.core_v1.list_pod_for_all_namespaces(\n            field_selector=field_selector\n        )\n\n        # Evict each pod\n        for pod in pods.items:\n            # Skip DaemonSet pods and mirror pods\n            if pod.metadata.owner_references:\n                for owner in pod.metadata.owner_references:\n                    if owner.kind in ['DaemonSet', 'Node']:\n                        continue\n\n            # Create eviction\n            eviction = client.V1Eviction(\n                metadata=client.V1ObjectMeta(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace\n                ),\n                delete_options=client.V1DeleteOptions(\n                    grace_period_seconds=grace_period_seconds\n                )\n            )\n\n            try:\n                self.core_v1.create_namespaced_pod_eviction(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace,\n                    body=eviction\n                )\n                print(f\"Evicted pod {pod.metadata.name}\")\n            except ApiException as e:\n                if e.status == 429:  # Too many requests\n                    print(f\"Pod {pod.metadata.name} protected by PDB\")\n                else:\n                    print(f\"Failed to evict {pod.metadata.name}: {e}\")\n\n        return {\"node\": node_name, \"status\": \"drained\"}\n\n    def uncordon_node(self, node_name: str):\n        \"\"\"Mark node as schedulable again.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": False\n            }\n        }\n        self.core_v1.patch_node(node_name, body)\n        print(f\"Node {node_name} uncordoned\")\n\n    def simulate_node_failure(\n        self,\n        node_name: str,\n        duration_seconds: int = 300\n    ):\n        \"\"\"\n        Simulate complete node failure.\n        Drain node, wait, then restore.\n        \"\"\"\n        print(f\"Simulating failure of node {node_name}\")\n\n        # Drain the node\n        self.drain_node(node_name)\n\n        # Wait for duration\n        print(f\"Node failed for {duration_seconds} seconds\")\n        time.sleep(duration_seconds)\n\n        # Restore node\n        self.uncordon_node(node_name)\n        print(\"Node restored\")\n```\n\n## Pod Autoscaling Chaos\n\n```yaml\n# Test HPA behavior under load\napiVersion: chaos-mesh.org/v1alpha1\nkind: StressChaos\nmetadata:\n  name: stress-hpa-trigger\nspec:\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'web-server'\n  stressors:\n    cpu:\n      workers: 2\n      load: 80  # 80% CPU load\n  duration: '10m'\n\n---\n# Verify HPA scaling response\napiVersion: v1\nkind: Pod\nmetadata:\n  name: chaos-verification\nspec:\n  containers:\n  - name: verifier\n    image: bitnami/kubectl:latest\n    command:\n      - /bin/bash\n      - -c\n      - |\n        # Monitor HPA scaling\n        while true; do\n          echo \"=== HPA Status ===\"\n          kubectl get hpa web-server -o json | \\\n            jq '.status | {current: .currentReplicas, desired: .desiredReplicas, cpu: .currentCPUUtilizationPercentage}'\n\n          echo \"=== Pod Count ===\"\n          kubectl get pods -l app=web-server --no-headers | wc -l\n\n          sleep 10\n        done\n```\n\n## Custom Resource Chaos\n\n```python\n# Python script to test custom CRD resilience\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport random\nimport time\n\ndef chaos_delete_custom_resources(\n    group: str,\n    version: str,\n    plural: str,\n    namespace: str,\n    percentage: int = 30\n):\n    \"\"\"\n    Randomly delete custom resources to test operator resilience.\n\n    Args:\n        group: API group (e.g., 'app.example.com')\n        version: API version (e.g., 'v1')\n        plural: Resource plural name (e.g., 'myapps')\n        namespace: Namespace to target\n        percentage: Percentage of resources to delete (0-100)\n    \"\"\"\n    config.load_kube_config()\n    custom_api = client.CustomObjectsApi()\n\n    try:\n        # List all custom resources\n        resources = custom_api.list_namespaced_custom_object(\n            group=group,\n            version=version,\n            namespace=namespace,\n            plural=plural\n        )\n\n        items = resources.get('items', [])\n        if not items:\n            print(\"No resources found\")\n            return\n\n        # Calculate number to delete\n        count_to_delete = max(1, int(len(items) * percentage / 100))\n\n        # Randomly select resources\n        to_delete = random.sample(items, count_to_delete)\n\n        print(f\"Deleting {count_to_delete} of {len(items)} {plural}\")\n\n        # Delete selected resources\n        for resource in to_delete:\n            name = resource['metadata']['name']\n            try:\n                custom_api.delete_namespaced_custom_object(\n                    group=group,\n                    version=version,\n                    namespace=namespace,\n                    plural=plural,\n                    name=name,\n                    body=client.V1DeleteOptions()\n                )\n                print(f\"Deleted {plural}/{name}\")\n                time.sleep(1)  # Rate limit deletions\n            except ApiException as e:\n                print(f\"Failed to delete {name}: {e}\")\n\n    except ApiException as e:\n        print(f\"Error listing resources: {e}\")\n\n# Example: Delete 30% of MyApp custom resources\nchaos_delete_custom_resources(\n    group='app.example.com',\n    version='v1',\n    plural='myapps',\n    namespace='production',\n    percentage=30\n)\n```\n\n## Quick Reference\n\n| Chaos Type | Tool | YAML/Command |\n|------------|------|--------------|\n| Pod delete | Litmus | `pod-delete` experiment |\n| Network latency | Chaos Mesh | `NetworkChaos` with action: delay |\n| Node drain | kubectl/API | `kubectl drain <node>` |\n| CPU stress | Chaos Mesh | `StressChaos` with cpu stressor |\n| DNS failure | Chaos Mesh | `DNSChaos` random/error action |\n| I/O latency | Chaos Mesh | `IOChaos` with latency action |\n| Network partition | Chaos Mesh | `NetworkChaos` partition |\n| Pod failure | Chaos Mesh | `PodChaos` pod-failure |\n","isInternal":false,"tokens":2567,"sizeBytes":10785},{"name":"SKILL.md","path":"skills/chaos-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/chaos-engineer/SKILL.md","title":"chaos-engineer","category":"anthropic-skill","format":"markdown","content":"---\nname: chaos-engineer\ndescription: Designs chaos experiments, creates failure injection frameworks, and facilitates game day exercises for distributed systems — producing runbooks, experiment manifests, rollback procedures, and post-mortem templates. Use when designing chaos experiments, implementing failure injection frameworks, or conducting game day exercises. Invoke for chaos experiments, resilience testing, blast radius control, game days, antifragile systems, fault injection, Chaos Monkey, Litmus Chaos.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: devops\n  triggers: chaos engineering, resilience testing, failure injection, game day, blast radius, chaos experiment, fault injection, Chaos Monkey, Litmus Chaos, antifragile\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: sre-engineer, devops-engineer, kubernetes-specialist\n---\n\n# Chaos Engineer\n\n## When to Use This Skill\n\n- Designing and executing chaos experiments\n- Implementing failure injection frameworks (Chaos Monkey, Litmus, etc.)\n- Planning and conducting game day exercises\n- Building blast radius controls and safety mechanisms\n- Setting up continuous chaos testing in CI/CD\n- Improving system resilience based on experiment findings\n\n## Core Workflow\n\n1. **System Analysis** - Map architecture, dependencies, critical paths, and failure modes\n2. **Experiment Design** - Define hypothesis, steady state, blast radius, and safety controls\n3. **Execute Chaos** - Run controlled experiments with monitoring and quick rollback\n4. **Learn & Improve** - Document findings, implement fixes, enhance monitoring\n5. **Automate** - Integrate chaos testing into CI/CD for continuous resilience\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Experiments | `references/experiment-design.md` | Designing hypothesis, blast radius, rollback |\n| Infrastructure | `references/infrastructure-chaos.md` | Server, network, zone, region failures |\n| Kubernetes | `references/kubernetes-chaos.md` | Pod, node, Litmus, chaos mesh experiments |\n| Tools & Automation | `references/chaos-tools.md` | Chaos Monkey, Gremlin, Pumba, CI/CD integration |\n| Game Days | `references/game-days.md` | Planning, executing, learning from game days |\n\n## Safety Checklist\n\nNon-obvious constraints that must be enforced on every experiment:\n\n- **Steady state first** — define and verify baseline metrics before injecting any failure\n- **Blast radius cap** — start with the smallest possible impact scope; expand only after validation\n- **Automated rollback ≤ 30 seconds** — abort path must be scripted and tested before the experiment begins\n- **Single variable** — change only one failure condition at a time until behaviour is well understood\n- **No production without safety nets** — customer-facing environments require circuit breakers, feature flags, or canary isolation\n- **Close the loop** — every experiment must produce a written learning summary and at least one tracked improvement\n\n## Output Templates\n\nWhen implementing chaos engineering, provide:\n1. Experiment design document (hypothesis, metrics, blast radius)\n2. Implementation code (failure injection scripts/manifests)\n3. Monitoring setup and alert configuration\n4. Rollback procedures and safety controls\n5. Learning summary and improvement recommendations\n\n## Concrete Example: Pod Failure Experiment (Litmus Chaos)\n\nThe following shows a complete experiment — from hypothesis to rollback — using Litmus Chaos on Kubernetes.\n\n### Step 1 — Define steady state and apply the experiment\n\n```bash\n# Verify baseline: p99 latency < 200ms, error rate < 0.1%\nkubectl get deploy my-service -n production\nkubectl top pods -n production -l app=my-service\n```\n\n### Step 2 — Create and apply a Litmus ChaosEngine manifest\n\n```yaml\n# chaos-pod-delete.yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: my-service-pod-delete\n  namespace: production\nspec:\n  appinfo:\n    appns: production\n    applabel: \"app=my-service\"\n    appkind: deployment\n  # Limit blast radius: only 1 replica at a time\n  engineState: active\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: \"60\"          # seconds\n            - name: CHAOS_INTERVAL\n              value: \"20\"          # delete one pod every 20s\n            - name: FORCE\n              value: \"false\"\n            - name: PODS_AFFECTED_PERC\n              value: \"33\"          # max 33% of replicas affected\n```\n\n```bash\n# Apply the experiment\nkubectl apply -f chaos-pod-delete.yaml\n\n# Watch experiment status\nkubectl describe chaosengine my-service-pod-delete -n production\nkubectl get chaosresult my-service-pod-delete-pod-delete -n production -w\n```\n\n### Step 3 — Monitor during the experiment\n\n```bash\n# Tail application logs for errors\nkubectl logs -l app=my-service -n production --since=2m -f\n\n# Check ChaosResult verdict when complete\nkubectl get chaosresult my-service-pod-delete-pod-delete \\\n  -n production -o jsonpath='{.status.experimentStatus.verdict}'\n```\n\n### Step 4 — Rollback / abort if steady state is violated\n\n```bash\n# Immediately stop the experiment\nkubectl patch chaosengine my-service-pod-delete \\\n  -n production --type merge -p '{\"spec\":{\"engineState\":\"stop\"}}'\n\n# Confirm all pods are healthy\nkubectl rollout status deployment/my-service -n production\n```\n\n## Concrete Example: Network Latency with toxiproxy\n\n```bash\n# Install toxiproxy CLI\nbrew install toxiproxy   # macOS; use the binary release on Linux\n\n# Start toxiproxy server (runs alongside your service)\ntoxiproxy-server &\n\n# Create a proxy for your downstream dependency\ntoxiproxy-cli create -l 0.0.0.0:22222 -u downstream-db:5432 db-proxy\n\n# Inject 300ms latency with 10% jitter — blast radius: this proxy only\ntoxiproxy-cli toxic add db-proxy -t latency -a latency=300 -a jitter=30\n\n# Run your load test / observe metrics here ...\n\n# Remove the toxic to restore normal behaviour\ntoxiproxy-cli toxic remove db-proxy -n latency_downstream\n```\n\n## Concrete Example: Chaos Monkey (Spinnaker / standalone)\n\n```bash\n# chaos-monkey-config.yml — restrict to a single ASG\ndeployment:\n  enabled: true\n  regionIndependence: false\nchaos:\n  enabled: true\n  meanTimeBetweenKillsInWorkDays: 2\n  minTimeBetweenKillsInWorkDays: 1\n  grouping: APP           # kill one instance per app, not per cluster\n  exceptions:\n    - account: production\n      region: us-east-1\n      detail: \"*-canary\"  # never kill canary instances\n\n# Apply and trigger a manual kill for testing\nchaos-monkey --app my-service --account staging --dry-run false\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/devops/chaos-engineer/)\n","frontmatter":{"name":"chaos-engineer","description":"Designs chaos experiments, creates failure injection frameworks, and facilitates game day exercises for distributed systems — producing runbooks, experiment manifests, rollback procedures, and post-mortem templates. Use when designing chaos experiments, implementing failure injection frameworks, or conducting game day exercises. Invoke for chaos experiments, resilience testing, blast radius control, game days, antifragile systems, fault injection, Chaos Monkey, Litmus Chaos.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"devops","triggers":"chaos engineering, resilience testing, failure injection, game day, blast radius, chaos experiment, fault injection, Chaos Monkey, Litmus Chaos, antifragile","role":"specialist","scope":"implementation","output-format":"code","related-skills":"sre-engineer, devops-engineer, kubernetes-specialist"}},"isInternal":false,"tokens":1633,"sizeBytes":7066},{"name":"design-patterns.md","path":"skills/cli-developer/references/design-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cli-developer/references/design-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# CLI Design Patterns\n\n## Command Hierarchy\n\n```\nmycli                           # Root command\n├── init [options]              # Simple command\n├── config\n│   ├── get <key>              # Nested subcommand\n│   ├── set <key> <value>\n│   └── list\n├── deploy [environment]        # Command with args\n│   ├── --dry-run              # Flag\n│   ├── --force\n│   └── --config <file>        # Option with value\n└── plugins\n    ├── install <name>\n    ├── list\n    └── remove <name>\n```\n\n## Flag Conventions\n\n```bash\n# Boolean flags (presence = true)\nmycli deploy --force --dry-run\n\n# Short + long forms\nmycli -v --verbose\nmycli -c config.yml --config config.yml\n\n# Required vs optional\nmycli deploy <env>              # Positional (required)\nmycli deploy --env production   # Flag (optional)\n\n# Multiple values\nmycli install pkg1 pkg2 pkg3    # Variadic args\nmycli --exclude node_modules --exclude .git\n```\n\n## Configuration Layers\n\nPriority order (highest to lowest):\n\n1. **Command-line flags** - Explicit user intent\n2. **Environment variables** - Runtime context\n3. **Config files (project)** - `.myclirc`, `mycli.config.js`\n4. **Config files (user)** - `~/.myclirc`, `~/.config/mycli/config.yml`\n5. **Config files (system)** - `/etc/mycli/config.yml`\n6. **Defaults** - Hard-coded sensible defaults\n\n```javascript\n// Example config resolution\nconst config = {\n  ...systemDefaults,\n  ...loadSystemConfig(),\n  ...loadUserConfig(),\n  ...loadProjectConfig(),\n  ...loadEnvVars(),\n  ...parseCliFlags(),\n};\n```\n\n## Exit Codes\n\nStandard POSIX exit codes:\n\n```javascript\nconst EXIT_CODES = {\n  SUCCESS: 0,\n  GENERAL_ERROR: 1,\n  MISUSE: 2,              // Invalid arguments\n  PERMISSION_DENIED: 77,\n  NOT_FOUND: 127,\n  SIGINT: 130,            // Ctrl+C\n};\n```\n\n## Plugin Architecture\n\n```\nmycli/\n├── core/                      # Core functionality\n├── plugins/\n│   ├── aws/                  # Plugin: AWS integration\n│   │   ├── package.json\n│   │   └── index.js\n│   └── github/               # Plugin: GitHub integration\n│       ├── package.json\n│       └── index.js\n└── plugin-loader.js          # Discovery & loading\n```\n\nPlugin discovery:\n1. Check `~/.mycli/plugins/`\n2. Check `node_modules/mycli-plugin-*`\n3. Check `MYCLI_PLUGIN_PATH` env var\n\n## Error Handling Patterns\n\n```javascript\n// Good: Actionable error messages\nError: Config file not found at /path/to/config.yml\n\nTried locations:\n  • ./mycli.config.yml\n  • ~/.myclirc\n  • /etc/mycli/config.yml\n\nRun 'mycli init' to create a config file, or use --config to specify location.\n\n// Bad: Unhelpful errors\nError: ENOENT\n```\n\n## Interactive vs Non-Interactive\n\n```javascript\n// Detect if running in CI/CD\nconst isCI = process.env.CI === 'true' || !process.stdout.isTTY;\n\nif (isCI) {\n  // Non-interactive: fail fast with clear errors\n  if (!options.environment) {\n    throw new Error('--environment required in non-interactive mode');\n  }\n} else {\n  // Interactive: prompt user\n  const environment = await prompt({\n    type: 'select',\n    message: 'Select environment:',\n    choices: ['development', 'staging', 'production'],\n  });\n}\n```\n\n## State Management\n\n```\n~/.mycli/\n├── config.yml           # User configuration\n├── cache/               # Cached data\n│   ├── plugins.json\n│   └── api-responses/\n├── credentials.json     # Sensitive data (600 perms)\n└── state.json          # Session state\n```\n\n## Performance Patterns\n\n```javascript\n// Lazy loading: Don't load unused dependencies\nif (command === 'deploy') {\n  const deploy = require('./commands/deploy'); // Load on demand\n  await deploy.run();\n}\n\n// Caching: Avoid repeated API calls\nconst cache = new Cache('~/.mycli/cache', { ttl: 3600 });\nlet plugins = await cache.get('plugins');\nif (!plugins) {\n  plugins = await fetchPlugins();\n  await cache.set('plugins', plugins);\n}\n\n// Async operations: Don't block unnecessarily\nawait Promise.all([\n  validateConfig(),\n  checkForUpdates(),\n  loadPlugins(),\n]);\n```\n\n## Versioning & Updates\n\n```javascript\n// Check for updates (non-blocking)\ncheckForUpdates().then(update => {\n  if (update.available) {\n    console.log(`Update available: ${update.version}`);\n    console.log(`Run: npm install -g mycli@latest`);\n  }\n}).catch(() => {\n  // Silently fail - don't interrupt user workflow\n});\n\n// Version compatibility\nconst MIN_NODE_VERSION = '18.0.0';\nif (!semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`)) {\n  console.error(`mycli requires Node.js ${MIN_NODE_VERSION} or higher`);\n  process.exit(1);\n}\n```\n\n## Help Text Design\n\n```\nUSAGE\n  mycli deploy [environment] [options]\n\nARGUMENTS\n  environment  Target environment (development|staging|production)\n\nOPTIONS\n  -c, --config <file>  Path to config file\n  -f, --force          Skip confirmation prompts\n  -d, --dry-run        Preview changes without executing\n  -v, --verbose        Show detailed output\n\nEXAMPLES\n  # Deploy to production\n  mycli deploy production\n\n  # Preview staging deployment\n  mycli deploy staging --dry-run\n\n  # Use custom config\n  mycli deploy --config ./custom.yml\n\nLearn more: https://docs.mycli.dev/deploy\n```\n","isInternal":false,"tokens":1318,"sizeBytes":5249},{"name":"go-cli.md","path":"skills/cli-developer/references/go-cli.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cli-developer/references/go-cli.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Go CLI Development\n\n## Cobra (Recommended)\n\nPowerful CLI framework used by kubectl, hugo, docker.\n\n```go\n// cmd/root.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"os\"\n    \"github.com/spf13/cobra\"\n    \"github.com/spf13/viper\"\n)\n\nvar (\n    cfgFile string\n    verbose bool\n)\n\nvar rootCmd = &cobra.Command{\n    Use:   \"mycli\",\n    Short: \"My awesome CLI tool\",\n    Long: `A longer description of your CLI application`,\n    Version: \"1.0.0\",\n}\n\nfunc Execute() {\n    if err := rootCmd.Execute(); err != nil {\n        fmt.Fprintln(os.Stderr, err)\n        os.Exit(1)\n    }\n}\n\nfunc init() {\n    cobra.OnInitialize(initConfig)\n\n    rootCmd.PersistentFlags().StringVar(&cfgFile, \"config\", \"\", \"config file\")\n    rootCmd.PersistentFlags().BoolVarP(&verbose, \"verbose\", \"v\", false, \"verbose output\")\n\n    viper.BindPFlag(\"verbose\", rootCmd.PersistentFlags().Lookup(\"verbose\"))\n}\n\nfunc initConfig() {\n    if cfgFile != \"\" {\n        viper.SetConfigFile(cfgFile)\n    } else {\n        home, err := os.UserHomeDir()\n        cobra.CheckErr(err)\n\n        viper.AddConfigPath(home)\n        viper.AddConfigPath(\".\")\n        viper.SetConfigType(\"yaml\")\n        viper.SetConfigName(\".mycli\")\n    }\n\n    viper.AutomaticEnv()\n\n    if err := viper.ReadInConfig(); err == nil {\n        fmt.Fprintln(os.Stderr, \"Using config file:\", viper.ConfigFileUsed())\n    }\n}\n\n// cmd/init.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/cobra\"\n)\n\nvar (\n    template string\n    force    bool\n)\n\nvar initCmd = &cobra.Command{\n    Use:   \"init [name]\",\n    Short: \"Initialize a new project\",\n    Args:  cobra.ExactArgs(1),\n    RunE: func(cmd *cobra.Command, args []string) error {\n        name := args[0]\n        return initProject(name, template, force)\n    },\n}\n\nfunc init() {\n    rootCmd.AddCommand(initCmd)\n\n    initCmd.Flags().StringVarP(&template, \"template\", \"t\", \"default\", \"Project template\")\n    initCmd.Flags().BoolVarP(&force, \"force\", \"f\", false, \"Overwrite existing\")\n}\n\nfunc initProject(name, template string, force bool) error {\n    fmt.Printf(\"Creating %s from %s\\n\", name, template)\n    return nil\n}\n\n// cmd/deploy.go\npackage cmd\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/cobra\"\n)\n\nvar (\n    dryRun bool\n)\n\nvar deployCmd = &cobra.Command{\n    Use:   \"deploy [environment]\",\n    Short: \"Deploy to environment\",\n    Args:  cobra.ExactArgs(1),\n    ValidArgs: []string{\"dev\", \"staging\", \"prod\"},\n    RunE: func(cmd *cobra.Command, args []string) error {\n        env := args[0]\n        return deploy(env, dryRun)\n    },\n}\n\nfunc init() {\n    rootCmd.AddCommand(deployCmd)\n    deployCmd.Flags().BoolVar(&dryRun, \"dry-run\", false, \"Preview only\")\n}\n\nfunc deploy(env string, dryRun bool) error {\n    if dryRun {\n        fmt.Printf(\"Would deploy to: %s\\n\", env)\n    } else {\n        fmt.Printf(\"Deploying to %s...\\n\", env)\n    }\n    return nil\n}\n\n// main.go\npackage main\n\nimport \"mycli/cmd\"\n\nfunc main() {\n    cmd.Execute()\n}\n```\n\n## Viper (Configuration)\n\nConfiguration management with multiple sources.\n\n```go\npackage config\n\nimport (\n    \"fmt\"\n    \"github.com/spf13/viper\"\n)\n\ntype Config struct {\n    Environment string `mapstructure:\"environment\"`\n    Timeout     int    `mapstructure:\"timeout\"`\n    Verbose     bool   `mapstructure:\"verbose\"`\n    API         APIConfig `mapstructure:\"api\"`\n}\n\ntype APIConfig struct {\n    Endpoint string `mapstructure:\"endpoint\"`\n    Token    string `mapstructure:\"token\"`\n}\n\nfunc Load() (*Config, error) {\n    // Set defaults\n    viper.SetDefault(\"environment\", \"development\")\n    viper.SetDefault(\"timeout\", 30)\n    viper.SetDefault(\"verbose\", false)\n\n    // Config file locations\n    viper.SetConfigName(\"config\")\n    viper.SetConfigType(\"yaml\")\n    viper.AddConfigPath(\"/etc/mycli/\")\n    viper.AddConfigPath(\"$HOME/.config/mycli\")\n    viper.AddConfigPath(\".\")\n\n    // Environment variables\n    viper.SetEnvPrefix(\"MYCLI\")\n    viper.AutomaticEnv()\n\n    // Read config\n    if err := viper.ReadInConfig(); err != nil {\n        if _, ok := err.(viper.ConfigFileNotFoundError); !ok {\n            return nil, fmt.Errorf(\"failed to read config: %w\", err)\n        }\n    }\n\n    // Unmarshal into struct\n    var cfg Config\n    if err := viper.Unmarshal(&cfg); err != nil {\n        return nil, fmt.Errorf(\"failed to unmarshal config: %w\", err)\n    }\n\n    return &cfg, nil\n}\n```\n\n## Bubble Tea (Interactive TUI)\n\nModern terminal UI framework for interactive CLIs.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n\n    tea \"github.com/charmbracelet/bubbletea\"\n    \"github.com/charmbracelet/lipgloss\"\n)\n\n// Model\ntype model struct {\n    choices  []string\n    cursor   int\n    selected map[int]struct{}\n}\n\nfunc initialModel() model {\n    return model{\n        choices:  []string{\"TypeScript\", \"ESLint\", \"Prettier\", \"Jest\"},\n        selected: make(map[int]struct{}),\n    }\n}\n\n// Init\nfunc (m model) Init() tea.Cmd {\n    return nil\n}\n\n// Update\nfunc (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {\n    switch msg := msg.(type) {\n    case tea.KeyMsg:\n        switch msg.String() {\n        case \"ctrl+c\", \"q\":\n            return m, tea.Quit\n\n        case \"up\", \"k\":\n            if m.cursor > 0 {\n                m.cursor--\n            }\n\n        case \"down\", \"j\":\n            if m.cursor < len(m.choices)-1 {\n                m.cursor++\n            }\n\n        case \" \":\n            _, ok := m.selected[m.cursor]\n            if ok {\n                delete(m.selected, m.cursor)\n            } else {\n                m.selected[m.cursor] = struct{}{}\n            }\n\n        case \"enter\":\n            return m, tea.Quit\n        }\n    }\n\n    return m, nil\n}\n\n// View\nfunc (m model) View() string {\n    s := \"Select features:\\n\\n\"\n\n    for i, choice := range m.choices {\n        cursor := \" \"\n        if m.cursor == i {\n            cursor = \">\"\n        }\n\n        checked := \" \"\n        if _, ok := m.selected[i]; ok {\n            checked = \"x\"\n        }\n\n        s += fmt.Sprintf(\"%s [%s] %s\\n\", cursor, checked, choice)\n    }\n\n    s += \"\\nPress space to select, enter to confirm, q to quit.\\n\"\n\n    return s\n}\n\nfunc main() {\n    p := tea.NewProgram(initialModel())\n    if _, err := p.Run(); err != nil {\n        fmt.Printf(\"Error: %v\", err)\n        os.Exit(1)\n    }\n}\n```\n\n## Progress Indicators\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/schollz/progressbar/v3\"\n)\n\nfunc main() {\n    // Simple progress bar\n    bar := progressbar.Default(100, \"Downloading\")\n    for i := 0; i < 100; i++ {\n        bar.Add(1)\n        time.Sleep(40 * time.Millisecond)\n    }\n\n    // Custom progress bar\n    bar = progressbar.NewOptions(100,\n        progressbar.OptionEnableColorCodes(true),\n        progressbar.OptionShowBytes(true),\n        progressbar.OptionSetWidth(15),\n        progressbar.OptionSetDescription(\"[cyan][1/3][reset] Downloading...\"),\n        progressbar.OptionSetTheme(progressbar.Theme{\n            Saucer:        \"[green]=[reset]\",\n            SaucerHead:    \"[green]>[reset]\",\n            SaucerPadding: \" \",\n            BarStart:      \"[\",\n            BarEnd:        \"]\",\n        }),\n    )\n\n    for i := 0; i < 100; i++ {\n        bar.Add(1)\n        time.Sleep(40 * time.Millisecond)\n    }\n}\n```\n\n## Spinner\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/briandowns/spinner\"\n)\n\nfunc main() {\n    s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)\n    s.Suffix = \" Installing dependencies...\"\n    s.Start()\n\n    time.Sleep(4 * time.Second)\n\n    s.UpdateCharSet(spinner.CharSets[9])\n    s.Suffix = \" Processing...\"\n    time.Sleep(2 * time.Second)\n\n    s.Stop()\n    fmt.Println(\"✓ Done!\")\n}\n```\n\n## Colored Output\n\n```go\npackage main\n\nimport (\n    \"github.com/fatih/color\"\n)\n\nfunc main() {\n    // Basic colors\n    color.Blue(\"Info: Starting deployment...\")\n    color.Green(\"Success: Deployment complete!\")\n    color.Yellow(\"Warning: Deprecated flag used\")\n    color.Red(\"Error: Deployment failed\")\n\n    // Custom styles\n    success := color.New(color.FgGreen, color.Bold).PrintlnFunc()\n    error := color.New(color.FgRed, color.Bold).PrintlnFunc()\n\n    success(\"✓ Build successful\")\n    error(\"✗ Build failed\")\n\n    // Printf-style\n    color.Cyan(\"Processing %d files...\\n\", 42)\n\n    // Disable colors for CI\n    if os.Getenv(\"CI\") != \"\" {\n        color.NoColor = true\n    }\n}\n```\n\n## Error Handling\n\n```go\npackage main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"os\"\n    \"syscall\"\n\n    \"github.com/spf13/cobra\"\n)\n\nvar deployCmd = &cobra.Command{\n    Use:   \"deploy\",\n    Short: \"Deploy application\",\n    RunE: func(cmd *cobra.Command, args []string) error {\n        if err := deploy(); err != nil {\n            return handleError(err)\n        }\n        return nil\n    },\n}\n\nfunc handleError(err error) error {\n    var exitCode int\n\n    switch {\n    case errors.Is(err, os.ErrPermission):\n        fmt.Fprintln(os.Stderr, \"Permission denied\")\n        fmt.Fprintln(os.Stderr, \"Try running with sudo or check file permissions\")\n        exitCode = 77\n\n    case errors.Is(err, os.ErrNotExist):\n        fmt.Fprintf(os.Stderr, \"File not found: %v\\n\", err)\n        exitCode = 127\n\n    default:\n        fmt.Fprintf(os.Stderr, \"Deployment failed: %v\\n\", err)\n        if os.Getenv(\"DEBUG\") != \"\" {\n            fmt.Fprintf(os.Stderr, \"%+v\\n\", err)\n        }\n        exitCode = 1\n    }\n\n    os.Exit(exitCode)\n    return nil\n}\n\n// Handle SIGINT (Ctrl+C)\nfunc main() {\n    // Setup signal handling\n    c := make(chan os.Signal, 1)\n    signal.Notify(c, os.Interrupt, syscall.SIGTERM)\n\n    go func() {\n        <-c\n        fmt.Println(\"\\nOperation cancelled\")\n        os.Exit(130)\n    }()\n\n    cmd.Execute()\n}\n```\n\n## Testing\n\n```go\npackage cmd\n\nimport (\n    \"bytes\"\n    \"testing\"\n\n    \"github.com/spf13/cobra\"\n    \"github.com/stretchr/testify/assert\"\n)\n\nfunc TestInitCommand(t *testing.T) {\n    cmd := &cobra.Command{Use: \"test\"}\n    cmd.AddCommand(initCmd)\n\n    b := bytes.NewBufferString(\"\")\n    cmd.SetOut(b)\n    cmd.SetArgs([]string{\"init\", \"my-project\"})\n\n    err := cmd.Execute()\n    assert.NoError(t, err)\n    assert.Contains(t, b.String(), \"Creating my-project\")\n}\n\nfunc TestInitWithTemplate(t *testing.T) {\n    cmd := &cobra.Command{Use: \"test\"}\n    cmd.AddCommand(initCmd)\n\n    b := bytes.NewBufferString(\"\")\n    cmd.SetOut(b)\n    cmd.SetArgs([]string{\"init\", \"my-project\", \"--template\", \"react\"})\n\n    err := cmd.Execute()\n    assert.NoError(t, err)\n    assert.Contains(t, b.String(), \"react\")\n}\n```\n\n## Build & Distribution\n\n```makefile\n# Makefile\nVERSION := $(shell git describe --tags --always --dirty)\nLDFLAGS := -ldflags \"-X main.version=$(VERSION)\"\n\n.PHONY: build\nbuild:\n\tgo build $(LDFLAGS) -o bin/mycli main.go\n\n.PHONY: install\ninstall:\n\tgo install $(LDFLAGS)\n\n.PHONY: test\ntest:\n\tgo test -v ./...\n\n.PHONY: release\nrelease:\n\tGOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-linux-amd64\n\tGOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-darwin-amd64\n\tGOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/mycli-darwin-arm64\n\tGOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/mycli-windows-amd64.exe\n```\n","isInternal":false,"tokens":2915,"sizeBytes":10971},{"name":"node-cli.md","path":"skills/cli-developer/references/node-cli.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cli-developer/references/node-cli.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Node.js CLI Development\n\n## Commander.js (Recommended)\n\nModern, elegant CLI framework with TypeScript support.\n\n```javascript\n#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { version } from './package.json';\n\nconst program = new Command();\n\nprogram\n  .name('mycli')\n  .description('My awesome CLI tool')\n  .version(version);\n\n// Simple command\nprogram\n  .command('init')\n  .description('Initialize a new project')\n  .option('-t, --template <type>', 'Project template', 'default')\n  .option('-f, --force', 'Overwrite existing files')\n  .action(async (options) => {\n    console.log(`Initializing with template: ${options.template}`);\n  });\n\n// Command with arguments\nprogram\n  .command('deploy <environment>')\n  .description('Deploy to environment')\n  .option('--dry-run', 'Preview without executing')\n  .action(async (environment, options) => {\n    if (options.dryRun) {\n      console.log(`Would deploy to: ${environment}`);\n    } else {\n      await deploy(environment);\n    }\n  });\n\n// Nested subcommands\nconst config = program.command('config').description('Manage configuration');\n\nconfig\n  .command('get <key>')\n  .description('Get config value')\n  .action((key) => console.log(getConfig(key)));\n\nconfig\n  .command('set <key> <value>')\n  .description('Set config value')\n  .action((key, value) => setConfig(key, value));\n\nprogram.parse();\n```\n\n## Yargs (Alternative)\n\nPowerful argument parsing with middleware support.\n\n```javascript\n#!/usr/bin/env node\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\n\nyargs(hideBin(process.argv))\n  .command(\n    'deploy <env>',\n    'Deploy to environment',\n    (yargs) => {\n      return yargs\n        .positional('env', {\n          describe: 'Environment name',\n          choices: ['dev', 'staging', 'prod'],\n        })\n        .option('force', {\n          alias: 'f',\n          type: 'boolean',\n          description: 'Force deployment',\n        });\n    },\n    async (argv) => {\n      await deploy(argv.env, { force: argv.force });\n    }\n  )\n  .middleware([(argv) => {\n    // Validate before all commands\n    if (!isConfigValid()) {\n      throw new Error('Invalid config');\n    }\n  }])\n  .demandCommand()\n  .help()\n  .parse();\n```\n\n## Interactive Prompts (Inquirer)\n\nBeautiful interactive prompts for user input.\n\n```javascript\nimport inquirer from 'inquirer';\n\n// Text input\nconst { name } = await inquirer.prompt([\n  {\n    type: 'input',\n    name: 'name',\n    message: 'Project name:',\n    default: 'my-project',\n    validate: (input) => input.length > 0 || 'Name required',\n  },\n]);\n\n// Select from list\nconst { environment } = await inquirer.prompt([\n  {\n    type: 'list',\n    name: 'environment',\n    message: 'Select environment:',\n    choices: ['development', 'staging', 'production'],\n    default: 'development',\n  },\n]);\n\n// Checkbox (multi-select)\nconst { features } = await inquirer.prompt([\n  {\n    type: 'checkbox',\n    name: 'features',\n    message: 'Select features:',\n    choices: [\n      { name: 'TypeScript', checked: true },\n      { name: 'ESLint', checked: true },\n      { name: 'Prettier', checked: true },\n      { name: 'Jest', checked: false },\n    ],\n  },\n]);\n\n// Confirmation\nconst { confirmed } = await inquirer.prompt([\n  {\n    type: 'confirm',\n    name: 'confirmed',\n    message: 'Deploy to production?',\n    default: false,\n  },\n]);\n\n// Password\nconst { password } = await inquirer.prompt([\n  {\n    type: 'password',\n    name: 'password',\n    message: 'Enter password:',\n    mask: '*',\n  },\n]);\n```\n\n## Terminal Output (Chalk)\n\nColorful terminal output with proper TTY detection.\n\n```javascript\nimport chalk from 'chalk';\n\n// Basic colors\nconsole.log(chalk.blue('Info: ') + 'Starting deployment...');\nconsole.log(chalk.green('Success: ') + 'Deployment complete');\nconsole.log(chalk.yellow('Warning: ') + 'Deprecated flag used');\nconsole.log(chalk.red('Error: ') + 'Deployment failed');\n\n// Styles\nconsole.log(chalk.bold.underline('Important'));\nconsole.log(chalk.dim('Less important'));\n\n// Templates\nconst success = chalk.green.bold;\nconst error = chalk.red.bold;\nconsole.log(success('✓') + ' Build successful');\nconsole.log(error('✗') + ' Build failed');\n\n// Disable colors for CI\nconst log = {\n  info: (msg) => console.log(chalk.blue('ℹ'), msg),\n  success: (msg) => console.log(chalk.green('✔'), msg),\n  warn: (msg) => console.log(chalk.yellow('⚠'), msg),\n  error: (msg) => console.log(chalk.red('✖'), msg),\n};\n\n// Auto-detects TTY and CI environments\n```\n\n## Progress Indicators (Ora)\n\nElegant terminal spinners and progress indicators.\n\n```javascript\nimport ora from 'ora';\n\n// Simple spinner\nconst spinner = ora('Loading...').start();\nawait doWork();\nspinner.succeed('Done!');\n\n// Update text\nconst spinner = ora('Starting...').start();\nspinner.text = 'Processing...';\nawait process();\nspinner.text = 'Finalizing...';\nawait finalize();\nspinner.succeed('Complete!');\n\n// Different states\nspinner.start('Installing dependencies...');\n// ... work\nspinner.succeed('Dependencies installed');\n// or\nspinner.fail('Installation failed');\n// or\nspinner.warn('Some packages skipped');\n// or\nspinner.info('Using cached packages');\n\n// Multiple spinners\nconst spinners = {\n  api: ora('Deploying API...').start(),\n  web: ora('Deploying web app...').start(),\n  db: ora('Running migrations...').start(),\n};\n\nawait Promise.all([\n  deployApi().then(() => spinners.api.succeed()),\n  deployWeb().then(() => spinners.web.succeed()),\n  runMigrations().then(() => spinners.db.succeed()),\n]);\n```\n\n## Progress Bars (cli-progress)\n\n```javascript\nimport cliProgress from 'cli-progress';\n\n// Single progress bar\nconst bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\nbar.start(100, 0);\n\nfor (let i = 0; i <= 100; i++) {\n  await processItem(i);\n  bar.update(i);\n}\n\nbar.stop();\n\n// Multi-progress\nconst multibar = new cliProgress.MultiBar({\n  clearOnComplete: false,\n  hideCursor: true,\n});\n\nconst bar1 = multibar.create(100, 0, { task: 'API' });\nconst bar2 = multibar.create(100, 0, { task: 'Web' });\n\nawait Promise.all([\n  processApi(bar1),\n  processWeb(bar2),\n]);\n\nmultibar.stop();\n```\n\n## File System Helpers\n\n```javascript\nimport fs from 'fs-extra';\nimport { globby } from 'globby';\nimport path from 'path';\n\n// Copy with template\nawait fs.copy('templates/app', targetDir, {\n  filter: (src) => !src.includes('node_modules'),\n});\n\n// Read/write JSON\nconst config = await fs.readJson('config.json');\nawait fs.writeJson('output.json', data, { spaces: 2 });\n\n// Ensure directory exists\nawait fs.ensureDir('dist/assets');\n\n// Find files\nconst files = await globby(['src/**/*.ts', '!src/**/*.test.ts']);\n```\n\n## Error Handling\n\n```javascript\nimport { Command } from 'commander';\n\nprogram\n  .command('deploy')\n  .action(async () => {\n    try {\n      await deploy();\n    } catch (error) {\n      if (error.code === 'EACCES') {\n        console.error(chalk.red('Permission denied'));\n        console.error('Try running with sudo or check file permissions');\n        process.exit(77);\n      } else if (error.code === 'ENOENT') {\n        console.error(chalk.red('File not found:'), error.path);\n        process.exit(127);\n      } else {\n        console.error(chalk.red('Deployment failed:'), error.message);\n        if (process.env.DEBUG) {\n          console.error(error.stack);\n        }\n        process.exit(1);\n      }\n    }\n  });\n\n// Handle SIGINT (Ctrl+C)\nprocess.on('SIGINT', () => {\n  console.log('\\nOperation cancelled');\n  process.exit(130);\n});\n```\n\n## Package.json Setup\n\n```json\n{\n  \"name\": \"mycli\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"bin\": {\n    \"mycli\": \"./bin/cli.js\"\n  },\n  \"files\": [\n    \"bin/\",\n    \"lib/\",\n    \"templates/\"\n  ],\n  \"engines\": {\n    \"node\": \">=18.0.0\"\n  },\n  \"dependencies\": {\n    \"commander\": \"^11.0.0\",\n    \"inquirer\": \"^9.0.0\",\n    \"chalk\": \"^5.0.0\",\n    \"ora\": \"^7.0.0\"\n  }\n}\n```\n\n## Testing CLIs\n\n```javascript\nimport { execaCommand } from 'execa';\nimport { describe, it, expect } from 'vitest';\n\ndescribe('mycli', () => {\n  it('shows version', async () => {\n    const { stdout } = await execaCommand('node bin/cli.js --version');\n    expect(stdout).toMatch(/\\d+\\.\\d+\\.\\d+/);\n  });\n\n  it('shows help', async () => {\n    const { stdout } = await execaCommand('node bin/cli.js --help');\n    expect(stdout).toContain('Usage:');\n  });\n\n  it('handles invalid command', async () => {\n    await expect(\n      execaCommand('node bin/cli.js invalid')\n    ).rejects.toThrow();\n  });\n});\n```\n","isInternal":false,"tokens":2146,"sizeBytes":8443},{"name":"python-cli.md","path":"skills/cli-developer/references/python-cli.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cli-developer/references/python-cli.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Python CLI Development\n\n## Typer (Recommended - Modern)\n\nFastAPI-style CLI framework with automatic help generation.\n\n```python\n#!/usr/bin/env python3\nimport typer\nfrom typing import Optional\nfrom enum import Enum\n\napp = typer.Typer()\n\nclass Environment(str, Enum):\n    dev = \"development\"\n    staging = \"staging\"\n    prod = \"production\"\n\n@app.command()\ndef init(\n    name: str = typer.Argument(..., help=\"Project name\"),\n    template: str = typer.Option(\"default\", help=\"Project template\"),\n    force: bool = typer.Option(False, \"--force\", \"-f\", help=\"Overwrite existing\"),\n):\n    \"\"\"Initialize a new project\"\"\"\n    typer.echo(f\"Creating {name} from {template}\")\n    if force:\n        typer.echo(\"Force mode enabled\")\n\n@app.command()\ndef deploy(\n    environment: Environment = typer.Argument(..., help=\"Target environment\"),\n    dry_run: bool = typer.Option(False, \"--dry-run\", help=\"Preview only\"),\n    config: Optional[typer.FileText] = typer.Option(None, help=\"Config file\"),\n):\n    \"\"\"Deploy to environment\"\"\"\n    if dry_run:\n        typer.echo(f\"Would deploy to: {environment.value}\")\n    else:\n        typer.echo(f\"Deploying to {environment.value}...\")\n\n# Nested commands\nconfig_app = typer.Typer()\napp.add_typer(config_app, name=\"config\", help=\"Manage configuration\")\n\n@config_app.command(\"get\")\ndef config_get(key: str):\n    \"\"\"Get config value\"\"\"\n    typer.echo(f\"Value: {get_config(key)}\")\n\n@config_app.command(\"set\")\ndef config_set(key: str, value: str):\n    \"\"\"Set config value\"\"\"\n    set_config(key, value)\n    typer.echo(f\"Set {key} = {value}\")\n\nif __name__ == \"__main__\":\n    app()\n```\n\n## Click (Widely Used)\n\nPowerful, composable CLI framework.\n\n```python\nimport click\n\n@click.group()\n@click.version_option()\ndef cli():\n    \"\"\"My awesome CLI tool\"\"\"\n    pass\n\n@cli.command()\n@click.argument('name')\n@click.option('--template', default='default', help='Project template')\n@click.option('--force', '-f', is_flag=True, help='Overwrite existing')\ndef init(name, template, force):\n    \"\"\"Initialize a new project\"\"\"\n    click.echo(f\"Creating {name} from {template}\")\n\n@cli.command()\n@click.argument('environment', type=click.Choice(['dev', 'staging', 'prod']))\n@click.option('--dry-run', is_flag=True, help='Preview only')\n@click.option('--config', type=click.File('r'), help='Config file')\ndef deploy(environment, dry_run, config):\n    \"\"\"Deploy to environment\"\"\"\n    if dry_run:\n        click.secho(f\"Would deploy to: {environment}\", fg='yellow')\n    else:\n        click.secho(f\"Deploying to {environment}...\", fg='green')\n\n# Nested groups\n@cli.group()\ndef config():\n    \"\"\"Manage configuration\"\"\"\n    pass\n\n@config.command('get')\n@click.argument('key')\ndef config_get(key):\n    \"\"\"Get config value\"\"\"\n    click.echo(get_config(key))\n\n@config.command('set')\n@click.argument('key')\n@click.argument('value')\ndef config_set(key, value):\n    \"\"\"Set config value\"\"\"\n    set_config(key, value)\n\nif __name__ == '__main__':\n    cli()\n```\n\n## Rich Terminal Output\n\nBeautiful terminal formatting and progress indicators.\n\n```python\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.progress import Progress, SpinnerColumn, TextColumn\nfrom rich.panel import Panel\nfrom rich.syntax import Syntax\nfrom rich import print as rprint\n\nconsole = Console()\n\n# Styled output\nconsole.print(\"[bold blue]Info:[/] Starting deployment...\")\nconsole.print(\"[bold green]Success:[/] Deployment complete!\")\nconsole.print(\"[bold yellow]Warning:[/] Deprecated flag used\")\nconsole.print(\"[bold red]Error:[/] Deployment failed\")\n\n# Tables\ntable = Table(title=\"Deployments\")\ntable.add_column(\"Environment\", style=\"cyan\")\ntable.add_column(\"Status\", style=\"magenta\")\ntable.add_column(\"Time\", style=\"green\")\n\ntable.add_row(\"Production\", \"✓ Success\", \"2m 34s\")\ntable.add_row(\"Staging\", \"✗ Failed\", \"1m 12s\")\nconsole.print(table)\n\n# Panels\nconsole.print(Panel.fit(\n    \"Deploy to production?\",\n    title=\"Confirmation\",\n    border_style=\"red\"\n))\n\n# Syntax highlighting\ncode = '''\ndef deploy(env: str):\n    print(f\"Deploying to {env}\")\n'''\nconsole.print(Syntax(code, \"python\", theme=\"monokai\"))\n\n# Progress bars\nwith Progress() as progress:\n    task = progress.add_task(\"[cyan]Deploying...\", total=100)\n    for i in range(100):\n        do_work()\n        progress.update(task, advance=1)\n\n# Spinners\nwith Progress(\n    SpinnerColumn(),\n    TextColumn(\"[progress.description]{task.description}\"),\n) as progress:\n    task = progress.add_task(\"Installing dependencies...\")\n    install_dependencies()\n```\n\n## Interactive Prompts (questionary)\n\n```python\nimport questionary\n\n# Text input\nname = questionary.text(\n    \"Project name:\",\n    default=\"my-project\",\n    validate=lambda x: len(x) > 0 or \"Name required\"\n).ask()\n\n# Select from list\nenvironment = questionary.select(\n    \"Select environment:\",\n    choices=[\"development\", \"staging\", \"production\"],\n    default=\"development\"\n).ask()\n\n# Checkbox (multi-select)\nfeatures = questionary.checkbox(\n    \"Select features:\",\n    choices=[\n        questionary.Choice(\"TypeScript\", checked=True),\n        questionary.Choice(\"ESLint\", checked=True),\n        questionary.Choice(\"Prettier\", checked=True),\n        questionary.Choice(\"Jest\", checked=False),\n    ]\n).ask()\n\n# Confirmation\nconfirmed = questionary.confirm(\n    \"Deploy to production?\",\n    default=False\n).ask()\n\nif confirmed:\n    deploy()\n\n# Password\npassword = questionary.password(\"Enter password:\").ask()\n```\n\n## Argparse (Standard Library)\n\nBuilt-in argument parsing (verbose but no dependencies).\n\n```python\nimport argparse\nimport sys\n\ndef main():\n    parser = argparse.ArgumentParser(\n        prog='mycli',\n        description='My awesome CLI tool',\n    )\n    parser.add_argument('--version', action='version', version='1.0.0')\n\n    subparsers = parser.add_subparsers(dest='command', required=True)\n\n    # Init command\n    init_parser = subparsers.add_parser('init', help='Initialize project')\n    init_parser.add_argument('name', help='Project name')\n    init_parser.add_argument('--template', default='default', help='Template')\n    init_parser.add_argument('-f', '--force', action='store_true')\n\n    # Deploy command\n    deploy_parser = subparsers.add_parser('deploy', help='Deploy')\n    deploy_parser.add_argument(\n        'environment',\n        choices=['dev', 'staging', 'prod'],\n        help='Target environment'\n    )\n    deploy_parser.add_argument('--dry-run', action='store_true')\n    deploy_parser.add_argument('--config', type=argparse.FileType('r'))\n\n    args = parser.parse_args()\n\n    if args.command == 'init':\n        init(args.name, args.template, args.force)\n    elif args.command == 'deploy':\n        deploy(args.environment, args.dry_run, args.config)\n\nif __name__ == '__main__':\n    main()\n```\n\n## Error Handling\n\n```python\nimport typer\nimport sys\nfrom pathlib import Path\n\napp = typer.Typer()\n\n@app.command()\ndef deploy():\n    try:\n        perform_deploy()\n    except PermissionError as e:\n        typer.secho(\"Permission denied\", fg=typer.colors.RED, err=True)\n        typer.echo(\"Try running with sudo or check file permissions\")\n        raise typer.Exit(code=77)\n    except FileNotFoundError as e:\n        typer.secho(f\"File not found: {e.filename}\", fg=typer.colors.RED, err=True)\n        raise typer.Exit(code=127)\n    except Exception as e:\n        typer.secho(f\"Deployment failed: {e}\", fg=typer.colors.RED, err=True)\n        if os.getenv('DEBUG'):\n            import traceback\n            traceback.print_exc()\n        raise typer.Exit(code=1)\n\n# Handle KeyboardInterrupt (Ctrl+C)\ndef main():\n    try:\n        app()\n    except KeyboardInterrupt:\n        typer.echo(\"\\nOperation cancelled\")\n        sys.exit(130)\n\nif __name__ == \"__main__\":\n    main()\n```\n\n## Configuration Management\n\n```python\nfrom pathlib import Path\nfrom typing import Any\nimport json\nimport os\n\nclass Config:\n    def __init__(self):\n        self.config_paths = [\n            Path(\"/etc/mycli/config.json\"),          # System\n            Path.home() / \".config\" / \"mycli\" / \"config.json\",  # User\n            Path.cwd() / \"mycli.json\",               # Project\n        ]\n\n    def load(self) -> dict[str, Any]:\n        config = self._defaults()\n\n        # Load from files (lowest to highest priority)\n        for path in self.config_paths:\n            if path.exists():\n                with path.open() as f:\n                    config.update(json.load(f))\n\n        # Override with environment variables\n        for key in config.keys():\n            env_var = f\"MYCLI_{key.upper()}\"\n            if env_var in os.environ:\n                config[key] = os.environ[env_var]\n\n        return config\n\n    def _defaults(self) -> dict[str, Any]:\n        return {\n            \"environment\": \"development\",\n            \"verbose\": False,\n            \"timeout\": 30,\n        }\n```\n\n## Setup.py / pyproject.toml\n\n```toml\n# pyproject.toml\n[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"mycli\"\nversion = \"1.0.0\"\ndescription = \"My awesome CLI tool\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"typer[all]>=0.9.0\",\n    \"rich>=13.0.0\",\n    \"questionary>=2.0.0\",\n]\n\n[project.scripts]\nmycli = \"mycli.cli:main\"\n\n[project.optional-dependencies]\ndev = [\n    \"pytest>=7.0.0\",\n    \"pytest-cov>=4.0.0\",\n]\n```\n\n## Testing CLIs\n\n```python\nfrom typer.testing import CliRunner\nfrom mycli.cli import app\n\nrunner = CliRunner()\n\ndef test_version():\n    result = runner.invoke(app, [\"--version\"])\n    assert result.exit_code == 0\n    assert \"1.0.0\" in result.stdout\n\ndef test_init():\n    result = runner.invoke(app, [\"init\", \"my-project\"])\n    assert result.exit_code == 0\n    assert \"Creating my-project\" in result.stdout\n\ndef test_init_with_template():\n    result = runner.invoke(app, [\"init\", \"my-project\", \"--template\", \"react\"])\n    assert result.exit_code == 0\n    assert \"react\" in result.stdout\n\ndef test_invalid_command():\n    result = runner.invoke(app, [\"invalid\"])\n    assert result.exit_code != 0\n```\n\n## Progress Bars (tqdm)\n\n```python\nfrom tqdm import tqdm\nimport time\n\n# Simple progress bar\nfor i in tqdm(range(100), desc=\"Processing\"):\n    process_item(i)\n\n# Custom format\nwith tqdm(total=100, desc=\"Downloading\", unit=\"MB\") as pbar:\n    for chunk in download_chunks():\n        pbar.update(len(chunk))\n\n# Multiple progress bars\nfrom tqdm import trange\n\nfor epoch in trange(10, desc=\"Epochs\"):\n    for batch in trange(100, desc=\"Batches\", leave=False):\n        train_batch(batch)\n```\n","isInternal":false,"tokens":2503,"sizeBytes":10437},{"name":"ux-patterns.md","path":"skills/cli-developer/references/ux-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cli-developer/references/ux-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# CLI UX Patterns\n\n## Progress Indicators\n\n### When to Use What\n\n```\nDeterminate (known total):\n  [████████████░░░░░░░░] 60% (3/5 files)\n  Use: File operations, downloads, batch processing\n\nIndeterminate (unknown duration):\n  ⠋ Loading...\n  Use: API calls, database queries, waiting for external services\n\nMulti-step:\n  ✓ Dependencies installed\n  ⠋ Building application...\n  ⏳ Running tests...\n  Use: Multi-phase operations (build, deploy, etc.)\n```\n\n### Progress Bar Best Practices\n\n```\nGood:\n[████████████░░░░░░░░] 60% | 120/200 MB | 2.4 MB/s | ETA: 33s\n↑ Visual     ↑ Percent  ↑ Progress  ↑ Rate     ↑ Time\n\nComponents:\n- Visual bar (20-40 chars)\n- Percentage (when known)\n- Current/total (with units)\n- Speed/rate (when applicable)\n- ETA (estimated time remaining)\n\nBad:\nProcessing... (no feedback)\n60% (no context)\n[████████████████████████████████████████] (too wide)\n```\n\n### Spinner Styles\n\n```\n⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏   Dots (elegant, low-key)\n⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷        Blocks (bold, attention)\n◐ ◓ ◑ ◒                  Circle (classic)\n▖ ▘ ▝ ▗                  Corners (minimal)\n⠁ ⠂ ⠄ ⡀ ⢀ ⠠ ⠐ ⠈        Line (subtle)\n\nChoose based on:\n- Terminal compatibility (stick to ASCII for Windows)\n- Branding (match your tool's personality)\n- Context (subtle for background, bold for main task)\n```\n\n## Color Usage\n\n### Semantic Colors\n\n```\nRed:     Errors, failures, destructive actions\nYellow:  Warnings, deprecations, non-critical issues\nGreen:   Success, completion, positive feedback\nBlue:    Information, hints, neutral messages\nCyan:    Commands, code, technical details\nMagenta: Highlights, special items\nGray:    Less important, metadata, timestamps\n\nExamples:\n✓ Success: Deployment complete\n✗ Error: File not found\n⚠ Warning: Deprecated flag --old-flag\nℹ Info: Using cache from ~/.mycli/cache\n```\n\n### When to Disable Colors\n\n```javascript\n// Detect non-TTY output (piped to file, etc.)\nconst noColor = !process.stdout.isTTY ||\n                process.env.NO_COLOR ||\n                process.env.CI === 'true';\n\nif (noColor) {\n  // Disable colors\n}\n\n// Support NO_COLOR standard\n// https://no-color.org/\n```\n\n### Color Accessibility\n\n```\n- Don't rely on color alone (use symbols too)\n- Provide high contrast (test with various terminals)\n- Support color blindness (red/green alternatives)\n\nGood:\n✓ Build successful (green)\n✗ Build failed (red)\n↑ Symbols work without color\n\nBad:\nSuccess (only color, no symbol)\nFailed (only color, no symbol)\n```\n\n## Help Text Design\n\n### Command Help Structure\n\n```\nUSAGE\n  mycli <command> [options]\n\nCOMMANDS\n  init         Initialize a new project\n  deploy       Deploy to environment\n  config       Manage configuration\n  plugins      Manage plugins\n\nOPTIONS\n  -h, --help     Show help\n  -v, --version  Show version\n  --config FILE  Config file path\n\nRun 'mycli <command> --help' for more information on a command.\n\nEXAMPLES\n  # Initialize a new project\n  mycli init my-app\n\n  # Deploy to production\n  mycli deploy production --dry-run\n\nLearn more: https://docs.mycli.dev\n```\n\n### Subcommand Help\n\n```\nUSAGE\n  mycli deploy <environment> [options]\n\nARGUMENTS\n  environment    Target environment (required)\n                 Values: development, staging, production\n\nOPTIONS\n  -c, --config <file>    Config file path\n                         Default: ./mycli.config.yml\n\n  -f, --force            Skip confirmation prompts\n                         Use with caution in production\n\n  -d, --dry-run          Preview changes without executing\n                         Shows what would happen\n\n  -v, --verbose          Show detailed output\n                         Includes debug information\n\nEXAMPLES\n  # Deploy to production (with confirmation)\n  mycli deploy production\n\n  # Preview staging deployment\n  mycli deploy staging --dry-run\n\n  # Use custom config\n  mycli deploy production --config ./prod.yml\n\n  # Force deploy without prompts\n  mycli deploy production --force\n\nFor more information, visit https://docs.mycli.dev/deploy\n```\n\n## Error Messages\n\n### Good Error Messages\n\n```\nPattern: [Context] → [Problem] → [Solution]\n\nExample 1: File not found\n✗ Error: Config file not found\n\nSearched locations:\n  • ./mycli.config.yml\n  • ~/.config/mycli/config.yml\n  • /etc/mycli/config.yml\n\nSolutions:\n  • Run 'mycli init' to create a config file\n  • Use --config to specify a different location\n  • Check file permissions\n\nExample 2: Validation error\n✗ Error: Invalid environment 'prod'\n\nExpected one of:\n  • development\n  • staging\n  • production\n\nDid you mean 'production'?\n\nExample 3: Permission error\n✗ Error: Permission denied writing to /etc/mycli/config.yml\n\nThis operation requires elevated permissions.\n\nTry:\n  • Run with sudo: sudo mycli config set key value\n  • Use user config: mycli config set --user key value\n  • Check file permissions: ls -la /etc/mycli/config.yml\n```\n\n### Error Message Guidelines\n\n```\nDO:\n✓ Be specific (\"Port 3000 already in use\" not \"Port unavailable\")\n✓ Show context (\"in file config.yml, line 42\")\n✓ Suggest solutions (\"Try running 'mycli fix'\")\n✓ Use plain language (\"File not found\" not \"ENOENT\")\n\nDON'T:\n✗ Show stack traces to users (save for --debug)\n✗ Use jargon (\"EACCES: permission denied\")\n✗ Leave users stuck (\"Invalid input\" with no explanation)\n✗ Be vague (\"Something went wrong\")\n```\n\n## Interactive Prompts\n\n### Prompt Types\n\n```\nText Input:\n  Project name: my-awesome-app\n  ↑ Clear label\n\nSelect (Single Choice):\n  ? Select environment: (Use arrow keys)\n  ❯ development\n    staging\n    production\n\nCheckbox (Multiple Choice):\n  ? Select features: (Press space to select, enter to confirm)\n  ◉ TypeScript\n  ◯ ESLint\n  ◉ Prettier\n  ◯ Jest\n\nConfirmation:\n  ? Deploy to production? (y/N)\n  ↑ Default is No (safer)\n\nPassword:\n  ? Enter password: ********\n  ↑ Masked input\n```\n\n### Prompt Guidelines\n\n```\nDO:\n✓ Show keyboard hints (\"Use arrow keys\", \"Press space\")\n✓ Provide sensible defaults (pre-select common choices)\n✓ Allow skipping with Ctrl+C\n✓ Validate input immediately\n✓ Show preview/summary before final action\n\nDON'T:\n✗ Require interaction in CI/CD environments\n✗ Ask obvious questions (confirm every action)\n✗ Hide what will happen next\n✗ Make users repeat information\n```\n\n## Output Formatting\n\n### Tables\n\n```\nGood:\n┌─────────────┬──────────┬──────────┐\n│ Environment │ Status   │ Updated  │\n├─────────────┼──────────┼──────────┤\n│ production  │ ✓ Active │ 2h ago   │\n│ staging     │ ✓ Active │ 5m ago   │\n│ development │ ✗ Down   │ 1d ago   │\n└─────────────┴──────────┴──────────┘\n\nMinimal (for scripting):\nEnvironment  Status  Updated\nproduction   Active  2h ago\nstaging      Active  5m ago\ndevelopment  Down    1d ago\n\nJSON (for programmatic use):\n[\n  {\"env\": \"production\", \"status\": \"active\", \"updated\": \"2h ago\"},\n  {\"env\": \"staging\", \"status\": \"active\", \"updated\": \"5m ago\"}\n]\n```\n\n### Lists\n\n```\nBulleted:\nFeatures:\n  • TypeScript support\n  • Hot reload\n  • Auto-formatting\n\nNumbered:\nSteps to deploy:\n  1. Build application\n  2. Run tests\n  3. Deploy to server\n  4. Verify deployment\n\nTree:\nmy-app/\n├── src/\n│   ├── components/\n│   └── utils/\n├── tests/\n└── package.json\n```\n\n## Status Messages\n\n### Real-time Updates\n\n```\nMulti-step process:\n✓ Dependencies installed (2.3s)\n✓ Application built (8.1s)\n⠋ Running tests... (current)\n⏳ Deploying... (pending)\n⏳ Verifying... (pending)\n\nUpdates:\n⠋ Installing dependencies...\n  → npm install\n✓ Dependencies installed (2.3s)\n\n⠋ Building application...\n  → webpack build\n✓ Application built (8.1s)\n  → Output: dist/ (2.4 MB)\n```\n\n### Summary/Completion\n\n```\n✓ Deployment complete!\n\nSummary:\n  Environment:  production\n  Version:      v1.2.3\n  Duration:     2m 34s\n  Deployed:     2023-12-14 10:30:45 UTC\n\nNext steps:\n  • View logs: mycli logs production\n  • Monitor:   mycli status production\n  • Rollback:  mycli rollback production\n\nURL: https://app.example.com\n```\n\n## Debugging & Verbose Mode\n\n```\nNormal mode (default):\n✓ Deployed to production (2m 34s)\n\nVerbose mode (--verbose):\n[10:30:12] Starting deployment...\n[10:30:13] Loading config from ./mycli.config.yml\n[10:30:14] Connecting to production server...\n[10:30:15] Uploading files (124 files, 2.4 MB)...\n[10:30:28] Running post-deploy hooks...\n[10:32:46] ✓ Deployment complete\n\nDebug mode (--debug):\n[DEBUG] Config loaded: {env: 'production', ...}\n[DEBUG] SSH connection established: user@host\n[DEBUG] Executing: rsync -avz ./dist/ user@host:/var/www/\n[DEBUG] Output: sending incremental file list...\n[DEBUG] Exit code: 0\n✓ Deployed to production (2m 34s)\n\nUsage:\n# Normal: concise output\nmycli deploy production\n\n# Verbose: detailed steps\nmycli deploy production --verbose\n\n# Debug: everything including internals\nDEBUG=* mycli deploy production\n```\n\n## Man Page Format\n\n```\nNAME\n    mycli-deploy - Deploy application to environment\n\nSYNOPSIS\n    mycli deploy <environment> [options]\n\nDESCRIPTION\n    Deploy your application to the specified environment.\n    Supports development, staging, and production environments.\n\nOPTIONS\n    -c, --config <file>\n        Path to configuration file\n        Default: ./mycli.config.yml\n\n    -f, --force\n        Skip all confirmation prompts\n        Use with caution in production\n\n    -d, --dry-run\n        Preview deployment without executing\n        Shows what would be deployed\n\nEXAMPLES\n    Deploy to production:\n        mycli deploy production\n\n    Preview staging deployment:\n        mycli deploy staging --dry-run\n\nSEE ALSO\n    mycli-init(1), mycli-config(1), mycli-rollback(1)\n```\n","isInternal":false,"tokens":2574,"sizeBytes":10115},{"name":"SKILL.md","path":"skills/cli-developer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cli-developer/SKILL.md","title":"cli-developer","category":"anthropic-skill","format":"markdown","content":"---\nname: cli-developer\ndescription: Use when building CLI tools, implementing argument parsing, or adding interactive prompts. Invoke for parsing flags and subcommands, displaying progress bars and spinners, generating bash/zsh/fish completion scripts, CLI design, shell completions, and cross-platform terminal applications using commander, click, typer, or cobra.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: devops\n  triggers: CLI, command-line, terminal app, argument parsing, shell completion, interactive prompt, progress bar, commander, click, typer, cobra\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: devops-engineer\n---\n\n# CLI Developer\n\n## Core Workflow\n\n1. **Analyze UX** — Identify user workflows, command hierarchy, common tasks. Validate by listing all commands and their expected `--help` output before writing code.\n2. **Design commands** — Plan subcommands, flags, arguments, configuration. Confirm flag naming is consistent and no existing signatures are broken.\n3. **Implement** — Build with the appropriate CLI framework for the language (see Reference Guide below). After wiring up commands, run `<cli> --help` to verify help text renders correctly and `<cli> --version` to confirm version output.\n4. **Polish** — Add completions, help text, error messages, progress indicators. Verify TTY detection for color output and graceful SIGINT handling.\n5. **Test** — Run cross-platform smoke tests; benchmark startup time (target: <50ms).\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Design Patterns | `references/design-patterns.md` | Subcommands, flags, config, architecture |\n| Node.js CLIs | `references/node-cli.md` | commander, yargs, inquirer, chalk |\n| Python CLIs | `references/python-cli.md` | click, typer, argparse, rich |\n| Go CLIs | `references/go-cli.md` | cobra, viper, bubbletea |\n| UX Patterns | `references/ux-patterns.md` | Progress bars, colors, help text |\n\n## Quick-Start Example\n\n### Node.js (commander)\n\n```js\n#!/usr/bin/env node\n// npm install commander\nconst { program } = require('commander');\n\nprogram\n  .name('mytool')\n  .description('Example CLI')\n  .version('1.0.0');\n\nprogram\n  .command('greet <name>')\n  .description('Greet a user')\n  .option('-l, --loud', 'uppercase the greeting')\n  .action((name, opts) => {\n    const msg = `Hello, ${name}!`;\n    console.log(opts.loud ? msg.toUpperCase() : msg);\n  });\n\nprogram.parse();\n```\n\nFor Python (click/typer) and Go (cobra) quick-start examples, see `references/python-cli.md` and `references/go-cli.md`.\n\n## Constraints\n\n### MUST DO\n- Keep startup time under 50ms\n- Provide clear, actionable error messages\n- Support `--help` and `--version` flags\n- Use consistent flag naming conventions\n- Handle SIGINT (Ctrl+C) gracefully\n- Validate user input early\n- Support both interactive and non-interactive modes\n- Test on Windows, macOS, and Linux\n\n### MUST NOT DO\n\n- **Block on synchronous I/O unnecessarily** — use async reads or stream processing instead.\n- **Print to stdout when output will be piped** — write logs/diagnostics to stderr.\n- **Use colors when output is not a TTY** — detect before applying color:\n  ```js\n  // Node.js\n  const useColor = process.stdout.isTTY;\n  ```\n  ```python\n  # Python\n  import sys\n  use_color = sys.stdout.isatty()\n  ```\n  ```go\n  // Go\n  import \"golang.org/x/term\"\n  useColor := term.IsTerminal(int(os.Stdout.Fd()))\n  ```\n- **Break existing command signatures** — treat flag/subcommand renames as breaking changes.\n- **Require interactive input in CI/CD environments** — always provide non-interactive fallbacks via flags or env vars.\n- **Hardcode paths or platform-specific logic** — use `os.homedir()` / `os.UserHomeDir()` / `Path.home()` instead.\n- **Ship without shell completions** — all three frameworks above have built-in completion generation.\n\n## Output Templates\n\nWhen implementing CLI features, provide:\n1. Command structure (main entry point, subcommands)\n2. Configuration handling (files, env vars, flags)\n3. Core implementation with error handling\n4. Shell completion scripts if applicable\n5. Brief explanation of UX decisions\n\n## Knowledge Reference\n\nCLI frameworks (commander, yargs, oclif, click, typer, argparse, cobra, viper), terminal UI (chalk, inquirer, rich, bubbletea), testing (snapshot testing, E2E), distribution (npm, pip, homebrew, releases), performance optimization\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/devops/cli-developer/)\n","frontmatter":{"name":"cli-developer","description":"Use when building CLI tools, implementing argument parsing, or adding interactive prompts. Invoke for parsing flags and subcommands, displaying progress bars and spinners, generating bash/zsh/fish completion scripts, CLI design, shell completions, and cross-platform terminal applications using commander, click, typer, or cobra.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"devops","triggers":"CLI, command-line, terminal app, argument parsing, shell completion, interactive prompt, progress bar, commander, click, typer, cobra","role":"specialist","scope":"implementation","output-format":"code","related-skills":"devops-engineer"}},"isInternal":false,"tokens":1153,"sizeBytes":4790},{"name":"aws.md","path":"skills/cloud-architect/references/aws.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cloud-architect/references/aws.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# AWS Architecture Reference\n\nComprehensive guide for AWS services, patterns, and Well-Architected Framework implementation.\n\n## Well-Architected Framework\n\n### Six Pillars\n\n1. **Operational Excellence**\n   - Infrastructure as Code (CloudFormation, CDK, Terraform)\n   - Continuous integration/deployment\n   - Observability (CloudWatch, X-Ray)\n   - Runbooks and playbooks\n   - Game days and failure injection\n\n2. **Security**\n   - Identity and Access Management (IAM)\n   - Detective controls (GuardDuty, Security Hub)\n   - Infrastructure protection (VPC, security groups, NACLs)\n   - Data protection (KMS, encryption at rest/transit)\n   - Incident response automation\n\n3. **Reliability**\n   - Multi-AZ deployments\n   - Auto Scaling groups\n   - Route 53 health checks and failover\n   - Backup and restore (AWS Backup)\n   - Chaos engineering (AWS FIS)\n\n4. **Performance Efficiency**\n   - Right-sizing with Compute Optimizer\n   - Caching strategies (CloudFront, ElastiCache)\n   - Database optimization (RDS Performance Insights)\n   - Serverless architectures\n   - Global content delivery\n\n5. **Cost Optimization**\n   - Reserved Instances and Savings Plans\n   - Spot Instances for fault-tolerant workloads\n   - S3 Intelligent-Tiering and lifecycle policies\n   - Right-sizing recommendations\n   - Cost allocation tags and budgets\n\n6. **Sustainability**\n   - Region selection for renewable energy\n   - Serverless to minimize idle resources\n   - Efficient data storage patterns\n   - Resource utilization optimization\n\n## Core Services Architecture\n\n### Compute\n\n**EC2 (Elastic Compute Cloud)**\n- Instance families: General (t3, m5), Compute (c5), Memory (r5), GPU (p3, g4)\n- Auto Scaling: Target tracking, step scaling, scheduled scaling\n- Placement groups: Cluster, partition, spread\n- Best practices: Use latest generation, right-size, enable detailed monitoring\n\n**Lambda**\n- Invocation models: Synchronous, asynchronous, event source mapping\n- Concurrency: Reserved, provisioned, burst limits\n- Layers for shared dependencies\n- Best practices: Keep functions small, use environment variables, set timeouts\n\n**ECS/EKS (Container Services)**\n- ECS: Fargate for serverless, EC2 for control\n- EKS: Managed Kubernetes with AWS integration\n- Service mesh: App Mesh for observability\n- Best practices: Use Fargate for simplicity, EKS for portability\n\n**Elastic Beanstalk**\n- Managed platform for web apps\n- Auto-scaling and load balancing included\n- Support for multiple languages and Docker\n\n### Storage\n\n**S3 (Simple Storage Service)**\n- Storage classes: Standard, IA, One Zone-IA, Glacier, Deep Archive\n- Lifecycle policies for automatic tiering\n- Versioning and MFA delete for protection\n- Cross-region replication for DR\n- Best practices: Enable versioning, use lifecycle policies, block public access\n\n**EBS (Elastic Block Store)**\n- Volume types: gp3 (general), io2 (IOPS), st1 (throughput), sc1 (cold)\n- Snapshots to S3 for backup\n- Encryption by default\n- Best practices: Use gp3 for most workloads, enable encryption\n\n**EFS (Elastic File System)**\n- NFSv4 file system for shared access\n- Performance modes: General purpose, Max I/O\n- Throughput modes: Bursting, provisioned\n- Best practices: Use lifecycle management, enable encryption\n\n**FSx**\n- FSx for Windows File Server (SMB)\n- FSx for Lustre (HPC workloads)\n- FSx for NetApp ONTAP\n- FSx for OpenZFS\n\n### Database\n\n**RDS (Relational Database Service)**\n- Engines: MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, Aurora\n- Multi-AZ for high availability\n- Read replicas for scalability\n- Automated backups and point-in-time recovery\n- Best practices: Use Aurora for performance, enable Multi-AZ, use read replicas\n\n**Aurora**\n- MySQL and PostgreSQL compatible\n- 5x MySQL, 3x PostgreSQL performance\n- Global databases for cross-region DR\n- Serverless v2 for variable workloads\n- Best practices: Use Aurora Serverless for unpredictable workloads\n\n**DynamoDB**\n- NoSQL key-value and document database\n- On-demand or provisioned capacity\n- Global tables for multi-region replication\n- DynamoDB Streams for change data capture\n- Best practices: Use on-demand for unpredictable traffic, implement GSI carefully\n\n**ElastiCache**\n- Redis or Memcached in-memory caching\n- Cluster mode for Redis scalability\n- Best practices: Use for session storage, API caching\n\n### Networking\n\n**VPC (Virtual Private Cloud)**\n- CIDR planning: Avoid overlaps, plan for growth\n- Subnets: Public (IGW), private (NAT), isolated (no internet)\n- Route tables and routing decisions\n- Security groups (stateful) and NACLs (stateless)\n- Best practices: Use /16 for VPC, /24 for subnets, plan IP space\n\n**Route 53**\n- DNS service with health checks\n- Routing policies: Simple, weighted, latency, failover, geolocation\n- Best practices: Use alias records, enable DNSSEC\n\n**CloudFront**\n- Global CDN with edge locations\n- Origin types: S3, ALB, custom origins\n- Lambda@Edge for request/response manipulation\n- Best practices: Enable compression, use field-level encryption\n\n**VPN and Direct Connect**\n- Site-to-Site VPN for encrypted tunnels\n- Direct Connect for dedicated bandwidth\n- Transit Gateway for hub-and-spoke topology\n- Best practices: Use Direct Connect for high bandwidth, Transit Gateway for complex routing\n\n**API Gateway**\n- REST APIs, HTTP APIs, WebSocket APIs\n- Throttling and quotas\n- Integration with Lambda, HTTP endpoints, AWS services\n- Best practices: Use HTTP APIs for lower cost, implement caching\n\n### Security\n\n**IAM (Identity and Access Management)**\n- Principle of least privilege\n- Roles for applications, not access keys\n- MFA for privileged users\n- Service Control Policies (SCPs) for organization-wide controls\n- Best practices: Use roles, enable MFA, rotate credentials\n\n**KMS (Key Management Service)**\n- Customer managed keys (CMKs)\n- Automatic key rotation\n- Envelope encryption pattern\n- Best practices: Enable automatic rotation, use grants for temporary access\n\n**Secrets Manager**\n- Automatic rotation for RDS credentials\n- Versioning and rollback\n- Best practices: Rotate secrets regularly, use VPC endpoints\n\n**Security Hub**\n- Centralized security findings\n- CIS AWS Foundations Benchmark\n- Integration with GuardDuty, Inspector, Macie\n\n**GuardDuty**\n- Threat detection using ML\n- Monitors CloudTrail, VPC Flow Logs, DNS logs\n\n## Architecture Patterns\n\n### High Availability\n\n**Multi-AZ Pattern**\n```\n- Application Load Balancer across 3 AZs\n- Auto Scaling group with instances in each AZ\n- RDS Multi-AZ for database\n- S3 for static assets (11 9's durability)\n```\n\n**Multi-Region Pattern**\n```\n- Route 53 with health checks and failover\n- CloudFront for global distribution\n- Aurora Global Database for <1s RPO\n- S3 Cross-Region Replication\n```\n\n### Serverless Architecture\n\n**API-Driven Pattern**\n```\nAPI Gateway -> Lambda -> DynamoDB\n              |\n              v\n          EventBridge -> Lambda (async processing)\n```\n\n**Event-Driven Pattern**\n```\nS3 Event -> Lambda -> Process -> SNS\n                                  |\n                                  v\n                              Multiple subscribers\n```\n\n### Microservices on AWS\n\n**Container-Based**\n```\nALB -> ECS Fargate (multiple services)\n    |\n    v\nService Discovery (Cloud Map)\n    |\n    v\nRDS/DynamoDB per service\n```\n\n**Service Mesh**\n```\nApp Mesh for traffic management\nX-Ray for distributed tracing\nCloudWatch Container Insights\n```\n\n### Data Lake Architecture\n\n```\nData Sources -> Kinesis Data Streams\n                      |\n                      v\n              Kinesis Firehose\n                      |\n                      v\n                S3 (raw bucket)\n                      |\n                      v\n        Glue ETL or Lambda processing\n                      |\n                      v\n            S3 (processed bucket)\n                      |\n                      v\n          Athena/Redshift Spectrum\n                      |\n                      v\n              QuickSight dashboards\n```\n\n## Migration Strategies (6Rs)\n\n1. **Rehost (Lift-and-Shift)**\n   - AWS Application Migration Service (MGN)\n   - Minimal changes, quick migration\n   - Use for legacy apps with compliance constraints\n\n2. **Replatform (Lift-Tinker-and-Shift)**\n   - Migrate to RDS instead of self-managed databases\n   - Use Elastic Beanstalk instead of custom app servers\n   - Small optimizations during migration\n\n3. **Repurchase (Drop-and-Shop)**\n   - Move to SaaS (e.g., Salesforce, Workday)\n   - Reduce maintenance burden\n\n4. **Refactor/Re-architect**\n   - Modernize to serverless or containers\n   - Highest effort, highest benefit\n   - Use for competitive advantage applications\n\n5. **Retire**\n   - Decommission unused applications\n   - Reduce attack surface and costs\n\n6. **Retain**\n   - Keep on-premises temporarily\n   - Migrate later or keep for regulatory reasons\n\n## Landing Zone Design\n\n**AWS Control Tower**\n- Multi-account strategy (AWS Organizations)\n- Account factory for standardization\n- Guardrails for governance (SCPs)\n- Centralized logging (CloudTrail, Config)\n\n**Account Structure**\n```\nRoot\n├── Security OU\n│   ├── Log Archive Account\n│   └── Security Tooling Account\n├── Infrastructure OU\n│   ├── Network Account (Transit Gateway, VPN)\n│   └── Shared Services Account\n└── Workloads OU\n    ├── Production Account\n    ├── Staging Account\n    └── Development Account\n```\n\n**Network Design**\n```\nTransit Gateway (hub)\n    |\n    ├── Production VPC\n    ├── Staging VPC\n    ├── Development VPC\n    └── On-premises (Direct Connect/VPN)\n```\n\n## Cost Optimization Strategies\n\n**Compute Savings**\n- Compute Savings Plans (up to 66% savings)\n- EC2 Reserved Instances (1-year or 3-year)\n- Spot Instances for batch/fault-tolerant workloads\n- Lambda: Reduce memory if possible, use reserved concurrency\n\n**Storage Savings**\n- S3 Intelligent-Tiering for unpredictable access\n- Lifecycle policies to Glacier/Deep Archive\n- EBS gp3 instead of gp2 (20% cheaper, better performance)\n- Delete unused snapshots and volumes\n\n**Database Savings**\n- Aurora Serverless v2 for variable workloads\n- RDS Reserved Instances\n- DynamoDB on-demand for unpredictable workloads\n- Read replicas in same region to reduce cross-AZ data transfer\n\n**Monitoring and Alerting**\n- AWS Cost Explorer for analysis\n- AWS Budgets for alerts\n- Cost Anomaly Detection\n- Trusted Advisor for recommendations\n\n## Disaster Recovery\n\n**RPO and RTO Targets**\n- Backup and Restore: Hours RPO/RTO (lowest cost)\n- Pilot Light: Minutes RPO, hours RTO\n- Warm Standby: Seconds RPO, minutes RTO\n- Multi-Site Active/Active: Near-zero RPO/RTO (highest cost)\n\n**Implementation**\n- AWS Backup for centralized backup management\n- Aurora Global Database for cross-region replication\n- S3 Cross-Region Replication\n- Route 53 health checks and failover routing\n- Regular DR testing with CloudFormation/Terraform\n\n## Monitoring and Observability\n\n**CloudWatch**\n- Metrics: Standard (5 min) and detailed (1 min)\n- Alarms with SNS notifications\n- Logs Insights for log analysis\n- Dashboards for visualization\n\n**X-Ray**\n- Distributed tracing for microservices\n- Service map visualization\n- Trace annotations and metadata\n\n**AWS Config**\n- Resource inventory and change tracking\n- Compliance rules evaluation\n- Relationship tracking between resources\n","isInternal":false,"tokens":2595,"sizeBytes":11309},{"name":"azure.md","path":"skills/cloud-architect/references/azure.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cloud-architect/references/azure.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Azure Architecture Reference\n\nComprehensive guide for Azure services, patterns, and Cloud Adoption Framework implementation.\n\n## Cloud Adoption Framework\n\n### Framework Phases\n\n1. **Strategy**\n   - Define business justification\n   - Expected business outcomes\n   - Business case development\n   - First project prioritization\n\n2. **Plan**\n   - Digital estate assessment\n   - Initial organization alignment\n   - Skills readiness plan\n   - Cloud adoption plan\n\n3. **Ready**\n   - Azure landing zone setup\n   - Azure setup guide\n   - Migration readiness\n   - Best practices validation\n\n4. **Adopt (Migrate + Innovate)**\n   - Migration: Assess, migrate, optimize\n   - Innovate: Build cloud-native solutions\n   - Best practices and patterns\n\n5. **Govern**\n   - Methodology for governance\n   - Governance benchmark\n   - Initial governance foundation\n   - Mature governance evolution\n\n6. **Manage**\n   - Business commitments\n   - Operations baseline\n   - Platform and workload specialization\n\n## Azure Well-Architected Framework\n\n### Five Pillars\n\n1. **Cost Optimization**\n   - Azure Cost Management and Billing\n   - Reserved instances and Savings Plans\n   - Azure Hybrid Benefit\n   - Auto-scaling and right-sizing\n\n2. **Operational Excellence**\n   - Infrastructure as Code (ARM, Bicep, Terraform)\n   - Azure DevOps and GitHub Actions\n   - Azure Monitor and Application Insights\n   - Deployment slots and blue-green deployments\n\n3. **Performance Efficiency**\n   - Azure CDN and Front Door\n   - Auto-scaling (VMSS, App Service)\n   - Caching (Redis, CDN)\n   - Performance diagnostics\n\n4. **Reliability**\n   - Availability Zones and regions\n   - Azure Site Recovery\n   - Load Balancer and Traffic Manager\n   - Backup and disaster recovery\n\n5. **Security**\n   - Azure AD (Entra ID)\n   - Network Security Groups and Firewalls\n   - Azure Key Vault\n   - Microsoft Defender for Cloud\n\n## Core Services Architecture\n\n### Compute\n\n**Virtual Machines**\n- VM sizes: General (D-series), Compute (F-series), Memory (E-series), GPU (N-series)\n- Availability Sets (99.95% SLA)\n- Availability Zones (99.99% SLA)\n- VM Scale Sets for auto-scaling\n- Best practices: Use managed disks, enable accelerated networking, use proximity placement groups\n\n**App Service**\n- Web Apps, API Apps, Mobile Apps\n- Deployment slots for staging\n- Auto-scaling based on metrics or schedule\n- Supports .NET, Java, Node.js, Python, PHP, Ruby\n- Best practices: Use deployment slots, enable auto-scaling, use App Service Plan efficiently\n\n**Azure Functions**\n- Consumption Plan (serverless)\n- Premium Plan (VNet integration, no cold start)\n- Dedicated Plan (App Service Plan)\n- Durable Functions for orchestration\n- Best practices: Keep functions small, use Premium for production, implement retry policies\n\n**Azure Kubernetes Service (AKS)**\n- Managed Kubernetes control plane\n- Azure CNI or kubenet networking\n- Azure AD integration\n- Virtual nodes (Azure Container Instances)\n- Best practices: Use system node pools, enable autoscaling, implement network policies\n\n**Container Instances**\n- Serverless containers\n- Fast startup without infrastructure management\n- Best for batch jobs and burstable workloads\n\n**Azure Batch**\n- Large-scale parallel and HPC workloads\n- Auto-scaling compute nodes\n- Task scheduling and dependencies\n\n### Storage\n\n**Blob Storage**\n- Storage tiers: Hot, Cool, Archive\n- Access tiers: Premium, Standard\n- Lifecycle management policies\n- Immutable storage for compliance\n- Best practices: Use lifecycle policies, enable soft delete, implement versioning\n\n**Azure Files**\n- SMB and NFS file shares\n- Integration with Azure File Sync\n- Premium tier for high performance\n- Best practices: Use Premium for databases, implement snapshots\n\n**Disk Storage**\n- Managed Disks: Premium SSD, Standard SSD, Standard HDD, Ultra Disk\n- Disk encryption with Azure Disk Encryption\n- Snapshots and incremental backups\n- Best practices: Use Premium SSD for production, enable encryption\n\n**Data Lake Storage Gen2**\n- Hierarchical namespace for big data\n- Built on Blob Storage\n- Integration with Azure Synapse and Databricks\n- Best practices: Enable hierarchical namespace, use lifecycle policies\n\n**Azure NetApp Files**\n- Enterprise-grade NFS and SMB shares\n- High performance and low latency\n- Snapshots and data protection\n\n### Database\n\n**Azure SQL Database**\n- Serverless and provisioned compute\n- Hyperscale for up to 100TB\n- Elastic pools for multiple databases\n- Auto-tuning and intelligent insights\n- Best practices: Use serverless for dev/test, enable geo-replication\n\n**Azure SQL Managed Instance**\n- Near 100% compatibility with SQL Server\n- VNet integration for isolation\n- Native virtual network implementation\n- Best practices: Use for lift-and-shift migrations\n\n**Cosmos DB**\n- Multi-model NoSQL database\n- Global distribution with multi-master\n- Consistency levels: Strong, Bounded staleness, Session, Consistent prefix, Eventual\n- APIs: SQL, MongoDB, Cassandra, Gremlin, Table\n- Best practices: Choose appropriate consistency, partition key design critical\n\n**Azure Database for PostgreSQL/MySQL/MariaDB**\n- Flexible Server (newer) vs Single Server (legacy)\n- High availability with zone redundancy\n- Read replicas for scaling\n- Best practices: Use Flexible Server, enable HA, implement connection pooling\n\n**Azure Cache for Redis**\n- In-memory caching\n- Clustering for scalability\n- Geo-replication for disaster recovery\n- Best practices: Use Premium tier for production, enable persistence\n\n### Networking\n\n**Virtual Network (VNet)**\n- CIDR planning (avoid overlaps)\n- Subnets with Network Security Groups\n- Service endpoints and Private Link\n- VNet peering for connectivity\n- Best practices: Plan IP address space, use NSGs, implement Private Link\n\n**Azure Load Balancer**\n- Layer 4 load balancing\n- Standard SKU (zone-redundant, SLA)\n- Health probes and distribution algorithms\n- Best practices: Use Standard SKU, configure health probes\n\n**Application Gateway**\n- Layer 7 load balancing\n- WAF (Web Application Firewall)\n- URL-based routing and SSL termination\n- Best practices: Enable WAF, use autoscaling\n\n**Azure Front Door**\n- Global load balancing and CDN\n- WAF at edge\n- Anycast for low latency\n- Best practices: Use for global applications, enable caching\n\n**VPN Gateway and ExpressRoute**\n- Site-to-Site VPN for encrypted connectivity\n- ExpressRoute for private, dedicated connection\n- Virtual WAN for global transit network\n- Best practices: Use ExpressRoute for production, implement redundancy\n\n**Azure Firewall**\n- Managed firewall service\n- Application and network rules\n- Threat intelligence\n- Best practices: Use in hub-spoke topology, enable DNS proxy\n\n**Azure Private Link**\n- Private connectivity to Azure services\n- No public internet exposure\n- Available for PaaS services\n- Best practices: Use for all PaaS services in production\n\n### Security and Identity\n\n**Azure Active Directory (Microsoft Entra ID)**\n- Identity and access management\n- Conditional Access policies\n- Multi-factor authentication\n- B2B and B2C scenarios\n- Best practices: Enable MFA, use Conditional Access, implement PIM\n\n**Azure Key Vault**\n- Secrets, keys, and certificates management\n- Hardware Security Module (HSM) backed\n- Soft delete and purge protection\n- Best practices: Enable soft delete, use RBAC, implement Private Link\n\n**Microsoft Defender for Cloud**\n- Security posture management\n- Threat protection for hybrid workloads\n- Regulatory compliance dashboard\n- Just-in-time VM access\n- Best practices: Enable enhanced security, implement recommendations\n\n**Azure Policy**\n- Governance and compliance at scale\n- Built-in and custom policies\n- Deny, audit, append effects\n- Best practices: Assign at management group level, test before enforce\n\n**Azure Sentinel**\n- Cloud-native SIEM and SOAR\n- AI-powered threat detection\n- Integration with Microsoft 365, third-party tools\n- Best practices: Enable data connectors, create custom analytics rules\n\n## Architecture Patterns\n\n### High Availability\n\n**Zone-Redundant Pattern**\n```\nAzure Front Door (global)\n    |\n    v\nApplication Gateway (zone-redundant)\n    |\n    v\nVM Scale Set (across availability zones)\n    |\n    v\nAzure SQL Database (zone-redundant)\n```\n\n**Multi-Region Pattern**\n```\nAzure Traffic Manager (DNS-based routing)\n    |\n    ├── Region 1: App Service + SQL Database (primary)\n    └── Region 2: App Service + SQL Database (geo-replica)\n```\n\n### Hub-Spoke Topology\n\n```\nHub VNet\n├── Azure Firewall\n├── VPN Gateway\n└── Shared Services\n    |\n    ├── Spoke VNet 1 (Production)\n    ├── Spoke VNet 2 (Development)\n    └── Spoke VNet 3 (DMZ)\n```\n\n### Serverless Architecture\n\n**Event-Driven Pattern**\n```\nEvent Grid -> Azure Functions -> Cosmos DB\n                    |\n                    v\n              Service Bus -> Functions (processing)\n```\n\n**API-First Pattern**\n```\nAPI Management\n    |\n    ├── Function App 1 (auth)\n    ├── Function App 2 (business logic)\n    └── Function App 3 (data access)\n```\n\n### Microservices on Azure\n\n**AKS-Based**\n```\nAzure Front Door\n    |\n    v\nApplication Gateway + WAF\n    |\n    v\nAKS (multiple microservices)\n    |\n    ├── Cosmos DB (microservice A)\n    ├── SQL Database (microservice B)\n    └── Service Bus (async communication)\n```\n\n**Container Apps Pattern**\n```\nAzure Container Apps\n├── Dapr for state management\n├── KEDA for event-driven scaling\n└── Azure Monitor for observability\n```\n\n### Data Platform\n\n```\nData Sources\n    |\n    v\nEvent Hubs / IoT Hub\n    |\n    v\nStream Analytics (real-time processing)\n    |\n    v\nData Lake Storage Gen2\n    |\n    v\nAzure Synapse Analytics\n    |\n    v\nPower BI (visualization)\n```\n\n## Landing Zone Design\n\n### Enterprise-Scale Landing Zone\n\n**Management Group Hierarchy**\n```\nTenant Root Group\n├── Platform\n│   ├── Management (monitoring, automation)\n│   ├── Connectivity (hub networks, VPN)\n│   └── Identity (domain controllers)\n└── Landing Zones\n    ├── Corp (internal workloads)\n    └── Online (internet-facing workloads)\n```\n\n**Network Topology**\n```\nHub VNet (Connectivity subscription)\n├── Azure Firewall\n├── VPN Gateway\n├── ExpressRoute Gateway\n└── Bastion\n\nSpoke VNets (Workload subscriptions)\n├── Production VNet\n├── Staging VNet\n└── Development VNet\n```\n\n**Governance**\n- Azure Policy for compliance\n- Management groups for hierarchy\n- RBAC assignments at appropriate scope\n- Resource tags for cost allocation\n- Azure Blueprints for repeatable deployments\n\n## Migration Strategies\n\n### Azure Migrate\n\n1. **Assess**\n   - Discovery with Azure Migrate appliance\n   - Dependency analysis\n   - Performance-based sizing\n   - Cost estimation\n\n2. **Migrate**\n   - Azure Migrate: Server Migration (agentless)\n   - Database Migration Service\n   - App Service Migration Assistant\n   - Data Box for large data transfers\n\n3. **Optimize**\n   - Right-sizing recommendations\n   - Reserved instances\n   - Azure Hybrid Benefit\n\n### Migration Patterns\n\n**Rehost**: Azure Migrate for VMs\n**Replatform**: App Service, Azure SQL Database\n**Refactor**: Container Apps, AKS, Functions\n**Rebuild**: Azure-native services (Cosmos DB, Cognitive Services)\n\n## Cost Optimization\n\n### Compute Savings\n- Azure Reserved Instances (1-year or 3-year, up to 72% savings)\n- Azure Savings Plans for Compute (up to 65% savings)\n- Spot VMs for fault-tolerant workloads (up to 90% savings)\n- Azure Hybrid Benefit (use existing Windows Server/SQL licenses)\n- Auto-shutdown for dev/test VMs\n\n### Storage Savings\n- Blob Storage lifecycle policies (Hot -> Cool -> Archive)\n- Azure Files: Standard tier for general use\n- Managed Disks: Standard SSD instead of Premium if possible\n- Delete unused snapshots and disks\n\n### Database Savings\n- Serverless tier for Azure SQL Database\n- Reserved capacity for Cosmos DB\n- DTU model vs vCore (choose based on workload)\n- Pause Azure Synapse when not in use\n\n### Monitoring\n- Azure Cost Management + Billing\n- Cost alerts and budgets\n- Azure Advisor recommendations\n- Resource tagging for cost allocation\n\n## Disaster Recovery\n\n### Azure Site Recovery\n\n**VM Replication**\n- Azure to Azure replication\n- On-premises to Azure (VMware, Hyper-V, physical)\n- RPO: 30 seconds to a few minutes\n- Automated failover and failback\n\n**Recovery Plans**\n- Multi-tier application recovery\n- Customizable scripts and manual actions\n- Integration with Azure Automation\n\n### Backup Strategies\n\n**Azure Backup**\n- VM backups (application-consistent)\n- SQL Server and SAP HANA in Azure VMs\n- Azure Files backup\n- Cross-region restore\n\n**Database Backup**\n- SQL Database: Automated backups (7-35 days)\n- Cosmos DB: Continuous backup (30 days)\n- Long-term retention policies\n\n### High Availability\n\n**RTO/RPO Targets**\n- Active-Active: Multi-region with Traffic Manager (near-zero)\n- Active-Passive: Geo-replication with failover (minutes)\n- Backup and Restore: Azure Backup (hours)\n\n## Monitoring and Observability\n\n### Azure Monitor\n\n**Components**\n- Metrics: Time-series data (1-minute resolution)\n- Logs: Log Analytics workspace for queries (KQL)\n- Alerts: Metric, log, and activity log alerts\n- Dashboards: Custom visualizations\n\n**Application Insights**\n- APM for web applications\n- Distributed tracing\n- Live Metrics Stream\n- Smart detection and anomaly detection\n- Best practices: Instrument all applications, set up availability tests\n\n### Log Analytics\n\n**KQL Queries**\n```kusto\n// Performance analysis\nPerf\n| where CounterName == \"% Processor Time\"\n| summarize avg(CounterValue) by bin(TimeGenerated, 5m), Computer\n| render timechart\n\n// Failed requests\nrequests\n| where success == false\n| summarize count() by resultCode, bin(timestamp, 1h)\n```\n\n**Workbooks**\n- Interactive reports\n- Parameterized queries\n- Combining metrics and logs\n\n## Identity and Access\n\n### Azure AD Best Practices\n\n- Enable MFA for all users\n- Use Conditional Access policies\n- Implement Privileged Identity Management (PIM)\n- Regular access reviews\n- Break-glass accounts\n\n### RBAC Design\n\n**Built-in Roles**\n- Owner: Full access including RBAC\n- Contributor: Full access except RBAC\n- Reader: Read-only access\n- Custom roles for specific needs\n\n**Scope Hierarchy**\n```\nManagement Group (highest)\n    |\nSubscription\n    |\nResource Group\n    |\nResource (lowest)\n```\n\nBest practices: Assign at highest appropriate scope, use groups not individual users, apply least privilege\n","isInternal":false,"tokens":3275,"sizeBytes":14468},{"name":"cost.md","path":"skills/cloud-architect/references/cost.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cloud-architect/references/cost.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Cloud Cost Optimization Reference\n\nComprehensive guide for cloud cost optimization including reserved instances, spot/preemptible, right-sizing, and FinOps practices.\n\n## FinOps Framework\n\n### FinOps Principles\n\n1. **Teams need to collaborate** - Finance, engineering, and business work together\n2. **Everyone takes ownership** - Decentralized cost responsibility\n3. **A centralized team drives FinOps** - Center of excellence for best practices\n4. **Reports should be accessible and timely** - Real-time visibility\n5. **Decisions are driven by business value** - Cost per business outcome\n6. **Take advantage of variable cost model** - Scale up and down as needed\n\n### FinOps Lifecycle\n\n```\n      Inform\n         |\n    +---------+\n    |         |\n    v         v\n Optimize --> Operate\n    ^         |\n    |         |\n    +---------+\n```\n\n**Inform Phase**\n- Visibility into cloud spend\n- Allocation and showback\n- Benchmarking and forecasting\n\n**Optimize Phase**\n- Rate optimization (RIs, savings plans)\n- Usage optimization (right-sizing)\n- Architectural optimization\n\n**Operate Phase**\n- Continuous improvement\n- Automation and governance\n- Anomaly detection\n\n## Compute Cost Optimization\n\n### Reserved Instances / Savings Plans\n\n**AWS Savings Plans**\n\n| Type | Flexibility | Savings |\n|------|-------------|---------|\n| Compute Savings Plans | Any EC2, Fargate, Lambda | Up to 66% |\n| EC2 Instance Savings Plans | Specific instance family, region | Up to 72% |\n| Reserved Instances | Specific instance type, AZ | Up to 72% |\n\n**Commitment Strategy**\n```\nBaseline (always-on): 1-year or 3-year Savings Plans\nVariable (predictable): Scheduled Reserved Instances\nSpiky (unpredictable): On-Demand + Spot\n```\n\n**Azure Reservations**\n```\n# Azure CLI - Purchase reservation\naz reservations reservation-order purchase \\\n  --sku Standard_D2s_v3 \\\n  --term P1Y \\\n  --billing-scope /subscriptions/{subscription-id} \\\n  --quantity 10 \\\n  --applied-scope-type Shared\n```\n\n**GCP Committed Use Discounts**\n- Resource-based: Specific vCPUs and memory\n- Spend-based: Dollar commitment for flexibility\n- 1-year (37% discount) or 3-year (55% discount)\n\n### Spot/Preemptible Instances\n\n**When to Use Spot**\n- Batch processing and analytics\n- CI/CD build agents\n- Stateless web servers (with auto-scaling)\n- Machine learning training\n- Development and testing environments\n\n**AWS Spot Best Practices**\n```yaml\n# EC2 Auto Scaling with Spot\nMixedInstancesPolicy:\n  InstancesDistribution:\n    OnDemandBaseCapacity: 2\n    OnDemandPercentageAboveBaseCapacity: 20\n    SpotAllocationStrategy: capacity-optimized\n  LaunchTemplate:\n    Overrides:\n      - InstanceType: m5.large\n      - InstanceType: m5a.large\n      - InstanceType: m4.large\n      - InstanceType: r5.large\n```\n\n**Spot Interruption Handling**\n```python\n# Check for spot termination notice (AWS)\nimport requests\n\ndef check_spot_termination():\n    try:\n        response = requests.get(\n            \"http://169.254.169.254/latest/meta-data/spot/termination-time\",\n            timeout=2\n        )\n        if response.status_code == 200:\n            # 2-minute warning - gracefully shutdown\n            graceful_shutdown()\n    except requests.exceptions.RequestException:\n        pass  # Not being terminated\n```\n\n**GCP Preemptible/Spot VMs**\n```hcl\n# Terraform - GCP Spot VM\nresource \"google_compute_instance\" \"spot\" {\n  name         = \"spot-instance\"\n  machine_type = \"n2-standard-4\"\n\n  scheduling {\n    preemptible                 = true\n    automatic_restart           = false\n    provisioning_model          = \"SPOT\"\n    instance_termination_action = \"STOP\"\n  }\n}\n```\n\n### Right-Sizing\n\n**Analysis Process**\n1. Collect metrics (CPU, memory, network, disk I/O)\n2. Identify idle or underutilized resources\n3. Recommend appropriate instance size\n4. Implement changes during maintenance windows\n5. Monitor and iterate\n\n**AWS Compute Optimizer**\n```bash\n# Enable Compute Optimizer\naws compute-optimizer update-enrollment-status \\\n  --status Active \\\n  --include-member-accounts\n\n# Get recommendations\naws compute-optimizer get-ec2-instance-recommendations \\\n  --filters name=Finding,values=OVER_PROVISIONED\n```\n\n**Right-Sizing Thresholds**\n| Metric | Underutilized | Optimal | Overutilized |\n|--------|---------------|---------|--------------|\n| CPU | <20% avg | 40-60% avg | >80% avg |\n| Memory | <30% avg | 50-70% avg | >85% avg |\n| Network | <10% capacity | Variable | >80% capacity |\n\n**Azure Advisor Recommendations**\n```bash\n# Get cost recommendations\naz advisor recommendation list \\\n  --category Cost \\\n  --query \"[?impact=='High']\"\n```\n\n## Storage Cost Optimization\n\n### Object Storage Tiering\n\n**AWS S3 Storage Classes**\n```\nS3 Standard\n    |\n    | (30 days)\n    v\nS3 Standard-IA\n    |\n    | (90 days)\n    v\nS3 Glacier Instant Retrieval\n    |\n    | (180 days)\n    v\nS3 Glacier Deep Archive\n```\n\n**Lifecycle Policy Example**\n```json\n{\n  \"Rules\": [\n    {\n      \"ID\": \"OptimizeCosts\",\n      \"Status\": \"Enabled\",\n      \"Filter\": { \"Prefix\": \"logs/\" },\n      \"Transitions\": [\n        { \"Days\": 30, \"StorageClass\": \"STANDARD_IA\" },\n        { \"Days\": 90, \"StorageClass\": \"GLACIER\" },\n        { \"Days\": 365, \"StorageClass\": \"DEEP_ARCHIVE\" }\n      ],\n      \"Expiration\": { \"Days\": 730 }\n    }\n  ]\n}\n```\n\n**S3 Intelligent-Tiering**\n- Automatic tiering based on access patterns\n- No retrieval fees\n- Small monitoring fee per object\n- Best for unpredictable access patterns\n\n### Block Storage Optimization\n\n**EBS Volume Selection**\n| Type | Use Case | $/GB/month |\n|------|----------|------------|\n| gp3 | General purpose | $0.08 |\n| gp2 | Legacy (migrate to gp3) | $0.10 |\n| io2 | High IOPS databases | $0.125+ |\n| st1 | Throughput (big data) | $0.045 |\n| sc1 | Cold archives | $0.015 |\n\n**gp3 Migration (20% savings)**\n```bash\n# Modify EBS volume from gp2 to gp3\naws ec2 modify-volume \\\n  --volume-id vol-12345678 \\\n  --volume-type gp3 \\\n  --iops 3000 \\\n  --throughput 125\n```\n\n### Database Storage\n\n**Aurora Storage Optimization**\n- Pay only for storage used (auto-scaling)\n- No pre-provisioning required\n- 10GB increments up to 128TB\n\n**DynamoDB Capacity Modes**\n| Mode | Best For | Pricing |\n|------|----------|---------|\n| On-Demand | Unpredictable traffic | Pay per request |\n| Provisioned | Steady traffic | Pay per capacity unit |\n| Provisioned + Auto Scaling | Variable but predictable | Lower cost than on-demand |\n\n## Network Cost Optimization\n\n### Data Transfer Costs\n\n**AWS Data Transfer Pricing**\n```\nInbound: Free\nSame AZ: Free\nCross-AZ: $0.01/GB each direction\nSame Region (via public IP): $0.01/GB\nCross-Region: $0.02/GB\nInternet Egress: $0.09/GB (first 10TB)\n```\n\n**Optimization Strategies**\n1. Keep traffic within same AZ when possible\n2. Use VPC endpoints for AWS services\n3. Use CloudFront for cacheable content\n4. Compress data before transfer\n5. Use regional rather than global services\n\n**VPC Endpoints (Avoid NAT Gateway)**\n```hcl\n# Gateway endpoint (free for S3, DynamoDB)\nresource \"aws_vpc_endpoint\" \"s3\" {\n  vpc_id       = aws_vpc.main.id\n  service_name = \"com.amazonaws.us-east-1.s3\"\n}\n\n# Interface endpoint (cheaper than NAT for specific services)\nresource \"aws_vpc_endpoint\" \"ecr\" {\n  vpc_id            = aws_vpc.main.id\n  service_name      = \"com.amazonaws.us-east-1.ecr.api\"\n  vpc_endpoint_type = \"Interface\"\n}\n```\n\n### CDN Optimization\n\n**CloudFront Cost Savings**\n- Lower data transfer rates than direct from origin\n- Cache hit ratio optimization (target >90%)\n- Use Origin Shield to reduce origin load\n- Compress objects (Gzip/Brotli)\n\n```yaml\n# CloudFront cache optimization\nCacheBehaviors:\n  - PathPattern: \"/static/*\"\n    CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6  # CachingOptimized\n    Compress: true\n    TTL:\n      DefaultTTL: 86400\n      MaxTTL: 31536000\n```\n\n## Serverless Cost Optimization\n\n### Lambda Optimization\n\n**Memory/CPU Tuning**\n```python\n# Use AWS Lambda Power Tuning\n# Finds optimal memory for cost vs performance\n\n# Results example:\n# 128MB:  $0.000021 per invocation, 3200ms duration\n# 256MB:  $0.000025 per invocation, 1600ms duration\n# 512MB:  $0.000031 per invocation, 800ms duration\n# 1024MB: $0.000042 per invocation, 450ms duration\n# Optimal: 512MB (best cost-performance balance)\n```\n\n**Cost Reduction Strategies**\n1. Right-size memory allocation\n2. Minimize cold starts (provisioned concurrency for critical paths)\n3. Use ARM64 (Graviton2) - 20% cheaper\n4. Optimize package size for faster cold starts\n5. Use Lambda Layers for shared dependencies\n\n**Graviton2 Migration**\n```yaml\n# SAM template with ARM64\nResources:\n  MyFunction:\n    Type: AWS::Serverless::Function\n    Properties:\n      Runtime: python3.11\n      Architectures:\n        - arm64  # 20% cost savings\n```\n\n### Container Optimization\n\n**Fargate Pricing Optimization**\n```\n# Fargate Spot: Up to 70% discount\n# Use for fault-tolerant workloads\n\nECS Service:\n  CapacityProviderStrategy:\n    - CapacityProvider: FARGATE_SPOT\n      Weight: 4\n    - CapacityProvider: FARGATE\n      Weight: 1\n      Base: 2  # Minimum on-demand tasks\n```\n\n**Right-Size Container Resources**\n```yaml\n# Analyze actual usage with Container Insights\nresources:\n  requests:\n    memory: \"256Mi\"  # Based on p95 usage + 20% buffer\n    cpu: \"100m\"      # Based on p95 usage + 20% buffer\n  limits:\n    memory: \"512Mi\"  # 2x requests for burst\n    cpu: \"500m\"\n```\n\n## Cost Allocation and Tagging\n\n### Tagging Strategy\n\n**Required Tags**\n```yaml\n# Terraform - enforce tags\nvariable \"required_tags\" {\n  default = {\n    environment  = \"prod\"\n    cost-center  = \"engineering\"\n    owner        = \"platform-team\"\n    project      = \"api-gateway\"\n    managed-by   = \"terraform\"\n  }\n}\n\nresource \"aws_instance\" \"example\" {\n  ami           = data.aws_ami.latest.id\n  instance_type = \"t3.medium\"\n  tags          = var.required_tags\n}\n```\n\n**Tag Enforcement**\n```json\n// AWS SCP - Deny untagged resources\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyUntaggedEC2\",\n      \"Effect\": \"Deny\",\n      \"Action\": \"ec2:RunInstances\",\n      \"Resource\": \"arn:aws:ec2:*:*:instance/*\",\n      \"Condition\": {\n        \"Null\": {\n          \"aws:RequestTag/cost-center\": \"true\"\n        }\n      }\n    }\n  ]\n}\n```\n\n### Cost Allocation Reports\n\n**AWS Cost and Usage Report**\n```bash\n# Enable detailed billing reports\naws cur put-report-definition \\\n  --report-definition '{\n    \"ReportName\": \"detailed-cost-report\",\n    \"TimeUnit\": \"HOURLY\",\n    \"Format\": \"Parquet\",\n    \"Compression\": \"Parquet\",\n    \"S3Bucket\": \"my-billing-bucket\",\n    \"S3Region\": \"us-east-1\",\n    \"AdditionalArtifacts\": [\"ATHENA\"]\n  }'\n```\n\n**Athena Queries for Analysis**\n```sql\n-- Cost by service and tag\nSELECT\n  line_item_product_code as service,\n  resource_tags_user_cost_center as cost_center,\n  SUM(line_item_unblended_cost) as cost\nFROM cost_report\nWHERE month = '2024-01'\nGROUP BY 1, 2\nORDER BY 3 DESC;\n\n-- Unused Reserved Instances\nSELECT\n  reservation_reservation_a_r_n,\n  reservation_unused_quantity,\n  reservation_unused_normalized_unit_quantity\nFROM cost_report\nWHERE reservation_unused_quantity > 0;\n```\n\n## Automation and Governance\n\n### Automated Cost Controls\n\n**AWS Budgets with Actions**\n```yaml\n# CloudFormation - Budget with auto-stop\nResources:\n  MonthlyCostBudget:\n    Type: AWS::Budgets::Budget\n    Properties:\n      Budget:\n        BudgetName: monthly-cost-limit\n        BudgetLimit:\n          Amount: 10000\n          Unit: USD\n        TimeUnit: MONTHLY\n        BudgetType: COST\n      NotificationsWithSubscribers:\n        - Notification:\n            NotificationType: ACTUAL\n            ComparisonOperator: GREATER_THAN\n            Threshold: 80\n          Subscribers:\n            - SubscriptionType: EMAIL\n              Address: finance@company.com\n```\n\n**Scheduled Scaling (Dev/Test)**\n```yaml\n# Stop non-prod resources nights/weekends\nResources:\n  ScaleDownSchedule:\n    Type: AWS::AutoScaling::ScheduledAction\n    Properties:\n      AutoScalingGroupName: !Ref DevASG\n      DesiredCapacity: 0\n      Recurrence: \"0 20 * * MON-FRI\"  # 8 PM weekdays\n\n  ScaleUpSchedule:\n    Type: AWS::AutoScaling::ScheduledAction\n    Properties:\n      AutoScalingGroupName: !Ref DevASG\n      DesiredCapacity: 3\n      Recurrence: \"0 8 * * MON-FRI\"   # 8 AM weekdays\n```\n\n### Cost Anomaly Detection\n\n**AWS Cost Anomaly Detection**\n```bash\n# Create anomaly monitor\naws ce create-anomaly-monitor \\\n  --anomaly-monitor '{\n    \"MonitorName\": \"ServiceMonitor\",\n    \"MonitorType\": \"DIMENSIONAL\",\n    \"MonitorDimension\": \"SERVICE\"\n  }'\n\n# Create anomaly subscription\naws ce create-anomaly-subscription \\\n  --anomaly-subscription '{\n    \"SubscriptionName\": \"CostAlerts\",\n    \"MonitorArnList\": [\"arn:aws:ce::123456789:anomalymonitor/abc123\"],\n    \"Subscribers\": [{\"Type\": \"EMAIL\", \"Address\": \"alerts@company.com\"}],\n    \"Threshold\": 100\n  }'\n```\n\n## Cost Metrics and KPIs\n\n### Key Metrics\n\n| Metric | Formula | Target |\n|--------|---------|--------|\n| Unit Cost | Total Cost / Business Metric | Decreasing |\n| Coverage | Reserved Hours / Total Hours | >70% |\n| Utilization | Used Reserved Hours / Purchased | >80% |\n| Waste | Idle Resource Cost / Total Cost | <10% |\n| Forecast Accuracy | Actual / Forecasted | 90-110% |\n\n### Dashboard Example\n\n```sql\n-- Cost efficiency dashboard metrics\nWITH metrics AS (\n  SELECT\n    date_trunc('month', usage_date) as month,\n    SUM(cost) as total_cost,\n    SUM(CASE WHEN reservation_arn IS NOT NULL THEN cost END) as reserved_cost,\n    COUNT(DISTINCT user_id) as active_users\n  FROM cloud_costs\n  GROUP BY 1\n)\nSELECT\n  month,\n  total_cost,\n  reserved_cost / total_cost as reservation_coverage,\n  total_cost / active_users as cost_per_user\nFROM metrics;\n```\n\n## Quick Wins Checklist\n\n**Immediate Savings (This Week)**\n- [ ] Delete unused EBS volumes and snapshots\n- [ ] Terminate stopped EC2 instances not needed\n- [ ] Remove unused Elastic IPs\n- [ ] Delete unused load balancers\n- [ ] Review and delete old AMIs\n\n**Short-Term (This Month)**\n- [ ] Right-size underutilized instances\n- [ ] Migrate gp2 volumes to gp3\n- [ ] Implement S3 lifecycle policies\n- [ ] Enable S3 Intelligent-Tiering\n- [ ] Schedule dev/test environments\n\n**Medium-Term (This Quarter)**\n- [ ] Purchase Savings Plans for baseline\n- [ ] Implement Spot for fault-tolerant workloads\n- [ ] Set up cost allocation tags\n- [ ] Enable Cost Anomaly Detection\n- [ ] Establish FinOps practices\n","isInternal":false,"tokens":3744,"sizeBytes":14289},{"name":"gcp.md","path":"skills/cloud-architect/references/gcp.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cloud-architect/references/gcp.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# GCP Architecture Reference\n\nComprehensive guide for Google Cloud Platform services, patterns, and architecture framework.\n\n## Google Cloud Architecture Framework\n\n### Five Pillars\n\n1. **Operational Excellence**\n   - Infrastructure as Code (Deployment Manager, Terraform)\n   - CI/CD with Cloud Build\n   - Monitoring with Cloud Monitoring (Stackdriver)\n   - SRE principles and SLOs\n   - Incident management\n\n2. **Security, Privacy, and Compliance**\n   - Identity and Access Management (Cloud IAM)\n   - VPC Service Controls for data perimeter\n   - Binary Authorization for containers\n   - Data encryption (default at rest and in transit)\n   - Security Command Center\n\n3. **Reliability**\n   - Multi-zone and multi-region deployments\n   - Load balancing and autoscaling\n   - Disaster recovery planning\n   - Chaos engineering practices\n   - SLIs, SLOs, and error budgets\n\n4. **Cost Optimization**\n   - Committed Use Discounts\n   - Sustained Use Discounts (automatic)\n   - Preemptible VMs and Spot VMs\n   - Recommender for right-sizing\n   - Active Assist for optimization\n\n5. **Performance Optimization**\n   - Cloud CDN and Media CDN\n   - Caching strategies (Memorystore)\n   - Database performance tuning\n   - Network optimization (Premium vs Standard tier)\n   - Regional and zonal resource placement\n\n## Core Services Architecture\n\n### Compute\n\n**Compute Engine**\n- Machine types: E2 (cost-optimized), N2 (balanced), C2 (compute-optimized), M2 (memory-optimized)\n- Custom machine types for specific needs\n- Preemptible VMs (up to 80% discount, max 24 hours)\n- Spot VMs (similar to preemptible, better availability)\n- Instance groups: Managed (with autoscaling), unmanaged\n- Best practices: Use latest generation, committed use discounts, Spot for batch jobs\n\n**Cloud Run**\n- Fully managed serverless container platform\n- Auto-scaling to zero\n- Pay per request\n- CPU allocated only during request handling\n- Best practices: Stateless containers, optimize cold starts, use Cloud Run jobs for batch\n\n**Cloud Functions**\n- Event-driven serverless functions\n- 1st gen: HTTP and background functions\n- 2nd gen: Built on Cloud Run, better performance\n- Event sources: Pub/Sub, Cloud Storage, Firestore, HTTP\n- Best practices: Use 2nd gen, minimize cold starts, implement retry logic\n\n**Google Kubernetes Engine (GKE)**\n- Managed Kubernetes with GCP integration\n- Autopilot mode: Fully managed, per-pod pricing\n- Standard mode: More control, node management\n- Workload Identity for secure service access\n- Binary Authorization for deployment policies\n- Best practices: Use Autopilot for simplicity, enable Workload Identity, implement network policies\n\n**App Engine**\n- Fully managed platform (PaaS)\n- Standard environment (sandboxed, auto-scaling)\n- Flexible environment (Docker containers, custom runtimes)\n- Traffic splitting for canary deployments\n- Best practices: Use Standard for web apps, Flexible for custom dependencies\n\n### Storage\n\n**Cloud Storage**\n- Storage classes: Standard, Nearline (30-day), Coldline (90-day), Archive (365-day)\n- Object lifecycle management\n- Object versioning and retention policies\n- Autoclass for automatic tier transitions\n- Requester pays for data transfer\n- Best practices: Use Autoclass, enable versioning, implement lifecycle policies\n\n**Persistent Disk**\n- Types: Standard (HDD), Balanced SSD, SSD, Extreme\n- Zonal and regional persistent disks\n- Snapshots for backup (incremental)\n- Disk resize without downtime\n- Best practices: Use Balanced SSD for most workloads, enable snapshots\n\n**Filestore**\n- Managed NFS file storage\n- Tiers: Basic (1-63.9 TB), Enterprise (1-10 TB, better performance)\n- Backup to Cloud Storage\n- Best practices: Use Enterprise for production, implement backups\n\n**Cloud Storage for Firebase**\n- Object storage for mobile and web apps\n- Client SDKs for direct upload/download\n- Security rules for access control\n\n### Database\n\n**Cloud SQL**\n- Managed MySQL, PostgreSQL, SQL Server\n- High availability configuration (regional)\n- Read replicas for scaling\n- Automated backups and point-in-time recovery\n- Best practices: Enable HA, use read replicas, implement connection pooling with Cloud SQL Proxy\n\n**Cloud Spanner**\n- Globally distributed relational database\n- Horizontal scalability with strong consistency\n- Multi-region for 99.999% availability\n- TrueTime for global consistency\n- Best practices: Design proper schema splits, use commit timestamps, optimize hotspots\n\n**Firestore (Native mode)**\n- NoSQL document database\n- Real-time synchronization\n- Offline support for mobile\n- ACID transactions\n- Best practices: Design document structure carefully, use collection group queries wisely\n\n**Bigtable**\n- NoSQL wide-column database\n- Petabyte-scale with single-digit millisecond latency\n- HBase API compatible\n- Linear scalability by adding nodes\n- Best practices: Design row keys to avoid hotspots, use replication for HA\n\n**Memorystore**\n- Managed Redis and Memcached\n- Standard tier (HA with replica) and Basic tier\n- Best practices: Use Standard tier for production, implement connection pooling\n\n**BigQuery**\n- Serverless data warehouse\n- SQL analytics on petabyte-scale data\n- Column-oriented storage\n- Automatic caching and optimization\n- Best practices: Partition and cluster tables, use approximate functions, control costs with quotas\n\n### Networking\n\n**VPC (Virtual Private Cloud)**\n- Global resource (subnets are regional)\n- Custom or auto mode networks\n- Firewall rules (stateful)\n- VPC peering and Shared VPC\n- Private Google Access for GCP services\n- Best practices: Use custom mode VPC, plan IP ranges, implement firewall rules\n\n**Cloud Load Balancing**\n- Global load balancing (HTTP(S), TCP/SSL Proxy, external TCP/UDP)\n- Regional load balancing (internal HTTP(S), internal TCP/UDP)\n- Anycast IP for global distribution\n- Backend services with health checks\n- Best practices: Use global for multi-region, enable CDN, configure health checks\n\n**Cloud CDN**\n- Global content delivery network\n- Cache invalidation and signed URLs\n- Integration with Cloud Storage and compute\n- Best practices: Enable compression, use cache-control headers\n\n**Cloud Interconnect and VPN**\n- Dedicated Interconnect (10 Gbps or 100 Gbps)\n- Partner Interconnect (50 Mbps to 50 Gbps)\n- Cloud VPN (HA VPN for 99.99% SLA)\n- Best practices: Use HA VPN for redundancy, Dedicated Interconnect for high bandwidth\n\n**Cloud Armor**\n- DDoS protection and WAF\n- Preconfigured and custom rules\n- Adaptive protection (ML-based)\n- Best practices: Enable for internet-facing services, use preconfigured rules\n\n**Private Service Connect**\n- Private connectivity to Google APIs and services\n- Service Directory for service discovery\n- Best practices: Use for all managed services in production\n\n### Serverless and Event-Driven\n\n**Pub/Sub**\n- Global message queue\n- At-least-once delivery\n- Push and pull subscriptions\n- Message ordering and filtering\n- Dead-letter topics\n- Best practices: Use message attributes for filtering, implement idempotent processing\n\n**Eventarc**\n- Event-driven architecture\n- Triggers for Cloud Run, Workflows, GKE\n- Sources: Audit Logs, Pub/Sub, custom events\n- Best practices: Use for decoupled architectures, implement event filtering\n\n**Cloud Scheduler**\n- Fully managed cron service\n- HTTP, Pub/Sub, and App Engine targets\n- Best practices: Use for periodic tasks, implement retry logic\n\n**Workflows**\n- Orchestrate and automate GCP and HTTP services\n- YAML-based workflow definition\n- Built-in error handling and retry\n- Best practices: Use for complex multi-step processes, implement compensating transactions\n\n### Security and Identity\n\n**Cloud IAM**\n- Resource hierarchy: Organization -> Folders -> Projects -> Resources\n- Roles: Primitive (Owner, Editor, Viewer), Predefined, Custom\n- Service accounts for applications\n- Workload Identity for GKE\n- Best practices: Use predefined roles, least privilege, service accounts for apps\n\n**Cloud Key Management (KMS)**\n- Encryption key management\n- Customer-managed encryption keys (CMEK)\n- Hardware Security Module (HSM) backed\n- Automatic key rotation\n- Best practices: Enable automatic rotation, use separate keys per environment\n\n**Secret Manager**\n- Store API keys, passwords, certificates\n- Versioning and access control\n- Automatic rotation integration\n- Best practices: Rotate secrets regularly, use IAM for access control\n\n**Security Command Center**\n- Centralized security and risk management\n- Asset discovery and vulnerability scanning\n- Threat detection and compliance monitoring\n- Best practices: Enable all detectors, review findings regularly\n\n**VPC Service Controls**\n- Create security perimeters around GCP resources\n- Prevent data exfiltration\n- Best practices: Use for sensitive data, implement access levels\n\n### AI and Machine Learning\n\n**Vertex AI**\n- Unified ML platform\n- AutoML for custom models\n- Pre-trained models (Vision, Natural Language, etc.)\n- MLOps with pipelines\n- Best practices: Use AutoML for quick start, implement feature store\n\n**BigQuery ML**\n- Create and execute ML models using SQL\n- Model types: Linear regression, logistic regression, clustering, etc.\n- Integration with Vertex AI\n- Best practices: Use for simple models, leverage BigQuery's scale\n\n## Architecture Patterns\n\n### High Availability\n\n**Multi-Zone Pattern**\n```\nGlobal HTTP(S) Load Balancer\n    |\n    v\nManaged Instance Group (multi-zone)\n    |\n    v\nCloud SQL (regional, HA configuration)\n    |\n    v\nCloud Storage (multi-region)\n```\n\n**Multi-Region Pattern**\n```\nGlobal HTTP(S) Load Balancer\n    |\n    ├── Backend Service Region 1 (Cloud Run)\n    └── Backend Service Region 2 (Cloud Run)\n         |\n         v\n    Cloud Spanner (multi-region)\n```\n\n### Serverless Architecture\n\n**Event-Driven Pattern**\n```\nCloud Storage upload event\n    |\n    v\nPub/Sub topic\n    |\n    v\nCloud Functions (image processing)\n    |\n    v\nFirestore (metadata storage)\n```\n\n**API-First Pattern**\n```\nCloud Endpoints or API Gateway\n    |\n    v\nCloud Run (multiple services)\n    |\n    ├── Cloud SQL (transactional data)\n    └── Firestore (user data)\n```\n\n### Microservices on GKE\n\n**GKE with Service Mesh**\n```\nGlobal Load Balancer\n    |\n    v\nGKE Ingress\n    |\n    v\nAnthos Service Mesh (Istio)\n    |\n    v\nMicroservices (Cloud Spanner, Firestore, Memorystore)\n```\n\n### Data Analytics Platform\n\n```\nData Sources\n    |\n    v\nPub/Sub (streaming)\n    |\n    v\nDataflow (Apache Beam)\n    |\n    v\nBigQuery (data warehouse)\n    |\n    v\nLooker or Data Studio (visualization)\n```\n\n**Batch Processing**\n```\nCloud Storage (raw data)\n    |\n    v\nDataproc (Apache Spark)\n    |\n    v\nBigQuery (analytics)\n```\n\n## Landing Zone Design\n\n### Resource Hierarchy\n\n```\nOrganization\n├── Folders (by environment or team)\n│   ├── Production Folder\n│   │   ├── Project A\n│   │   └── Project B\n│   ├── Staging Folder\n│   └── Development Folder\n└── Shared Services Folder\n    ├── Networking Project (Shared VPC host)\n    ├── Security Project (KMS, Secret Manager)\n    └── Logging Project (centralized logs)\n```\n\n### Network Design\n\n**Shared VPC Pattern**\n```\nHost Project (networking team)\n├── Shared VPC\n│   ├── Subnet Production (region A)\n│   ├── Subnet Staging (region A)\n│   └── Subnet Development (region B)\n\nService Projects (application teams)\n├── Production Project (uses Production subnet)\n├── Staging Project (uses Staging subnet)\n└── Development Project (uses Development subnet)\n```\n\n**Hub-and-Spoke with VPN**\n```\nOn-premises Network\n    |\n    v\nCloud VPN / Interconnect\n    |\n    v\nHub VPC (shared services)\n    |\n    ├── Spoke VPC 1 (production workloads)\n    ├── Spoke VPC 2 (development workloads)\n    └── Spoke VPC 3 (analytics workloads)\n```\n\n### Governance\n\n**Organization Policies**\n- Restrict public IP assignment\n- Enforce uniform bucket-level access\n- Restrict VM external IP\n- Define allowed resource locations\n\n**IAM Strategy**\n- Use Google Groups for role assignments\n- Separate duties (network admin, security admin, etc.)\n- Service accounts per application\n- Workload Identity for GKE workloads\n\n**Logging and Monitoring**\n```\nAll Projects\n    |\n    v\nLog Router\n    |\n    ├── Cloud Logging (default sink)\n    ├── BigQuery (long-term analysis)\n    ├── Cloud Storage (archive)\n    └── Pub/Sub (real-time processing)\n```\n\n## Migration Strategies\n\n### Migrate to Virtual Machines\n\n**Tools**\n- Migrate to Virtual Machines (formerly Migrate for Compute Engine)\n- Supports VMware, AWS, Azure, physical servers\n- Agentless or agent-based migration\n- Waves and test clones\n\n**Process**\n1. Assess: Fit assessment and TCO analysis\n2. Plan: Group VMs, define migration waves\n3. Deploy: Set up infrastructure (VPC, firewall rules)\n4. Migrate: Test migration, cutover, validation\n5. Optimize: Right-sizing, committed use discounts\n\n### Database Migration\n\n**Database Migration Service**\n- Minimal downtime migrations\n- Supports MySQL, PostgreSQL, SQL Server, Oracle\n- Continuous replication for cutover flexibility\n\n**Transfer Appliance**\n- Physical device for large data transfers\n- Up to 1 PB capacity\n- Offline data transfer\n\n## Cost Optimization\n\n### Compute Savings\n\n**Committed Use Discounts**\n- 1-year or 3-year commitments\n- Up to 57% savings for VMs\n- Resource-based or spend-based\n\n**Sustained Use Discounts**\n- Automatic discounts for running VMs >25% of month\n- Up to 30% savings\n- No commitment required\n\n**Preemptible and Spot VMs**\n- Up to 80% discount\n- Can be terminated by GCP\n- Best for batch processing, fault-tolerant workloads\n\n**Recommender**\n- VM rightsizing recommendations\n- Idle resource identification\n- Committed use discount recommendations\n\n### Storage Savings\n\n**Cloud Storage**\n- Autoclass for automatic tier transitions\n- Lifecycle policies (delete or transition)\n- Nearline (30+ days), Coldline (90+ days), Archive (365+ days)\n- Requester pays for data transfer\n\n**Persistent Disk**\n- Delete orphaned disks\n- Use balanced SSD instead of SSD when possible\n- Resize disks to match actual usage\n\n### BigQuery Savings\n\n**On-Demand Pricing**\n- $5 per TB processed\n- Use partitioning and clustering\n- Query cache for free repeated queries\n\n**Flat-Rate Pricing**\n- Predictable costs for heavy users\n- Autoscaling slots available\n- Flex slots for short-term commitments\n\n**Best Practices**\n- Use approximate aggregation functions (APPROX_COUNT_DISTINCT)\n- Avoid SELECT *, specify columns\n- Use materialized views for common queries\n- Set up cost controls with custom quotas\n\n### Monitoring Costs\n\n**Cloud Billing**\n- Budgets and alerts\n- Cost breakdown by project, service, SKU\n- Export to BigQuery for analysis\n- Recommendations from Active Assist\n\n## Disaster Recovery\n\n### Backup Strategies\n\n**VM Backups**\n- Persistent disk snapshots (incremental)\n- Machine images (include metadata and config)\n- Cross-region snapshot copy\n- Snapshot schedules for automation\n\n**Database Backups**\n- Cloud SQL: Automated backups (7-365 days retention)\n- Cloud Spanner: Backups on demand or scheduled\n- Firestore: Automated daily exports\n- Bigtable: Backups to Cloud Storage\n\n### High Availability\n\n**RTO/RPO Matrix**\n\n| Pattern | RPO | RTO | Cost |\n|---------|-----|-----|------|\n| Active-Active Multi-Region | Seconds | Seconds | High |\n| Active-Passive with Replication | Minutes | Minutes | Medium |\n| Warm Standby | Minutes | 10-30 min | Medium |\n| Backup and Restore | Hours | Hours | Low |\n\n**Cloud SQL HA**\n- Regional configuration with synchronous replication\n- Automatic failover\n- 99.95% SLA (vs 99.5% for single zone)\n\n**Cloud Spanner**\n- Multi-region configuration\n- 99.999% availability SLA\n- Synchronous replication across regions\n\n### Disaster Recovery Testing\n\n- Regular DR drills (quarterly recommended)\n- Document runbooks\n- Test restoration procedures\n- Measure actual RTO/RPO vs targets\n\n## Monitoring and Observability\n\n### Cloud Monitoring (formerly Stackdriver)\n\n**Metrics**\n- System metrics (CPU, memory, disk, network)\n- Custom metrics via Cloud Monitoring API\n- Metric scopes for multi-project monitoring\n- Uptime checks for availability\n\n**Dashboards and Charts**\n- Predefined dashboards for GCP services\n- Custom dashboards with filters and grouping\n- SLO monitoring with error budgets\n\n### Cloud Logging\n\n**Log Types**\n- Admin Activity logs (always enabled, no charge)\n- Data Access logs (must be enabled)\n- System Event logs\n- Access Transparency logs (for Google access)\n\n**Log Sinks**\n- Route logs to BigQuery, Cloud Storage, Pub/Sub\n- Aggregated sinks at organization/folder level\n- Exclusion filters to reduce costs\n\n### Cloud Trace\n\n**Distributed Tracing**\n- Automatic instrumentation for App Engine, Cloud Run, GKE\n- Manual instrumentation with client libraries\n- Latency analysis and performance insights\n- Integration with Zipkin\n\n### Cloud Profiler\n\n**Continuous Profiling**\n- CPU and memory profiling\n- Low overhead (< 0.5% CPU)\n- Flame graphs for visualization\n- Supported languages: Java, Go, Python, Node.js\n\n### Error Reporting\n\n**Aggregated Error Tracking**\n- Automatic error grouping\n- Stack trace analysis\n- Integration with Cloud Logging\n- Notifications for new errors\n","isInternal":false,"tokens":3903,"sizeBytes":17222},{"name":"multi-cloud.md","path":"skills/cloud-architect/references/multi-cloud.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cloud-architect/references/multi-cloud.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Multi-Cloud Architecture Reference\n\nComprehensive guide for multi-cloud strategies, abstraction layers, portability patterns, and vendor lock-in mitigation.\n\n## Multi-Cloud Strategy\n\n### When to Use Multi-Cloud\n\n**Valid Use Cases**\n- Regulatory compliance requiring data residency in specific regions\n- Best-of-breed service selection (BigQuery for analytics, AWS for ML)\n- Acquisition integration (different clouds in merged organizations)\n- Disaster recovery with cloud provider as failure domain\n- Negotiating leverage with cloud vendors\n\n**Poor Reasons for Multi-Cloud**\n- \"Avoiding vendor lock-in\" without specific exit scenario\n- Assuming portability is free (it has significant costs)\n- Political decisions without technical justification\n- Spreading workloads arbitrarily across providers\n\n### Multi-Cloud Patterns\n\n**Active-Active**\n```\nUsers -> Global Load Balancer\n              |\n    +---------+---------+\n    |                   |\n  AWS Region        GCP Region\n    |                   |\n    +----> Data Sync <--+\n```\n- Highest complexity and cost\n- Best for global latency optimization\n- Requires robust data synchronization\n\n**Active-Passive (DR)**\n```\nUsers -> Primary (AWS)\n              |\n         [Failover]\n              |\n         Secondary (Azure)\n```\n- Lower complexity than active-active\n- Cloud provider becomes failure domain\n- Cold or warm standby in secondary cloud\n\n**Segmented by Workload**\n```\nAnalytics -> GCP (BigQuery)\nCore App  -> AWS (ECS, RDS)\nOffice    -> Azure (M365 integration)\n```\n- Each workload on best-fit cloud\n- No cross-cloud data synchronization\n- Simplest multi-cloud pattern\n\n## Abstraction Layers\n\n### Infrastructure Abstraction\n\n**Terraform (Recommended)**\n```hcl\n# Provider-agnostic module structure\nmodule \"compute\" {\n  source = \"./modules/compute\"\n\n  provider_type = var.cloud_provider  # aws, azure, gcp\n  instance_type = var.instance_size\n  region        = var.region\n}\n\n# Provider-specific implementations\n# modules/compute/aws.tf\nresource \"aws_instance\" \"main\" {\n  count         = var.provider_type == \"aws\" ? 1 : 0\n  ami           = data.aws_ami.latest.id\n  instance_type = local.aws_instance_map[var.instance_size]\n}\n\n# modules/compute/azure.tf\nresource \"azurerm_virtual_machine\" \"main\" {\n  count    = var.provider_type == \"azure\" ? 1 : 0\n  vm_size  = local.azure_vm_map[var.instance_size]\n}\n```\n\n**Pulumi (Code-First)**\n```typescript\n// Abstract cloud resources with TypeScript\ninterface ComputeConfig {\n  size: \"small\" | \"medium\" | \"large\";\n  region: string;\n}\n\nfunction createCompute(config: ComputeConfig, provider: \"aws\" | \"gcp\") {\n  if (provider === \"aws\") {\n    return new aws.ec2.Instance(\"web\", {\n      instanceType: sizeMap.aws[config.size],\n      // ...\n    });\n  } else {\n    return new gcp.compute.Instance(\"web\", {\n      machineType: sizeMap.gcp[config.size],\n      // ...\n    });\n  }\n}\n```\n\n### Container Orchestration (Kubernetes)\n\n**Portable Kubernetes Deployment**\n```yaml\n# Same manifests work across EKS, AKS, GKE\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: web-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: web\n  template:\n    spec:\n      containers:\n      - name: web\n        image: myregistry/web:v1\n        resources:\n          requests:\n            memory: \"256Mi\"\n            cpu: \"250m\"\n          limits:\n            memory: \"512Mi\"\n            cpu: \"500m\"\n```\n\n**Cloud-Specific Considerations**\n| Feature | EKS | AKS | GKE |\n|---------|-----|-----|-----|\n| Load Balancer | ALB/NLB annotations | Azure LB | GCP LB |\n| Storage Class | gp3, io2 | managed-premium | pd-ssd |\n| IAM Integration | IRSA | Workload Identity | Workload Identity |\n| Ingress | AWS ALB Controller | AGIC | GKE Ingress |\n\n### Application Abstraction\n\n**Database Abstraction**\n```python\n# Use standard protocols (SQL, Redis, S3 API)\nfrom sqlalchemy import create_engine\n\n# Same code works with:\n# - AWS RDS PostgreSQL\n# - Azure Database for PostgreSQL\n# - GCP Cloud SQL PostgreSQL\n# - Self-managed PostgreSQL\n\nDATABASE_URL = os.environ[\"DATABASE_URL\"]\nengine = create_engine(DATABASE_URL)\n```\n\n**Object Storage Abstraction**\n```python\nimport boto3\nfrom botocore.config import Config\n\n# S3-compatible API works with:\n# - AWS S3\n# - GCP Cloud Storage (interoperability mode)\n# - MinIO\n# - Cloudflare R2\n\ns3_client = boto3.client(\n    's3',\n    endpoint_url=os.environ.get(\"S3_ENDPOINT\"),  # Override for non-AWS\n    aws_access_key_id=os.environ[\"ACCESS_KEY\"],\n    aws_secret_access_key=os.environ[\"SECRET_KEY\"],\n)\n```\n\n## Data Synchronization\n\n### Database Replication\n\n**Cross-Cloud PostgreSQL**\n```\nAWS RDS Primary\n      |\n      | Logical Replication\n      v\nGCP Cloud SQL Replica (read-only)\n```\n\nConfiguration:\n```sql\n-- On primary (AWS RDS)\nCREATE PUBLICATION my_publication FOR ALL TABLES;\n\n-- On replica (GCP Cloud SQL)\nCREATE SUBSCRIPTION my_subscription\n  CONNECTION 'host=aws-rds-endpoint dbname=mydb user=repl'\n  PUBLICATION my_publication;\n```\n\n**Conflict Resolution Strategies**\n- Last-write-wins (timestamp-based)\n- Application-level conflict resolution\n- CRDT data structures for eventually consistent data\n- Avoid multi-master for transactional data\n\n### Object Storage Sync\n\n**Rclone for Cross-Cloud Sync**\n```bash\n# Sync S3 to GCS\nrclone sync s3:my-bucket gcs:my-bucket \\\n  --transfers 32 \\\n  --checkers 16 \\\n  --s3-upload-concurrency 8\n\n# Bidirectional sync with conflict handling\nrclone bisync s3:bucket gcs:bucket \\\n  --conflict-resolve newer\n```\n\n**Event-Driven Replication**\n```\nS3 Bucket -> S3 Event -> Lambda -> GCS Upload\n                              |\n                              v\n                       Consistency Check\n```\n\n## Vendor Lock-In Mitigation\n\n### Lock-In Risk Assessment\n\n| Service Type | Lock-In Risk | Mitigation Strategy |\n|--------------|--------------|---------------------|\n| Compute (VMs) | Low | Standard OS images, IaC |\n| Kubernetes | Low | Portable manifests, avoid proprietary add-ons |\n| Object Storage | Low | S3-compatible API, standard formats |\n| Managed Databases | Medium | Standard SQL, logical backups |\n| Serverless Functions | High | Abstraction layers, containers |\n| Proprietary AI/ML | High | Open-source alternatives, ONNX models |\n| Managed Services | High | Evaluate portability before adoption |\n\n### Mitigation Strategies\n\n**1. Use Open Standards**\n- SQL databases over proprietary NoSQL\n- Kubernetes over ECS/Cloud Run\n- S3 API for object storage\n- OpenTelemetry for observability\n- OIDC for authentication\n\n**2. Abstract Proprietary Services**\n```typescript\n// Wrap cloud-specific services\ninterface QueueService {\n  send(message: string): Promise<void>;\n  receive(): Promise<string>;\n}\n\nclass SQSQueue implements QueueService {\n  async send(message: string) {\n    await this.sqsClient.sendMessage({ QueueUrl: this.url, MessageBody: message });\n  }\n}\n\nclass PubSubQueue implements QueueService {\n  async send(message: string) {\n    await this.pubsubClient.topic(this.topic).publish(Buffer.from(message));\n  }\n}\n\n// Factory pattern for cloud selection\nfunction createQueue(provider: string): QueueService {\n  switch (provider) {\n    case \"aws\": return new SQSQueue();\n    case \"gcp\": return new PubSubQueue();\n  }\n}\n```\n\n**3. Maintain Exit Capability**\n- Regular data export testing\n- Document cloud-specific dependencies\n- Keep IaC portable across providers\n- Estimate migration effort annually\n\n**4. Containerize Everything**\n```dockerfile\n# Portable container runs anywhere\nFROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\nEXPOSE 3000\nCMD [\"node\", \"server.js\"]\n```\n\n## Network Connectivity\n\n### Cross-Cloud Networking\n\n**VPN Interconnect**\n```\nAWS VPC                          GCP VPC\n   |                                |\n   +---> AWS VPN Gateway            |\n              |                     |\n              | IPsec tunnel        |\n              |                     |\n              +---> GCP Cloud VPN <-+\n```\n\n**Dedicated Interconnect (Enterprise)**\n```\nOn-Premises Data Center\n         |\n    +----+----+\n    |         |\nAWS Direct  GCP Cloud\nConnect     Interconnect\n    |         |\n    v         v\nAWS VPC    GCP VPC\n    |         |\n    +----+----+\n         |\n    Transit Hub (e.g., Megaport, Equinix)\n```\n\n### Service Mesh Across Clouds\n\n**Istio Multi-Cluster**\n```yaml\n# Primary cluster (AWS EKS)\napiVersion: install.istio.io/v1alpha1\nkind: IstioOperator\nspec:\n  values:\n    global:\n      meshID: multi-cloud-mesh\n      multiCluster:\n        clusterName: eks-primary\n      network: aws-network\n\n# Remote cluster (GCP GKE)\nspec:\n  values:\n    global:\n      meshID: multi-cloud-mesh\n      multiCluster:\n        clusterName: gke-secondary\n      network: gcp-network\n```\n\n## Cost Management\n\n### Cross-Cloud Cost Visibility\n\n**FinOps Tools**\n- CloudHealth by VMware\n- Apptio Cloudability\n- Spot.io (now part of NetApp)\n- Kubecost for Kubernetes\n\n**Unified Tagging Strategy**\n```\nRequired Tags (all clouds):\n- environment: prod/staging/dev\n- cost-center: engineering/marketing/sales\n- owner: team-name\n- project: project-code\n- managed-by: terraform/manual\n```\n\n### Cost Comparison Framework\n\n```\n| Workload Type | AWS | Azure | GCP | Decision |\n|---------------|-----|-------|-----|----------|\n| General Compute | EC2 m5 | D-series | n2-standard | Compare $/vCPU/hour |\n| GPU Training | p4d | NC-series | A2 | GCP often cheaper |\n| Object Storage | S3 | Blob | GCS | Similar, check egress |\n| Analytics | Redshift | Synapse | BigQuery | BigQuery for ad-hoc |\n| Kubernetes | EKS | AKS | GKE | GKE Autopilot simplest |\n```\n\n## Observability\n\n### Unified Monitoring Stack\n\n**OpenTelemetry Collector**\n```yaml\n# Collect from all clouds, export to single backend\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n      http:\n\nprocessors:\n  batch:\n  attributes:\n    actions:\n      - key: cloud.provider\n        action: upsert\n        value: ${CLOUD_PROVIDER}\n\nexporters:\n  prometheus:\n    endpoint: \"0.0.0.0:8889\"\n  jaeger:\n    endpoint: jaeger:14250\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch, attributes]\n      exporters: [jaeger]\n    metrics:\n      receivers: [otlp]\n      processors: [batch, attributes]\n      exporters: [prometheus]\n```\n\n**Grafana for Unified Dashboards**\n- AWS CloudWatch data source\n- Azure Monitor data source\n- GCP Cloud Monitoring data source\n- Single pane of glass across all clouds\n\n## Security Considerations\n\n### Identity Federation\n\n**Cross-Cloud Identity**\n```\nCorporate IdP (Okta/Azure AD)\n         |\n    SAML/OIDC\n         |\n    +----+----+----+\n    |    |    |    |\n  AWS  Azure  GCP  K8s\n  IAM   AD   IAM  RBAC\n```\n\n### Secrets Management\n\n**HashiCorp Vault (Cloud-Agnostic)**\n```hcl\n# Single secrets management across clouds\nresource \"vault_aws_secret_backend_role\" \"aws_role\" {\n  backend = vault_aws_secret_backend.aws.path\n  name    = \"app-role\"\n  credential_type = \"iam_user\"\n}\n\nresource \"vault_gcp_secret_roleset\" \"gcp_role\" {\n  backend     = vault_gcp_secret_backend.gcp.path\n  roleset     = \"app-role\"\n  project     = var.gcp_project\n  token_scopes = [\"https://www.googleapis.com/auth/cloud-platform\"]\n}\n```\n\n### Network Security\n\n**Zero-Trust Across Clouds**\n- mTLS between all services (service mesh)\n- No implicit trust based on network location\n- Identity-based access control\n- Encrypted transit between clouds (VPN/interconnect)\n","isInternal":false,"tokens":2807,"sizeBytes":11331},{"name":"SKILL.md","path":"skills/cloud-architect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cloud-architect/SKILL.md","title":"cloud-architect","category":"anthropic-skill","format":"markdown","content":"---\nname: cloud-architect\ndescription: Designs cloud architectures, creates migration plans, generates cost optimization recommendations, and produces disaster recovery strategies across AWS, Azure, and GCP. Use when designing cloud architectures, planning migrations, or optimizing multi-cloud deployments. Invoke for Well-Architected Framework, cost optimization, disaster recovery, landing zones, security architecture, serverless design.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: infrastructure\n  triggers: AWS, Azure, GCP, Google Cloud, cloud migration, cloud architecture, multi-cloud, cloud cost, Well-Architected, landing zone, cloud security, disaster recovery, cloud native, serverless architecture\n  role: architect\n  scope: infrastructure\n  output-format: architecture\n  related-skills: devops-engineer, kubernetes-specialist, terraform-engineer, security-reviewer, microservices-architect, monitoring-expert\n---\n\n# Cloud Architect\n\n## Core Workflow\n\n1. **Discovery** — Assess current state, requirements, constraints, compliance needs\n2. **Design** — Select services, design topology, plan data architecture\n3. **Security** — Implement zero-trust, identity federation, encryption\n4. **Cost Model** — Right-size resources, reserved capacity, auto-scaling\n5. **Migration** — Apply 6Rs framework, define waves, validate connectivity before cutover\n6. **Operate** — Set up monitoring, automation, continuous optimization\n\n### Workflow Validation Checkpoints\n\n**After Design:** Confirm every component has a redundancy strategy and no single points of failure exist in the topology.\n\n**Before Migration cutover:** Validate VPC peering or connectivity is fully established:\n```bash\n# AWS: confirm peering connection is Active before proceeding\naws ec2 describe-vpc-peering-connections \\\n  --filters \"Name=status-code,Values=active\"\n\n# Azure: confirm VNet peering state\naz network vnet peering list \\\n  --resource-group myRG --vnet-name myVNet \\\n  --query \"[].{Name:name,State:peeringState}\"\n```\n\n**After Migration:** Verify application health and routing:\n```bash\n# AWS: check target group health in ALB\naws elbv2 describe-target-health \\\n  --target-group-arn arn:aws:elasticloadbalancing:...\n```\n\n**After DR test:** Confirm RTO/RPO targets were met; document actual recovery times.\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| AWS Services | `references/aws.md` | EC2, S3, Lambda, RDS, Well-Architected Framework |\n| Azure Services | `references/azure.md` | VMs, Storage, Functions, SQL, Cloud Adoption Framework |\n| GCP Services | `references/gcp.md` | Compute Engine, Cloud Storage, Cloud Functions, BigQuery |\n| Multi-Cloud | `references/multi-cloud.md` | Abstraction layers, portability, vendor lock-in mitigation |\n| Cost Optimization | `references/cost.md` | Reserved instances, spot, right-sizing, FinOps practices |\n\n## Constraints\n\n### MUST DO\n- Design for high availability (99.9%+)\n- Implement security by design (zero-trust)\n- Use infrastructure as code (Terraform, CloudFormation)\n- Enable cost allocation tags and monitoring\n- Plan disaster recovery with defined RTO/RPO\n- Implement multi-region for critical workloads\n- Use managed services when possible\n- Document architectural decisions\n\n### MUST NOT DO\n- Store credentials in code or public repos\n- Skip encryption (at rest and in transit)\n- Create single points of failure\n- Ignore cost optimization opportunities\n- Deploy without proper monitoring\n- Use overly complex architectures\n- Ignore compliance requirements\n- Skip disaster recovery testing\n\n## Common Patterns with Examples\n\n### Least-Privilege IAM (Zero-Trust)\n\nRather than broad policies, scope permissions to specific resources and actions:\n\n```bash\n# AWS: create a scoped role for an application\naws iam create-role \\\n  --role-name AppRole \\\n  --assume-role-policy-document file://trust-policy.json\n\naws iam put-role-policy \\\n  --role-name AppRole \\\n  --policy-name AppInlinePolicy \\\n  --policy-document '{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n      \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\"],\n      \"Resource\": \"arn:aws:s3:::my-app-bucket/*\"\n    }]\n  }'\n```\n\n```hcl\n# Terraform equivalent\nresource \"aws_iam_role\" \"app_role\" {\n  name               = \"AppRole\"\n  assume_role_policy = data.aws_iam_policy_document.trust.json\n}\n\nresource \"aws_iam_role_policy\" \"app_policy\" {\n  role = aws_iam_role.app_role.id\n  policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [{\n      Effect   = \"Allow\"\n      Action   = [\"s3:GetObject\", \"s3:PutObject\"]\n      Resource = \"${aws_s3_bucket.app.arn}/*\"\n    }]\n  })\n}\n```\n\n### VPC with Public/Private Subnets (Terraform)\n\n```hcl\nresource \"aws_vpc\" \"main\" {\n  cidr_block           = \"10.0.0.0/16\"\n  enable_dns_hostnames = true\n  tags = { Name = \"main\", CostCenter = var.cost_center }\n}\n\nresource \"aws_subnet\" \"private\" {\n  count             = 2\n  vpc_id            = aws_vpc.main.id\n  cidr_block        = cidrsubnet(\"10.0.0.0/16\", 8, count.index)\n  availability_zone = data.aws_availability_zones.available.names[count.index]\n}\n\nresource \"aws_subnet\" \"public\" {\n  count                   = 2\n  vpc_id                  = aws_vpc.main.id\n  cidr_block              = cidrsubnet(\"10.0.0.0/16\", 8, count.index + 10)\n  availability_zone       = data.aws_availability_zones.available.names[count.index]\n  map_public_ip_on_launch = true\n}\n```\n\n### Auto-Scaling Group (Terraform)\n\n```hcl\nresource \"aws_autoscaling_group\" \"app\" {\n  desired_capacity    = 2\n  min_size            = 1\n  max_size            = 10\n  vpc_zone_identifier = aws_subnet.private[*].id\n\n  launch_template {\n    id      = aws_launch_template.app.id\n    version = \"$Latest\"\n  }\n\n  tag {\n    key                 = \"CostCenter\"\n    value               = var.cost_center\n    propagate_at_launch = true\n  }\n}\n\nresource \"aws_autoscaling_policy\" \"cpu_target\" {\n  autoscaling_group_name = aws_autoscaling_group.app.name\n  policy_type            = \"TargetTrackingScaling\"\n  target_tracking_configuration {\n    predefined_metric_specification {\n      predefined_metric_type = \"ASGAverageCPUUtilization\"\n    }\n    target_value = 60.0\n  }\n}\n```\n\n### Cost Analysis CLI\n\n```bash\n# AWS: identify top cost drivers for the last 30 days\naws ce get-cost-and-usage \\\n  --time-period Start=$(date -d '30 days ago' +%Y-%m-%d),End=$(date +%Y-%m-%d) \\\n  --granularity MONTHLY \\\n  --metrics \"UnblendedCost\" \\\n  --group-by Type=DIMENSION,Key=SERVICE \\\n  --query 'ResultsByTime[0].Groups[*].{Service:Keys[0],Cost:Metrics.UnblendedCost.Amount}' \\\n  --output table\n\n# Azure: review spend by resource group\naz consumption usage list \\\n  --start-date $(date -d '30 days ago' +%Y-%m-%d) \\\n  --end-date $(date +%Y-%m-%d) \\\n  --query \"[].{ResourceGroup:resourceGroup,Cost:pretaxCost,Currency:currency}\" \\\n  --output table\n```\n\n## Output Templates\n\nWhen designing cloud architecture, provide:\n1. Architecture diagram with services and data flow\n2. Service selection rationale (compute, storage, database, networking)\n3. Security architecture (IAM, network segmentation, encryption)\n4. Cost estimation and optimization strategy\n5. Deployment approach and rollback plan\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/infrastructure/cloud-architect/)\n","frontmatter":{"name":"cloud-architect","description":"Designs cloud architectures, creates migration plans, generates cost optimization recommendations, and produces disaster recovery strategies across AWS, Azure, and GCP. Use when designing cloud architectures, planning migrations, or optimizing multi-cloud deployments. Invoke for Well-Architected Framework, cost optimization, disaster recovery, landing zones, security architecture, serverless design.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"infrastructure","triggers":"AWS, Azure, GCP, Google Cloud, cloud migration, cloud architecture, multi-cloud, cloud cost, Well-Architected, landing zone, cloud security, disaster recovery, cloud native, serverless architecture","role":"architect","scope":"infrastructure","output-format":"architecture","related-skills":"devops-engineer, kubernetes-specialist, terraform-engineer, security-reviewer, microservices-architect, monitoring-expert"}},"isInternal":false,"tokens":1867,"sizeBytes":7539},{"name":"api-docs-fastapi-django.md","path":"skills/code-documenter/references/api-docs-fastapi-django.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/api-docs-fastapi-django.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# API Documentation: FastAPI & Django\n\n## FastAPI (Auto-generates from types)\n\nFastAPI automatically generates OpenAPI documentation from type hints and docstrings.\n\n### Endpoint Documentation\n\n```python\nfrom fastapi import FastAPI, HTTPException, status\nfrom pydantic import BaseModel, Field\n\nclass UserCreate(BaseModel):\n    \"\"\"User creation request body.\"\"\"\n\n    name: str = Field(..., min_length=1, max_length=100, example=\"John Doe\")\n    email: str = Field(..., example=\"john@example.com\")\n\nclass UserResponse(BaseModel):\n    \"\"\"User response with generated ID.\"\"\"\n\n    id: int = Field(..., example=1)\n    name: str\n    email: str\n\n@app.post(\n    \"/users\",\n    response_model=UserResponse,\n    status_code=status.HTTP_201_CREATED,\n    summary=\"Create a new user\",\n    tags=[\"Users\"],\n)\nasync def create_user(user: UserCreate) -> UserResponse:\n    \"\"\"Create a new user account.\n\n    Args:\n        user: User creation data including name and email.\n\n    Returns:\n        Created user with generated ID.\n\n    Raises:\n        HTTPException: 400 if email already exists.\n    \"\"\"\n```\n\n### Router with Tags\n\n```python\nfrom fastapi import APIRouter\n\nrouter = APIRouter(\n    prefix=\"/users\",\n    tags=[\"Users\"],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n@router.get(\n    \"/{user_id}\",\n    response_model=UserResponse,\n    summary=\"Get user by ID\",\n)\nasync def get_user(user_id: int) -> UserResponse:\n    \"\"\"Retrieve a user by their unique identifier.\"\"\"\n```\n\n## Django REST Framework (drf-spectacular)\n\n### ViewSet Documentation\n\n```python\nfrom rest_framework import viewsets, status\nfrom rest_framework.decorators import action\nfrom drf_spectacular.utils import extend_schema, OpenApiParameter\n\nclass UserViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    ViewSet for managing user accounts.\n\n    list: Get all users with pagination.\n    create: Create a new user account.\n    retrieve: Get a specific user by ID.\n    update: Update all user fields.\n    partial_update: Update specific user fields.\n    destroy: Delete a user account.\n    \"\"\"\n\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n    @extend_schema(\n        summary=\"Get current user\",\n        description=\"Returns the authenticated user's profile\",\n        responses={200: UserSerializer},\n    )\n    @action(detail=False, methods=[\"get\"])\n    def me(self, request):\n        \"\"\"Get the authenticated user's profile.\"\"\"\n        serializer = self.get_serializer(request.user)\n        return Response(serializer.data)\n```\n\n### Serializer Documentation\n\n```python\nfrom rest_framework import serializers\n\nclass UserSerializer(serializers.ModelSerializer):\n    \"\"\"Serializer for user model with validation.\"\"\"\n\n    class Meta:\n        model = User\n        fields = [\"id\", \"name\", \"email\", \"created_at\"]\n        read_only_fields = [\"id\", \"created_at\"]\n\n    name = serializers.CharField(\n        help_text=\"User's display name\",\n        max_length=100,\n    )\n    email = serializers.EmailField(\n        help_text=\"User's email address (unique)\",\n    )\n```\n\n### Custom Schema\n\n```python\nfrom drf_spectacular.utils import extend_schema, OpenApiExample\n\n@extend_schema(\n    request=UserCreateSerializer,\n    responses={\n        201: UserSerializer,\n        400: OpenApiTypes.OBJECT,\n    },\n    examples=[\n        OpenApiExample(\n            \"Valid request\",\n            value={\"name\": \"John\", \"email\": \"john@example.com\"},\n        ),\n    ],\n)\ndef create(self, request):\n    \"\"\"Create a new user.\"\"\"\n```\n\n## Quick Reference\n\n| Framework | Documentation Source | Output |\n|-----------|---------------------|--------|\n| FastAPI | Type hints + docstrings | Auto Swagger UI |\n| DRF | Serializers + drf-spectacular | Auto Swagger UI |\n\n| FastAPI Decorator | Purpose |\n|-------------------|---------|\n| `summary` | Short endpoint description |\n| `description` | Detailed description |\n| `tags` | Group endpoints |\n| `response_model` | Response schema |\n| `responses` | Additional response codes |\n\n| DRF Decorator | Purpose |\n|---------------|---------|\n| `@extend_schema` | Customize schema |\n| `OpenApiParameter` | Query/path params |\n| `OpenApiExample` | Request examples |\n","isInternal":false,"tokens":917,"sizeBytes":4148},{"name":"api-docs-nestjs-express.md","path":"skills/code-documenter/references/api-docs-nestjs-express.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/api-docs-nestjs-express.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# API Documentation: NestJS & Express\n\n## NestJS (@nestjs/swagger)\n\nNestJS requires explicit decorators for OpenAPI documentation.\n\n### Controller Documentation\n\n```typescript\nimport { Controller, Post, Body, Get, Param } from '@nestjs/common';\nimport {\n  ApiTags,\n  ApiOperation,\n  ApiResponse,\n  ApiParam,\n  ApiBearerAuth,\n} from '@nestjs/swagger';\n\n@ApiTags('Users')\n@ApiBearerAuth()\n@Controller('users')\nexport class UsersController {\n  @Post()\n  @ApiOperation({ summary: 'Create a new user' })\n  @ApiResponse({\n    status: 201,\n    description: 'User created successfully',\n    type: UserDto,\n  })\n  @ApiResponse({\n    status: 400,\n    description: 'Invalid input data',\n  })\n  async create(@Body() dto: CreateUserDto): Promise<UserDto> {\n    return this.usersService.create(dto);\n  }\n\n  @Get(':id')\n  @ApiOperation({ summary: 'Get user by ID' })\n  @ApiParam({\n    name: 'id',\n    description: 'User unique identifier',\n    example: '123',\n  })\n  @ApiResponse({ status: 200, type: UserDto })\n  @ApiResponse({ status: 404, description: 'User not found' })\n  async findOne(@Param('id') id: string): Promise<UserDto> {\n    return this.usersService.findOne(id);\n  }\n}\n```\n\n### DTO Documentation\n\n```typescript\nimport { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsEmail, IsString, MinLength } from 'class-validator';\n\nexport class CreateUserDto {\n  @ApiProperty({\n    description: \"User's display name\",\n    example: 'John Doe',\n    minLength: 1,\n    maxLength: 100,\n  })\n  @IsString()\n  @MinLength(1)\n  name: string;\n\n  @ApiProperty({\n    description: \"User's email address\",\n    example: 'john@example.com',\n  })\n  @IsEmail()\n  email: string;\n\n  @ApiPropertyOptional({\n    description: 'Profile picture URL',\n    example: 'https://example.com/avatar.jpg',\n  })\n  avatarUrl?: string;\n}\n```\n\n## Express (swagger-jsdoc)\n\nExpress uses JSDoc comments with swagger annotations.\n\n### Setup\n\n```javascript\nconst swaggerJsdoc = require('swagger-jsdoc');\nconst swaggerUi = require('swagger-ui-express');\n\nconst options = {\n  definition: {\n    openapi: '3.0.0',\n    info: {\n      title: 'API Documentation',\n      version: '1.0.0',\n    },\n  },\n  apis: ['./routes/*.js'],\n};\n\nconst specs = swaggerJsdoc(options);\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));\n```\n\n### Route Documentation\n\n```javascript\n/**\n * @swagger\n * /users:\n *   post:\n *     summary: Create a new user\n *     tags: [Users]\n *     requestBody:\n *       required: true\n *       content:\n *         application/json:\n *           schema:\n *             $ref: '#/components/schemas/CreateUser'\n *     responses:\n *       201:\n *         description: User created successfully\n *         content:\n *           application/json:\n *             schema:\n *               $ref: '#/components/schemas/User'\n *       400:\n *         description: Invalid input\n */\nrouter.post('/users', createUser);\n\n/**\n * @swagger\n * /users/{id}:\n *   get:\n *     summary: Get user by ID\n *     tags: [Users]\n *     parameters:\n *       - in: path\n *         name: id\n *         required: true\n *         schema:\n *           type: string\n *         description: User ID\n *     responses:\n *       200:\n *         description: User found\n *         content:\n *           application/json:\n *             schema:\n *               $ref: '#/components/schemas/User'\n *       404:\n *         description: User not found\n */\nrouter.get('/users/:id', getUser);\n```\n\n### Schema Documentation\n\n```javascript\n/**\n * @swagger\n * components:\n *   schemas:\n *     CreateUser:\n *       type: object\n *       required:\n *         - name\n *         - email\n *       properties:\n *         name:\n *           type: string\n *           description: User's display name\n *           example: John Doe\n *         email:\n *           type: string\n *           format: email\n *           description: User's email address\n *           example: john@example.com\n *     User:\n *       allOf:\n *         - $ref: '#/components/schemas/CreateUser'\n *         - type: object\n *           properties:\n *             id:\n *               type: string\n *               description: Unique identifier\n *             createdAt:\n *               type: string\n *               format: date-time\n */\n```\n\n## Quick Reference\n\n| NestJS Decorator | Purpose |\n|------------------|---------|\n| `@ApiTags()` | Group endpoints |\n| `@ApiOperation()` | Endpoint summary |\n| `@ApiResponse()` | Response documentation |\n| `@ApiParam()` | Path parameter |\n| `@ApiQuery()` | Query parameter |\n| `@ApiBody()` | Request body |\n| `@ApiBearerAuth()` | Auth requirement |\n| `@ApiProperty()` | DTO property |\n\n| Express swagger-jsdoc | Purpose |\n|-----------------------|---------|\n| `@swagger` | Start swagger block |\n| `tags` | Group endpoints |\n| `summary` | Short description |\n| `parameters` | Path/query params |\n| `requestBody` | Request body schema |\n| `responses` | Response schemas |\n| `$ref` | Reference schema |\n","isInternal":false,"tokens":1203,"sizeBytes":4950},{"name":"coverage-reports.md","path":"skills/code-documenter/references/coverage-reports.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/coverage-reports.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Coverage Reports\n\n## Documentation Coverage Report Template\n\n```markdown\n# Documentation Report: {project_name}\n\n## Summary\n- **Files analyzed**: 45\n- **Functions documented**: 120/150 (80%)\n- **Classes documented**: 25/25 (100%)\n- **API endpoints documented**: 30/30 (100%)\n\n## Coverage Before/After\n- Before: 45%\n- After: 92%\n\n## Files Modified\n\n| File | Functions Added | Notes |\n|------|-----------------|-------|\n| src/services/user.ts | 8 | All public methods |\n| src/services/auth.ts | 5 | Added examples |\n| src/controllers/users.ts | 6 | Added @Api decorators |\n| src/dto/user.dto.ts | 4 | Added @ApiProperty |\n\n## API Documentation\n\n- **Framework**: NestJS\n- **Strategy**: @nestjs/swagger decorators\n- **Swagger UI**: /api/docs\n- **OpenAPI spec**: /api-json\n\n## Documentation Style\n\n- **Python**: Google style docstrings\n- **TypeScript**: JSDoc with @param, @returns\n- **API**: OpenAPI 3.0 via decorators\n\n## Next Steps\n\n### Recommendations\n1. Run `npm run docs:lint` to validate JSDoc\n2. Add `eslint-plugin-jsdoc` to enforce documentation\n3. Consider adding examples for complex functions\n4. Set up documentation CI checks\n\n### Missing Documentation\n| File | Missing | Priority |\n|------|---------|----------|\n| src/utils/crypto.ts | 3 functions | High |\n| src/helpers/date.ts | 2 functions | Medium |\n\n### CI Integration\n```yaml\n# Add to CI pipeline\n- name: Check documentation\n  run: npm run docs:check\n\n- name: Generate API docs\n  run: npm run docs:generate\n```\n```\n\n## Checklist During Documentation\n\n```markdown\n## Documentation Checklist\n\n### Before Starting\n- [ ] Confirmed format preference (Google/JSDoc/etc.)\n- [ ] Identified files to exclude (tests, generated)\n- [ ] Detected framework for API docs\n\n### Functions/Methods\n- [ ] All public functions documented\n- [ ] Parameters described with types\n- [ ] Return values documented\n- [ ] Exceptions/errors documented\n- [ ] Examples added for complex functions\n\n### Classes\n- [ ] Class purpose described\n- [ ] Constructor parameters documented\n- [ ] Public methods documented\n- [ ] Important attributes explained\n\n### API Endpoints\n- [ ] All endpoints have summaries\n- [ ] Request bodies documented\n- [ ] Response schemas defined\n- [ ] Error responses documented\n- [ ] Authentication requirements noted\n\n### Final Checks\n- [ ] Ran documentation linter\n- [ ] Verified Swagger UI renders correctly\n- [ ] No inaccurate documentation\n- [ ] Coverage report generated\n```\n\n## Framework-Specific Linting\n\n```bash\n# JavaScript/TypeScript - ESLint\nnpm install eslint-plugin-jsdoc --save-dev\n# Add to .eslintrc: \"plugins\": [\"jsdoc\"]\n\n# Python - pydocstyle\npip install pydocstyle\npydocstyle --convention=google src/\n\n# Python - interrogate (coverage)\npip install interrogate\ninterrogate -v src/\n```\n\n## Quick Reference\n\n| Metric | Good | Acceptable | Poor |\n|--------|------|------------|------|\n| Function coverage | >90% | 70-90% | <70% |\n| Class coverage | 100% | >90% | <90% |\n| API endpoint coverage | 100% | 100% | <100% |\n| Example coverage | >50% | 30-50% | <30% |\n","isInternal":false,"tokens":773,"sizeBytes":3032},{"name":"documentation-systems.md","path":"skills/code-documenter/references/documentation-systems.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/documentation-systems.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Documentation Systems & Infrastructure\n\n## Static Site Generators\n\n### Docusaurus (Meta)\n\n```bash\n# Setup\nnpx create-docusaurus@latest docs classic\ncd docs && npm start\n\n# Structure\ndocs/\n├── docs/           # Documentation pages\n├── blog/           # Blog posts\n├── src/\n│   └── pages/      # Custom pages\n└── docusaurus.config.js\n```\n\n**docusaurus.config.js:**\n```javascript\nmodule.exports = {\n  title: 'My API',\n  tagline: 'Build amazing things',\n  url: 'https://docs.example.com',\n  baseUrl: '/',\n\n  themeConfig: {\n    navbar: {\n      items: [\n        {to: '/docs/intro', label: 'Docs', position: 'left'},\n        {to: '/api', label: 'API', position: 'left'},\n      ],\n    },\n\n    // Algolia search\n    algolia: {\n      apiKey: '<algolia-search-api-key>',\n      indexName: 'your_index',\n      contextualSearch: true,\n    },\n\n    prism: {\n      theme: lightCodeTheme,\n      darkTheme: darkCodeTheme,\n      additionalLanguages: ['python', 'rust'],\n    },\n  },\n};\n```\n\n### MkDocs (Python)\n\n```yaml\n# mkdocs.yml\nsite_name: My API Documentation\ntheme:\n  name: material\n  features:\n    - navigation.tabs\n    - navigation.sections\n    - toc.integrate\n    - search.suggest\n    - search.highlight\n  palette:\n    - scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n\nplugins:\n  - search\n  - mkdocstrings:\n      handlers:\n        python:\n          options:\n            show_source: true\n  - git-revision-date-localized\n\nmarkdown_extensions:\n  - pymdownx.highlight\n  - pymdownx.superfences\n  - admonition\n  - codehilite\n\nnav:\n  - Home: index.md\n  - Getting Started: getting-started.md\n  - API Reference: api/\n```\n\n### VitePress (Vue)\n\n```typescript\n// .vitepress/config.ts\nexport default defineConfig({\n  title: 'API Docs',\n  description: 'Developer documentation',\n\n  themeConfig: {\n    nav: [\n      { text: 'Guide', link: '/guide/' },\n      { text: 'API', link: '/api/' },\n    ],\n\n    sidebar: {\n      '/guide/': [\n        {\n          text: 'Introduction',\n          items: [\n            { text: 'Getting Started', link: '/guide/getting-started' },\n            { text: 'Configuration', link: '/guide/config' },\n          ],\n        },\n      ],\n    },\n\n    search: {\n      provider: 'local',\n    },\n\n    editLink: {\n      pattern: 'https://github.com/user/repo/edit/main/docs/:path',\n    },\n  },\n});\n```\n\n## Multi-Version Documentation\n\n### Version Switcher\n\n```javascript\n// Docusaurus versions\n{\n  versions: {\n    current: {\n      label: '2.0 (Next)',\n      path: 'next',\n    },\n  },\n  onlyIncludeVersions: ['current', '1.5', '1.4'],\n}\n```\n\n### Migration Guides\n\n```markdown\n# Migration Guide: v1 to v2\n\n## Breaking Changes\n\n### Authentication\n**v1:**\n```python\nclient.authenticate(api_key)\n```\n\n**v2:**\n```python\nclient = Client(api_key=api_key)  # Pass in constructor\n```\n\n### Renamed Methods\n| v1 | v2 | Notes |\n|----|----|----- |\n| `get_user()` | `fetch_user()` | Async now |\n| `delete_user()` | `remove_user()` | Returns Promise |\n\n## Deprecation Timeline\n- v1.x: Supported until Dec 2025\n- v2.0: Released Jan 2025\n- v2.1: Current (June 2025)\n```\n\n## Search Implementation\n\n### Algolia DocSearch\n\n```html\n<!-- Add to theme -->\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@docsearch/css@3\" />\n\n<script src=\"https://cdn.jsdelivr.net/npm/@docsearch/js@3\"></script>\n<script>\n  docsearch({\n    appId: '<algolia-app-id>',\n    apiKey: '<algolia-search-api-key>',\n    indexName: 'your_index',\n    container: '#docsearch',\n  });\n</script>\n```\n\n### Local Search (Lunr.js)\n\n```javascript\nconst idx = lunr(function() {\n  this.ref('id');\n  this.field('title', { boost: 10 });\n  this.field('content');\n\n  documents.forEach(doc => this.add(doc));\n});\n\n// Search\nconst results = idx.search('authentication');\n```\n\n## Documentation Testing\n\n### Link Checking\n\n```bash\n# linkcheck (Python)\npip install linkchecker\nlinkchecker http://localhost:3000/docs\n\n# broken-link-checker (Node)\nnpm install -g broken-link-checker\nblc http://localhost:3000 -ro\n```\n\n### Code Example Testing\n\n```python\n# doctest for Python examples\n\"\"\"\n>>> add(2, 3)\n5\n>>> add(-1, 1)\n0\n\"\"\"\n\n# Run tests\npython -m doctest -v docs/*.md\n```\n\n```javascript\n// Jest for TypeScript examples\n// Extract code blocks and test\nimport { runExamples } from './test-docs';\n\ntest('API examples work', async () => {\n  const examples = extractExamples('./docs/api.md');\n  await expect(runExamples(examples)).resolves.toBeTruthy();\n});\n```\n\n## Performance Optimization\n\n### Build Optimization\n\n```javascript\n// Webpack/Vite config\nexport default {\n  build: {\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          'vendor': ['react', 'react-dom'],\n        },\n      },\n    },\n  },\n\n  optimizeDeps: {\n    include: ['prismjs'],\n  },\n};\n```\n\n### CDN & Caching\n\n```nginx\n# nginx.conf\nlocation /docs {\n  expires 1y;\n  add_header Cache-Control \"public, immutable\";\n}\n\nlocation ~* \\.(html)$ {\n  expires 1h;\n  add_header Cache-Control \"public, must-revalidate\";\n}\n```\n\n## Analytics Integration\n\n### Google Analytics\n\n```javascript\n// Docusaurus\ngtag: {\n  trackingID: 'G-XXXXXXXXXX',\n  anonymizeIP: true,\n},\n```\n\n### Custom Analytics\n\n```javascript\n// Track search queries\nfunction trackSearch(query, results) {\n  analytics.track('docs_search', {\n    query,\n    resultCount: results.length,\n    timestamp: new Date(),\n  });\n}\n```\n\n## Quick Reference\n\n| Tool | Best For | Tech Stack |\n|------|----------|-----------|\n| Docusaurus | React projects, versioning | React, MDX |\n| MkDocs | Python projects, simple setup | Python, Jinja2 |\n| VitePress | Vue projects, fast builds | Vue, Vite |\n| Nextra | Next.js integration | React, Next.js |\n| Mintlify | Modern UI, AI search | React |\n\n| Search Solution | Cost | Features |\n|----------------|------|----------|\n| Algolia DocSearch | Free (OSS) | Fast, typo-tolerant |\n| Local (Lunr.js) | Free | Offline, no server |\n| Typesense | Free (self-host) | Privacy-focused |\n| Meilisearch | Free (self-host) | Fast, relevance |\n","isInternal":false,"tokens":1631,"sizeBytes":6109},{"name":"interactive-api-docs.md","path":"skills/code-documenter/references/interactive-api-docs.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/interactive-api-docs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Interactive API Documentation\n\n## OpenAPI 3.1 Advanced Features\n\n### Reusable Components\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: Users API\n  version: 2.0.0\n\ncomponents:\n  # Reusable schemas\n  schemas:\n    User:\n      type: object\n      required: [id, email]\n      properties:\n        id:\n          type: string\n          format: uuid\n          example: \"123e4567-e89b-12d3-a456-426614174000\"\n        email:\n          type: string\n          format: email\n          example: \"user@example.com\"\n\n    Error:\n      type: object\n      properties:\n        code:\n          type: string\n        message:\n          type: string\n        details:\n          type: object\n\n    PaginatedResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items: {}\n        total:\n          type: integer\n        page:\n          type: integer\n\n  # Reusable parameters\n  parameters:\n    PageParam:\n      name: page\n      in: query\n      schema:\n        type: integer\n        default: 1\n        minimum: 1\n\n    LimitParam:\n      name: limit\n      in: query\n      schema:\n        type: integer\n        default: 20\n        minimum: 1\n        maximum: 100\n\n  # Security schemes\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\n    ApiKeyAuth:\n      type: apiKey\n      in: header\n      name: X-API-Key\n\n    OAuth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://api.example.com/oauth/authorize\n          tokenUrl: https://api.example.com/oauth/token\n          scopes:\n            read:users: Read user data\n            write:users: Modify user data\n\n  # Reusable responses\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    Unauthorized:\n      description: Authentication required\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\npaths:\n  /users:\n    get:\n      summary: List users\n      parameters:\n        - $ref: '#/components/parameters/PageParam'\n        - $ref: '#/components/parameters/LimitParam'\n      security:\n        - BearerAuth: []\n      responses:\n        '200':\n          description: Success\n          content:\n            application/json:\n              schema:\n                allOf:\n                  - $ref: '#/components/schemas/PaginatedResponse'\n                  - type: object\n                    properties:\n                      data:\n                        type: array\n                        items:\n                          $ref: '#/components/schemas/User'\n```\n\n## Interactive Documentation Portals\n\n### Swagger UI Customization\n\n```javascript\n// Custom Swagger UI\nconst swaggerUi = require('swagger-ui-express');\nconst swaggerDocument = require('./openapi.json');\n\nconst options = {\n  customCss: '.swagger-ui .topbar { display: none }',\n  customSiteTitle: \"API Docs\",\n  customfavIcon: \"/favicon.ico\",\n  swaggerOptions: {\n    persistAuthorization: true,\n    displayRequestDuration: true,\n    filter: true,\n    tryItOutEnabled: true,\n    requestInterceptor: (req) => {\n      req.headers['X-Custom-Header'] = 'value';\n      return req;\n    },\n  },\n};\n\napp.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));\n```\n\n### Redoc (Modern Alternative)\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <title>API Documentation</title>\n  <link href=\"https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700\" rel=\"stylesheet\">\n</head>\n<body>\n  <redoc spec-url='./openapi.yaml'\n    hide-download-button\n    required-props-first\n    native-scrollbars\n    theme='{\n      \"colors\": {\n        \"primary\": {\n          \"main\": \"#4285F4\"\n        }\n      },\n      \"typography\": {\n        \"fontSize\": \"16px\",\n        \"fontFamily\": \"Roboto, sans-serif\"\n      }\n    }'>\n  </redoc>\n  <script src=\"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js\"></script>\n</body>\n</html>\n```\n\n### Stoplight Elements\n\n```javascript\nimport { API } from '@stoplight/elements';\nimport '@stoplight/elements/styles.min.css';\n\nfunction App() {\n  return (\n    <API\n      apiDescriptionUrl=\"./openapi.yaml\"\n      router=\"hash\"\n      layout=\"sidebar\"\n      tryItCredentialsPolicy=\"include\"\n    />\n  );\n}\n```\n\n## Multi-Protocol Documentation\n\n### GraphQL Schema Documentation\n\n```graphql\n\"\"\"\nUser account in the system\n\"\"\"\ntype User {\n  \"\"\"\n  Unique user identifier\n  \"\"\"\n  id: ID!\n\n  \"\"\"\n  User's email address (unique)\n  @example \"user@example.com\"\n  \"\"\"\n  email: String!\n\n  \"\"\"\n  Display name\n  @example \"John Doe\"\n  \"\"\"\n  name: String!\n\n  \"\"\"\n  User's posts (paginated)\n  \"\"\"\n  posts(\n    \"\"\"Number of items per page (max 100)\"\"\"\n    limit: Int = 20\n    \"\"\"Page offset\"\"\"\n    offset: Int = 0\n  ): PostConnection!\n}\n\ntype Query {\n  \"\"\"\n  Fetch a user by ID\n  \"\"\"\n  user(\n    \"\"\"User's unique identifier\"\"\"\n    id: ID!\n  ): User\n\n  \"\"\"\n  Search users by name or email\n  \"\"\"\n  searchUsers(\n    \"\"\"Search query\"\"\"\n    query: String!\n    \"\"\"Maximum results to return\"\"\"\n    limit: Int = 10\n  ): [User!]!\n}\n\ntype Mutation {\n  \"\"\"\n  Create a new user account\n  \"\"\"\n  createUser(\n    \"\"\"User creation input\"\"\"\n    input: CreateUserInput!\n  ): CreateUserPayload!\n}\n\n\"\"\"\nInput for creating a user\n\"\"\"\ninput CreateUserInput {\n  \"\"\"User's email address\"\"\"\n  email: String!\n  \"\"\"Display name\"\"\"\n  name: String!\n}\n```\n\n**GraphQL Playground:**\n```javascript\nconst { ApolloServer } = require('apollo-server');\n\nconst server = new ApolloServer({\n  typeDefs,\n  resolvers,\n  introspection: true,  // Enable in dev\n  playground: {\n    settings: {\n      'editor.theme': 'dark',\n      'editor.fontSize': 14,\n    },\n  },\n});\n```\n\n### WebSocket Protocol Documentation\n\n```yaml\n# AsyncAPI 2.0\nasyncapi: 2.5.0\ninfo:\n  title: Chat WebSocket API\n  version: 1.0.0\n  description: Real-time chat messaging\n\nchannels:\n  chat/{roomId}:\n    parameters:\n      roomId:\n        description: Chat room identifier\n        schema:\n          type: string\n\n    subscribe:\n      summary: Receive messages\n      message:\n        oneOf:\n          - $ref: '#/components/messages/ChatMessage'\n          - $ref: '#/components/messages/UserJoined'\n\n    publish:\n      summary: Send a message\n      message:\n        $ref: '#/components/messages/ChatMessage'\n\ncomponents:\n  messages:\n    ChatMessage:\n      name: message\n      payload:\n        type: object\n        properties:\n          userId:\n            type: string\n          content:\n            type: string\n          timestamp:\n            type: string\n            format: date-time\n\n    UserJoined:\n      name: userJoined\n      payload:\n        type: object\n        properties:\n          userId:\n            type: string\n          username:\n            type: string\n```\n\n### gRPC Documentation\n\n```protobuf\nsyntax = \"proto3\";\n\npackage users.v1;\n\n// User service manages user accounts\nservice UserService {\n  // Get a user by ID\n  // Returns: User object or NOT_FOUND error\n  rpc GetUser(GetUserRequest) returns (User) {}\n\n  // List all users with pagination\n  // Returns: Paginated list of users\n  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {}\n\n  // Create a new user\n  // Returns: Created user or ALREADY_EXISTS error\n  rpc CreateUser(CreateUserRequest) returns (User) {}\n\n  // Stream user updates in real-time\n  // Returns: Stream of user update events\n  rpc WatchUsers(WatchUsersRequest) returns (stream UserEvent) {}\n}\n\n// User account\nmessage User {\n  // Unique identifier\n  string id = 1;\n\n  // Email address (unique, required)\n  string email = 2;\n\n  // Display name\n  string name = 3;\n\n  // Account creation timestamp\n  google.protobuf.Timestamp created_at = 4;\n}\n```\n\n## SDK Documentation Strategies\n\n### Multi-Language Examples\n\n```markdown\n# Create User\n\n## Python\n```python\nfrom myapi import Client\n\nclient = Client(api_key=\"<api-key>\")\nuser = client.users.create(\n    name=\"John Doe\",\n    email=\"john@example.com\"\n)\nprint(user.id)\n```\n\n## TypeScript\n```typescript\nimport { Client } from '@myapi/sdk';\n\nconst client = new Client({ apiKey: '<api-key>' });\nconst user = await client.users.create({\n  name: 'John Doe',\n  email: 'john@example.com',\n});\nconsole.log(user.id);\n```\n\n## Go\n```go\nimport \"github.com/myapi/sdk-go\"\n\nclient := sdk.NewClient(\"<api-key>\")\nuser, err := client.Users.Create(ctx, &sdk.CreateUserInput{\n    Name:  \"John Doe\",\n    Email: \"john@example.com\",\n})\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Println(user.ID)\n```\n\n## Ruby\n```ruby\nrequire 'myapi'\n\nclient = MyAPI::Client.new(api_key: '<api-key>')\nuser = client.users.create(\n  name: 'John Doe',\n  email: 'john@example.com'\n)\nputs user.id\n```\n```\n\n### SDK Reference Template\n\n```markdown\n# Users SDK\n\n## Installation\n```bash\nnpm install @myapi/sdk\n```\n\n## Configuration\n```typescript\nimport { Client } from '@myapi/sdk';\n\nconst client = new Client({\n  apiKey: process.env.API_KEY,\n  baseUrl: 'https://api.example.com',  // Optional\n  timeout: 30000,  // Optional, default 30s\n});\n```\n\n## Methods\n\n### `client.users.create(data)`\nCreate a new user.\n\n**Parameters:**\n- `data.name` (string, required) - User's display name\n- `data.email` (string, required) - User's email address\n\n**Returns:** Promise<User>\n\n**Throws:**\n- `ValidationError` - Invalid input data\n- `ConflictError` - Email already exists\n- `AuthenticationError` - Invalid API key\n\n**Example:**\n```typescript\nconst user = await client.users.create({\n  name: 'John Doe',\n  email: 'john@example.com',\n});\n```\n\n## Error Handling\n```typescript\nimport { ValidationError, ConflictError } from '@myapi/sdk';\n\ntry {\n  await client.users.create(data);\n} catch (error) {\n  if (error instanceof ValidationError) {\n    console.error('Invalid data:', error.fields);\n  } else if (error instanceof ConflictError) {\n    console.error('User already exists');\n  }\n}\n```\n```\n\n## Quick Reference\n\n| Tool | Protocol | Features |\n|------|----------|----------|\n| Swagger UI | REST | Try-it-out, auth |\n| Redoc | REST | Clean, responsive |\n| Stoplight | REST | Modern, mock server |\n| GraphQL Playground | GraphQL | Explorer, history |\n| AsyncAPI Studio | WebSocket | Visual editor |\n| grpcui | gRPC | Interactive console |\n","isInternal":false,"tokens":2501,"sizeBytes":10194},{"name":"python-docstrings.md","path":"skills/code-documenter/references/python-docstrings.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/python-docstrings.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Python Docstrings\n\n## Google Style (Recommended)\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"Calculate total cost including tax.\n\n    Args:\n        items: List of items to calculate total for.\n        tax_rate: Tax rate as decimal (e.g., 0.08 for 8%).\n\n    Returns:\n        Total cost including tax.\n\n    Raises:\n        ValueError: If tax_rate is negative or items is empty.\n\n    Example:\n        >>> calculate_total([Item(10), Item(20)], 0.1)\n        33.0\n    \"\"\"\n```\n\n## NumPy Style\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"\n    Calculate total cost including tax.\n\n    Parameters\n    ----------\n    items : list[Item]\n        List of items to calculate total for.\n    tax_rate : float, optional\n        Tax rate as decimal (e.g., 0.08 for 8%). Default is 0.0.\n\n    Returns\n    -------\n    float\n        Total cost including tax.\n\n    Raises\n    ------\n    ValueError\n        If tax_rate is negative or items is empty.\n\n    Examples\n    --------\n    >>> calculate_total([Item(10), Item(20)], 0.1)\n    33.0\n    \"\"\"\n```\n\n## Sphinx Style\n\n```python\ndef calculate_total(items: list[Item], tax_rate: float = 0.0) -> float:\n    \"\"\"Calculate total cost including tax.\n\n    :param items: List of items to calculate total for.\n    :type items: list[Item]\n    :param tax_rate: Tax rate as decimal (e.g., 0.08 for 8%).\n    :type tax_rate: float\n    :returns: Total cost including tax.\n    :rtype: float\n    :raises ValueError: If tax_rate is negative or items is empty.\n\n    .. code-block:: python\n\n        >>> calculate_total([Item(10), Item(20)], 0.1)\n        33.0\n    \"\"\"\n```\n\n## Class Documentation\n\n```python\nclass UserService:\n    \"\"\"Service for managing user operations.\n\n    This service handles CRUD operations for users and\n    integrates with the authentication system.\n\n    Attributes:\n        db: Database session for queries.\n        cache: Redis client for caching.\n\n    Example:\n        >>> service = UserService(db, cache)\n        >>> user = await service.create_user(data)\n    \"\"\"\n\n    def __init__(self, db: AsyncSession, cache: Redis) -> None:\n        \"\"\"Initialize UserService.\n\n        Args:\n            db: Database session for queries.\n            cache: Redis client for caching.\n        \"\"\"\n```\n\n## Quick Reference\n\n| Style | Args Format | Returns Format |\n|-------|-------------|----------------|\n| Google | `Args:` block | `Returns:` block |\n| NumPy | `Parameters` section | `Returns` section |\n| Sphinx | `:param name:` | `:returns:` |\n\n## Sections Available\n\n| Section | Google | NumPy | Sphinx |\n|---------|--------|-------|--------|\n| Parameters | `Args:` | `Parameters` | `:param:` |\n| Returns | `Returns:` | `Returns` | `:returns:` |\n| Raises | `Raises:` | `Raises` | `:raises:` |\n| Examples | `Example:` | `Examples` | `.. code-block::` |\n| Notes | `Note:` | `Notes` | `.. note::` |\n| Attributes | `Attributes:` | `Attributes` | `:ivar:` |\n","isInternal":false,"tokens":756,"sizeBytes":2964},{"name":"typescript-jsdoc.md","path":"skills/code-documenter/references/typescript-jsdoc.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/typescript-jsdoc.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# TypeScript JSDoc\n\n## Function Documentation\n\n```typescript\n/**\n * Calculate total cost including tax.\n *\n * @param items - List of items to calculate total for\n * @param taxRate - Tax rate as decimal (e.g., 0.08 for 8%)\n * @returns Total cost including tax\n * @throws {Error} If taxRate is negative or items is empty\n *\n * @example\n * ```typescript\n * const total = calculateTotal(items, 0.08);\n * console.log(total); // 108.00\n * ```\n */\nfunction calculateTotal(items: Item[], taxRate = 0): number {\n```\n\n## Class Documentation\n\n```typescript\n/**\n * Service for managing user operations.\n *\n * Handles CRUD operations and integrates with authentication system.\n *\n * @example\n * ```typescript\n * const service = new UserService(db, cache);\n * const user = await service.create(userData);\n * ```\n */\nclass UserService {\n  /**\n   * Create a new UserService instance.\n   *\n   * @param db - Database connection\n   * @param cache - Redis cache client\n   */\n  constructor(\n    private readonly db: Database,\n    private readonly cache: Cache,\n  ) {}\n}\n```\n\n## Interface Documentation\n\n```typescript\n/**\n * User data transfer object.\n *\n * @interface UserDto\n */\ninterface UserDto {\n  /** Unique user identifier */\n  id: string;\n\n  /** User's email address (unique) */\n  email: string;\n\n  /** User's display name */\n  name: string;\n\n  /** Account creation timestamp */\n  createdAt: Date;\n}\n```\n\n## Generic Types\n\n```typescript\n/**\n * Paginated response wrapper.\n *\n * @template T - Type of items in the data array\n */\ninterface PaginatedResponse<T> {\n  /** Array of items for current page */\n  data: T[];\n\n  /** Total number of items across all pages */\n  total: number;\n\n  /** Current page number (1-indexed) */\n  page: number;\n\n  /** Number of items per page */\n  limit: number;\n}\n```\n\n## Async Functions\n\n```typescript\n/**\n * Fetch user by ID from database.\n *\n * @param id - User's unique identifier\n * @returns Promise resolving to user data or null if not found\n * @throws {DatabaseError} If connection fails\n *\n * @async\n */\nasync function findUserById(id: string): Promise<User | null> {\n```\n\n## Quick Reference\n\n| Tag | Purpose | Example |\n|-----|---------|---------|\n| `@param` | Parameter description | `@param name - User's name` |\n| `@returns` | Return value | `@returns User object` |\n| `@throws` | Exception thrown | `@throws {Error} If invalid` |\n| `@example` | Usage example | Code block |\n| `@see` | Reference link | `@see UserService` |\n| `@deprecated` | Mark deprecated | `@deprecated Use v2 instead` |\n| `@template` | Generic type param | `@template T - Item type` |\n| `@async` | Async function | Mark async |\n| `@private` | Private member | Internal use |\n| `@readonly` | Read-only property | Cannot modify |\n\n## Common Patterns\n\n```typescript\n// Optional parameters\n/** @param [options] - Optional configuration */\n\n// Default values\n/** @param [limit=10] - Items per page (default: 10) */\n\n// Multiple types\n/** @param input - Input value (string or number) */\n\n// Callback parameters\n/**\n * @callback FilterFn\n * @param item - Item to filter\n * @returns Whether item passes filter\n */\n```\n","isInternal":false,"tokens":766,"sizeBytes":3110},{"name":"user-guides-tutorials.md","path":"skills/code-documenter/references/user-guides-tutorials.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/references/user-guides-tutorials.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-documenter%2Freferences%2Fuser-guides-tutorials.md -->","isInternal":false,"tokens":2756,"sizeBytes":11002},{"name":"SKILL.md","path":"skills/code-documenter/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-documenter/SKILL.md","title":"code-documenter","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-documenter%2FSKILL.md -->","frontmatter":{"name":"code-documenter","description":"Generates, formats, and validates technical documentation — including docstrings, OpenAPI/Swagger specs, JSDoc annotations, doc portals, and user guides. Use when adding docstrings to functions or classes, creating API documentation, building documentation sites, or writing tutorials and user guides. Invoke for OpenAPI/Swagger specs, JSDoc, doc portals, getting started guides.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"quality","triggers":"documentation, docstrings, OpenAPI, Swagger, JSDoc, comments, API docs, tutorials, user guides, doc site","role":"specialist","scope":"implementation","output-format":"code","related-skills":"spec-miner, fullstack-guardian, code-reviewer"}},"isInternal":false,"tokens":1368,"sizeBytes":5808},{"name":"common-issues.md","path":"skills/code-reviewer/references/common-issues.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/references/common-issues.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Fcommon-issues.md -->","isInternal":false,"tokens":727,"sizeBytes":2999},{"name":"feedback-examples.md","path":"skills/code-reviewer/references/feedback-examples.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/references/feedback-examples.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Ffeedback-examples.md -->","isInternal":false,"tokens":809,"sizeBytes":3458},{"name":"receiving-feedback.md","path":"skills/code-reviewer/references/receiving-feedback.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/references/receiving-feedback.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freceiving-feedback.md -->","isInternal":false,"tokens":1532,"sizeBytes":6680},{"name":"report-template.md","path":"skills/code-reviewer/references/report-template.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/references/report-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freport-template.md -->","isInternal":false,"tokens":740,"sizeBytes":3075},{"name":"review-checklist.md","path":"skills/code-reviewer/references/review-checklist.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/references/review-checklist.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Freview-checklist.md -->","isInternal":false,"tokens":584,"sizeBytes":2485},{"name":"spec-compliance-review.md","path":"skills/code-reviewer/references/spec-compliance-review.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/references/spec-compliance-review.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2Freferences%2Fspec-compliance-review.md -->","isInternal":false,"tokens":1768,"sizeBytes":9009},{"name":"SKILL.md","path":"skills/code-reviewer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/code-reviewer/SKILL.md","title":"code-reviewer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcode-reviewer%2FSKILL.md -->","frontmatter":{"name":"code-reviewer","description":"Analyzes code diffs and files to identify bugs, security vulnerabilities (SQL injection, XSS, insecure deserialization), code smells, N+1 queries, naming issues, and architectural concerns, then produces a structured review report with prioritized, actionable feedback. Use when reviewing pull requests, conducting code quality audits, identifying refactoring opportunities, or checking for security issues. Invoke for PR reviews, code quality checks, refactoring suggestions, review code, code quality. Complements specialized skills (security-reviewer, test-master) by providing broad-scope review across correctness, performance, maintainability, and test coverage in a single pass.","license":"MIT","allowed-tools":"Read Grep Glob","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"quality","triggers":"code review, PR review, pull request, review code, code quality","role":"specialist","scope":"review","output-format":"report","related-skills":"security-reviewer, test-master, architecture-designer"},"allowedTools":["Read","Grep","Glob"]},"allowedTools":["Read","Grep","Glob"],"isInternal":false,"tokens":1188,"sizeBytes":5353},{"name":"build-tooling.md","path":"skills/cpp-pro/references/build-tooling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cpp-pro/references/build-tooling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fbuild-tooling.md -->","isInternal":false,"tokens":2712,"sizeBytes":9761},{"name":"concurrency.md","path":"skills/cpp-pro/references/concurrency.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cpp-pro/references/concurrency.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fconcurrency.md -->","isInternal":false,"tokens":2502,"sizeBytes":10918},{"name":"memory-performance.md","path":"skills/cpp-pro/references/memory-performance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cpp-pro/references/memory-performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fmemory-performance.md -->","isInternal":false,"tokens":2446,"sizeBytes":9104},{"name":"modern-cpp.md","path":"skills/cpp-pro/references/modern-cpp.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cpp-pro/references/modern-cpp.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Fmodern-cpp.md -->","isInternal":false,"tokens":1723,"sizeBytes":6565},{"name":"templates.md","path":"skills/cpp-pro/references/templates.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cpp-pro/references/templates.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2Freferences%2Ftemplates.md -->","isInternal":false,"tokens":1975,"sizeBytes":7908},{"name":"SKILL.md","path":"skills/cpp-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/cpp-pro/SKILL.md","title":"cpp-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcpp-pro%2FSKILL.md -->","frontmatter":{"name":"cpp-pro","description":"Writes, optimizes, and debugs C++ applications using modern C++20/23 features, template metaprogramming, and high-performance systems techniques. Use when building or refactoring C++ code requiring concepts, ranges, coroutines, SIMD optimization, or careful memory management — or when addressing performance bottlenecks, concurrency issues, and build system configuration with CMake.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"C++, C++20, C++23, modern C++, template metaprogramming, systems programming, performance optimization, SIMD, memory management, CMake","role":"specialist","scope":"implementation","output-format":"code","related-skills":"rust-engineer, embedded-systems"}},"isInternal":false,"tokens":1101,"sizeBytes":4672},{"name":"aspnet-core.md","path":"skills/csharp-developer/references/aspnet-core.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/csharp-developer/references/aspnet-core.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Faspnet-core.md -->","isInternal":false,"tokens":2109,"sizeBytes":10744},{"name":"blazor.md","path":"skills/csharp-developer/references/blazor.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/csharp-developer/references/blazor.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fblazor.md -->","isInternal":false,"tokens":2844,"sizeBytes":12940},{"name":"entity-framework.md","path":"skills/csharp-developer/references/entity-framework.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/csharp-developer/references/entity-framework.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fentity-framework.md -->","isInternal":false,"tokens":2393,"sizeBytes":11717},{"name":"modern-csharp.md","path":"skills/csharp-developer/references/modern-csharp.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/csharp-developer/references/modern-csharp.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fmodern-csharp.md -->","isInternal":false,"tokens":1481,"sizeBytes":6219},{"name":"performance.md","path":"skills/csharp-developer/references/performance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/csharp-developer/references/performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2Freferences%2Fperformance.md -->","isInternal":false,"tokens":2700,"sizeBytes":12454},{"name":"SKILL.md","path":"skills/csharp-developer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/csharp-developer/SKILL.md","title":"csharp-developer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fcsharp-developer%2FSKILL.md -->","frontmatter":{"name":"csharp-developer","description":"Use when building C# applications with .NET 8+, ASP.NET Core APIs, or Blazor web apps. Builds REST APIs using minimal or controller-based routing, configures database access with Entity Framework Core, implements async patterns and cancellation, structures applications with CQRS via MediatR, and scaffolds Blazor components with state management. Invoke for C#, .NET, ASP.NET Core, Blazor, Entity Framework, EF Core, Minimal API, MAUI, SignalR.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"C#, .NET, ASP.NET Core, Blazor, Entity Framework, EF Core, Minimal API, MAUI, SignalR","role":"specialist","scope":"implementation","output-format":"code","related-skills":"api-designer, database-optimizer, devops-engineer"}},"isInternal":false,"tokens":1231,"sizeBytes":5255},{"name":"index-strategies.md","path":"skills/database-optimizer/references/index-strategies.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/database-optimizer/references/index-strategies.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Findex-strategies.md -->","isInternal":false,"tokens":1959,"sizeBytes":8131},{"name":"monitoring-analysis.md","path":"skills/database-optimizer/references/monitoring-analysis.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/database-optimizer/references/monitoring-analysis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fmonitoring-analysis.md -->","isInternal":false,"tokens":3689,"sizeBytes":14596},{"name":"mysql-tuning.md","path":"skills/database-optimizer/references/mysql-tuning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/database-optimizer/references/mysql-tuning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fmysql-tuning.md -->","isInternal":false,"tokens":2868,"sizeBytes":11021},{"name":"postgresql-tuning.md","path":"skills/database-optimizer/references/postgresql-tuning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/database-optimizer/references/postgresql-tuning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fpostgresql-tuning.md -->","isInternal":false,"tokens":2679,"sizeBytes":10284},{"name":"query-optimization.md","path":"skills/database-optimizer/references/query-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/database-optimizer/references/query-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2Freferences%2Fquery-optimization.md -->","isInternal":false,"tokens":1542,"sizeBytes":5793},{"name":"SKILL.md","path":"skills/database-optimizer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/database-optimizer/SKILL.md","title":"database-optimizer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdatabase-optimizer%2FSKILL.md -->","frontmatter":{"name":"database-optimizer","description":"Optimizes database queries and improves performance across PostgreSQL and MySQL systems. Use when investigating slow queries, analyzing execution plans, or optimizing database performance. Invoke for index design, query rewrites, configuration tuning, partitioning strategies, lock contention resolution.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"infrastructure","triggers":"database optimization, slow query, query performance, database tuning, index optimization, execution plan, EXPLAIN ANALYZE, database performance, PostgreSQL optimization, MySQL optimization","role":"specialist","scope":"optimization","output-format":"analysis-and-code","related-skills":"devops-engineer, postgres-pro, graphql-architect"}},"isInternal":false,"tokens":1408,"sizeBytes":6232},{"name":"common-patterns.md","path":"skills/debugging-wizard/references/common-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/debugging-wizard/references/common-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fcommon-patterns.md -->","isInternal":false,"tokens":809,"sizeBytes":3103},{"name":"debugging-tools.md","path":"skills/debugging-wizard/references/debugging-tools.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/debugging-wizard/references/debugging-tools.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fdebugging-tools.md -->","isInternal":false,"tokens":806,"sizeBytes":2833},{"name":"quick-fixes.md","path":"skills/debugging-wizard/references/quick-fixes.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/debugging-wizard/references/quick-fixes.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fquick-fixes.md -->","isInternal":false,"tokens":919,"sizeBytes":3529},{"name":"strategies.md","path":"skills/debugging-wizard/references/strategies.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/debugging-wizard/references/strategies.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fstrategies.md -->","isInternal":false,"tokens":747,"sizeBytes":2958},{"name":"systematic-debugging.md","path":"skills/debugging-wizard/references/systematic-debugging.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/debugging-wizard/references/systematic-debugging.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2Freferences%2Fsystematic-debugging.md -->","isInternal":false,"tokens":2593,"sizeBytes":13818},{"name":"SKILL.md","path":"skills/debugging-wizard/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/debugging-wizard/SKILL.md","title":"debugging-wizard","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdebugging-wizard%2FSKILL.md -->","frontmatter":{"name":"debugging-wizard","description":"Parses error messages, traces execution flow through stack traces, correlates log entries to identify failure points, and applies systematic hypothesis-driven methodology to isolate and resolve bugs. Use when investigating errors, analyzing stack traces, finding root causes of unexpected behavior, troubleshooting crashes, or performing log analysis, error investigation, or root cause analysis.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"quality","triggers":"debug, error, bug, exception, traceback, stack trace, troubleshoot, not working, crash, fix issue","role":"specialist","scope":"analysis","output-format":"analysis","related-skills":"test-master, fullstack-guardian, monitoring-expert"}},"isInternal":false,"tokens":916,"sizeBytes":3917},{"name":"deployment-strategies.md","path":"skills/devops-engineer/references/deployment-strategies.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/deployment-strategies.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fdeployment-strategies.md -->","isInternal":false,"tokens":1271,"sizeBytes":4910},{"name":"docker-patterns.md","path":"skills/devops-engineer/references/docker-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/docker-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fdocker-patterns.md -->","isInternal":false,"tokens":803,"sizeBytes":2733},{"name":"github-actions.md","path":"skills/devops-engineer/references/github-actions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/github-actions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fgithub-actions.md -->","isInternal":false,"tokens":836,"sizeBytes":3157},{"name":"gitlab-ci.md","path":"skills/devops-engineer/references/gitlab-ci.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/gitlab-ci.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fgitlab-ci.md -->","isInternal":false,"tokens":2119,"sizeBytes":8524},{"name":"incident-response.md","path":"skills/devops-engineer/references/incident-response.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/incident-response.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fincident-response.md -->","isInternal":false,"tokens":2235,"sizeBytes":8632},{"name":"kubernetes.md","path":"skills/devops-engineer/references/kubernetes.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/kubernetes.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fkubernetes.md -->","isInternal":false,"tokens":777,"sizeBytes":3039},{"name":"platform-engineering.md","path":"skills/devops-engineer/references/platform-engineering.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/platform-engineering.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fplatform-engineering.md -->","isInternal":false,"tokens":2276,"sizeBytes":8914},{"name":"release-automation.md","path":"skills/devops-engineer/references/release-automation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/release-automation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Frelease-automation.md -->","isInternal":false,"tokens":2849,"sizeBytes":11309},{"name":"terraform-iac.md","path":"skills/devops-engineer/references/terraform-iac.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/references/terraform-iac.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2Freferences%2Fterraform-iac.md -->","isInternal":false,"tokens":821,"sizeBytes":3169},{"name":"SKILL.md","path":"skills/devops-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/devops-engineer/SKILL.md","title":"devops-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdevops-engineer%2FSKILL.md -->","frontmatter":{"name":"devops-engineer","description":"Creates Dockerfiles, configures CI/CD pipelines, writes Kubernetes manifests, and generates Terraform/Pulumi infrastructure templates. Handles deployment automation, GitOps configuration, incident response runbooks, and internal developer platform tooling. Use when setting up CI/CD pipelines, containerizing applications, managing infrastructure as code, deploying to Kubernetes clusters, configuring cloud platforms, automating releases, or responding to production incidents. Invoke for pipelines, Docker, Kubernetes, GitOps, Terraform, GitHub Actions, on-call, or platform engineering.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.2.0","domain":"devops","triggers":"DevOps, CI/CD, deployment, Docker, Kubernetes, Terraform, GitHub Actions, infrastructure, platform engineering, incident response, on-call, self-service","role":"engineer","scope":"implementation","output-format":"code","related-skills":"terraform-engineer, kubernetes-specialist, sre-engineer, monitoring-expert, security-reviewer"}},"isInternal":false,"tokens":1527,"sizeBytes":6680},{"name":"authentication.md","path":"skills/django-expert/references/authentication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-expert/references/authentication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fauthentication.md -->","isInternal":false,"tokens":847,"sizeBytes":3962},{"name":"drf-serializers.md","path":"skills/django-expert/references/drf-serializers.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-expert/references/drf-serializers.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fdrf-serializers.md -->","isInternal":false,"tokens":844,"sizeBytes":4132},{"name":"models-orm.md","path":"skills/django-expert/references/models-orm.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-expert/references/models-orm.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fmodels-orm.md -->","isInternal":false,"tokens":886,"sizeBytes":3886},{"name":"testing-django.md","path":"skills/django-expert/references/testing-django.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-expert/references/testing-django.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Ftesting-django.md -->","isInternal":false,"tokens":1186,"sizeBytes":5553},{"name":"viewsets-views.md","path":"skills/django-expert/references/viewsets-views.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-expert/references/viewsets-views.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2Freferences%2Fviewsets-views.md -->","isInternal":false,"tokens":920,"sizeBytes":4305},{"name":"SKILL.md","path":"skills/django-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-expert/SKILL.md","title":"django-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-expert%2FSKILL.md -->","frontmatter":{"name":"django-expert","description":"Use when building Django web applications or REST APIs with Django REST Framework. Invoke when working with settings.py, models.py, manage.py, or any Django project file. Creates Django models with proper indexes, optimizes ORM queries using select_related/prefetch_related, builds DRF serializers and viewsets, and configures JWT authentication. Trigger terms: Django, DRF, Django REST Framework, Django ORM, Django model, serializer, viewset, Python web.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":"Django, DRF, Django REST Framework, Django ORM, Django model, serializer, viewset, Python web","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, fastapi-expert, test-master, django-storages-s3"}},"isInternal":false,"tokens":1352,"sizeBytes":6210},{"name":"configuration.md","path":"skills/django-storages-s3/references/configuration.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-storages-s3/references/configuration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fconfiguration.md -->","isInternal":false,"tokens":1650,"sizeBytes":6477},{"name":"custom-backends.md","path":"skills/django-storages-s3/references/custom-backends.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-storages-s3/references/custom-backends.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fcustom-backends.md -->","isInternal":false,"tokens":1259,"sizeBytes":5260},{"name":"presigned-urls.md","path":"skills/django-storages-s3/references/presigned-urls.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-storages-s3/references/presigned-urls.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Fpresigned-urls.md -->","isInternal":false,"tokens":927,"sizeBytes":3859},{"name":"testing-storages.md","path":"skills/django-storages-s3/references/testing-storages.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-storages-s3/references/testing-storages.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2Freferences%2Ftesting-storages.md -->","isInternal":false,"tokens":1061,"sizeBytes":4043},{"name":"SKILL.md","path":"skills/django-storages-s3/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/django-storages-s3/SKILL.md","title":"django-storages-s3","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdjango-storages-s3%2FSKILL.md -->","frontmatter":{"name":"django-storages-s3","description":"Use when configuring Django to store static and media files on AWS S3 with django-storages. Invoke when working with the STORAGES setting, S3 buckets, presigned URLs, CloudFront, or boto3-backed file storage in settings.py. Configures the Django 4.2+ STORAGES dict, public/private custom backends, presigned GET/POST URLs, IAM policies, and S3 mocking for tests. Trigger terms: django-storages, S3, boto3, S3Boto3Storage, STORAGES, presigned URL, CloudFront, media files, collectstatic, AWS_STORAGE_BUCKET_NAME.","license":"MIT","metadata":{"author":"https://github.com/awais786","version":"1.0.0","domain":"backend","triggers":"django-storages, S3, boto3, S3Boto3Storage, STORAGES, presigned URL, CloudFront, media files, collectstatic","role":"specialist","scope":"implementation","output-format":"code","related-skills":"django-expert"}},"isInternal":false,"tokens":1911,"sizeBytes":7468},{"name":"authentication.md","path":"skills/dotnet-core-expert/references/authentication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/dotnet-core-expert/references/authentication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fauthentication.md -->","isInternal":false,"tokens":3009,"sizeBytes":14968},{"name":"clean-architecture.md","path":"skills/dotnet-core-expert/references/clean-architecture.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/dotnet-core-expert/references/clean-architecture.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fclean-architecture.md -->","isInternal":false,"tokens":2357,"sizeBytes":11931},{"name":"cloud-native.md","path":"skills/dotnet-core-expert/references/cloud-native.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/dotnet-core-expert/references/cloud-native.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fcloud-native.md -->","isInternal":false,"tokens":3028,"sizeBytes":13998},{"name":"entity-framework.md","path":"skills/dotnet-core-expert/references/entity-framework.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/dotnet-core-expert/references/entity-framework.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fentity-framework.md -->","isInternal":false,"tokens":2370,"sizeBytes":12344},{"name":"minimal-apis.md","path":"skills/dotnet-core-expert/references/minimal-apis.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/dotnet-core-expert/references/minimal-apis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2Freferences%2Fminimal-apis.md -->","isInternal":false,"tokens":1706,"sizeBytes":8348},{"name":"SKILL.md","path":"skills/dotnet-core-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/dotnet-core-expert/SKILL.md","title":"dotnet-core-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fdotnet-core-expert%2FSKILL.md -->","frontmatter":{"name":"dotnet-core-expert","description":"Use when building .NET 8 applications with minimal APIs, clean architecture, or cloud-native microservices. Invoke for Entity Framework Core, CQRS with MediatR, JWT authentication, AOT compilation.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":".NET Core, .NET 8, ASP.NET Core, C# 12, minimal API, Entity Framework Core, microservices .NET, CQRS, MediatR","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, microservices-architect, cloud-architect, test-master"}},"isInternal":false,"tokens":1178,"sizeBytes":5255},{"name":"communication-protocols.md","path":"skills/embedded-systems/references/communication-protocols.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/embedded-systems/references/communication-protocols.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fcommunication-protocols.md -->","isInternal":false,"tokens":4018,"sizeBytes":11767},{"name":"memory-optimization.md","path":"skills/embedded-systems/references/memory-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/embedded-systems/references/memory-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fmemory-optimization.md -->","isInternal":false,"tokens":3066,"sizeBytes":10910},{"name":"microcontroller-programming.md","path":"skills/embedded-systems/references/microcontroller-programming.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/embedded-systems/references/microcontroller-programming.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fmicrocontroller-programming.md -->","isInternal":false,"tokens":3128,"sizeBytes":9647},{"name":"power-optimization.md","path":"skills/embedded-systems/references/power-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/embedded-systems/references/power-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Fpower-optimization.md -->","isInternal":false,"tokens":3055,"sizeBytes":10862},{"name":"rtos-patterns.md","path":"skills/embedded-systems/references/rtos-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/embedded-systems/references/rtos-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2Freferences%2Frtos-patterns.md -->","isInternal":false,"tokens":2049,"sizeBytes":8117},{"name":"SKILL.md","path":"skills/embedded-systems/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/embedded-systems/SKILL.md","title":"embedded-systems","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fembedded-systems%2FSKILL.md -->","frontmatter":{"name":"embedded-systems","description":"Use when developing firmware for microcontrollers, implementing RTOS applications, or optimizing power consumption. Invoke for STM32, ESP32, FreeRTOS, bare-metal, power optimization, real-time systems, configure peripherals, write interrupt handlers, implement DMA transfers, debug timing issues.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"specialized","triggers":"embedded systems, firmware, microcontroller, RTOS, FreeRTOS, STM32, ESP32, bare metal, interrupt, DMA, real-time","role":"specialist","scope":"implementation","output-format":"code","related-skills":""}},"isInternal":false,"tokens":1547,"sizeBytes":6295},{"name":"async-sqlalchemy.md","path":"skills/fastapi-expert/references/async-sqlalchemy.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/references/async-sqlalchemy.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fasync-sqlalchemy.md -->","isInternal":false,"tokens":975,"sizeBytes":4081},{"name":"authentication.md","path":"skills/fastapi-expert/references/authentication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/references/authentication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fauthentication.md -->","isInternal":false,"tokens":1017,"sizeBytes":4404},{"name":"endpoints-routing.md","path":"skills/fastapi-expert/references/endpoints-routing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/references/endpoints-routing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fendpoints-routing.md -->","isInternal":false,"tokens":1030,"sizeBytes":4097},{"name":"migration-from-django.md","path":"skills/fastapi-expert/references/migration-from-django.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/references/migration-from-django.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fmigration-from-django.md -->","isInternal":false,"tokens":6349,"sizeBytes":27647},{"name":"pydantic-v2.md","path":"skills/fastapi-expert/references/pydantic-v2.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/references/pydantic-v2.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Fpydantic-v2.md -->","isInternal":false,"tokens":838,"sizeBytes":3351},{"name":"testing-async.md","path":"skills/fastapi-expert/references/testing-async.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/references/testing-async.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2Freferences%2Ftesting-async.md -->","isInternal":false,"tokens":1078,"sizeBytes":4627},{"name":"SKILL.md","path":"skills/fastapi-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fastapi-expert/SKILL.md","title":"fastapi-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffastapi-expert%2FSKILL.md -->","frontmatter":{"name":"fastapi-expert","description":"Use when building high-performance async Python APIs with FastAPI and Pydantic V2. Invoke to create REST endpoints, define Pydantic models, implement authentication flows, set up async SQLAlchemy database operations, add JWT authentication, build WebSocket endpoints, or generate OpenAPI documentation. Trigger terms: FastAPI, Pydantic, async Python, Python API, REST API Python, SQLAlchemy async, JWT authentication, OpenAPI, Swagger Python.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":"FastAPI, Pydantic, async Python, Python API, REST API Python, SQLAlchemy async, JWT authentication, OpenAPI, Swagger Python","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, django-expert, test-master"}},"isInternal":false,"tokens":1612,"sizeBytes":7073},{"name":"acceptance-criteria.md","path":"skills/feature-forge/references/acceptance-criteria.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/feature-forge/references/acceptance-criteria.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Facceptance-criteria.md -->","isInternal":false,"tokens":690,"sizeBytes":2984},{"name":"ears-syntax.md","path":"skills/feature-forge/references/ears-syntax.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/feature-forge/references/ears-syntax.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fears-syntax.md -->","isInternal":false,"tokens":623,"sizeBytes":2699},{"name":"interview-questions.md","path":"skills/feature-forge/references/interview-questions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/feature-forge/references/interview-questions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Finterview-questions.md -->","isInternal":false,"tokens":1256,"sizeBytes":5492},{"name":"pre-discovery-subagents.md","path":"skills/feature-forge/references/pre-discovery-subagents.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/feature-forge/references/pre-discovery-subagents.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fpre-discovery-subagents.md -->","isInternal":false,"tokens":416,"sizeBytes":2084},{"name":"specification-template.md","path":"skills/feature-forge/references/specification-template.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/feature-forge/references/specification-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2Freferences%2Fspecification-template.md -->","isInternal":false,"tokens":626,"sizeBytes":2592},{"name":"SKILL.md","path":"skills/feature-forge/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/feature-forge/SKILL.md","title":"feature-forge","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffeature-forge%2FSKILL.md -->","frontmatter":{"name":"feature-forge","description":"Conducts structured requirements workshops to produce feature specifications, user stories, EARS-format functional requirements, acceptance criteria, and implementation checklists. Use when defining new features, gathering requirements, or writing specifications. Invoke for feature definition, requirements gathering, user stories, EARS format specs, PRDs, acceptance criteria, or requirement matrices.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"workflow","triggers":"requirements, specification, feature definition, user stories, EARS, planning","role":"specialist","scope":"design","output-format":"document","related-skills":"fullstack-guardian, spec-miner, test-master"}},"isInternal":false,"tokens":932,"sizeBytes":4465},{"name":"dataset-preparation.md","path":"skills/fine-tuning-expert/references/dataset-preparation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fine-tuning-expert/references/dataset-preparation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fdataset-preparation.md -->","isInternal":false,"tokens":3803,"sizeBytes":16450},{"name":"deployment-optimization.md","path":"skills/fine-tuning-expert/references/deployment-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fine-tuning-expert/references/deployment-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fdeployment-optimization.md -->","isInternal":false,"tokens":4366,"sizeBytes":17135},{"name":"evaluation-metrics.md","path":"skills/fine-tuning-expert/references/evaluation-metrics.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fine-tuning-expert/references/evaluation-metrics.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fevaluation-metrics.md -->","isInternal":false,"tokens":4099,"sizeBytes":18041},{"name":"hyperparameter-tuning.md","path":"skills/fine-tuning-expert/references/hyperparameter-tuning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fine-tuning-expert/references/hyperparameter-tuning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Fhyperparameter-tuning.md -->","isInternal":false,"tokens":4023,"sizeBytes":16582},{"name":"lora-peft.md","path":"skills/fine-tuning-expert/references/lora-peft.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fine-tuning-expert/references/lora-peft.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2Freferences%2Flora-peft.md -->","isInternal":false,"tokens":2916,"sizeBytes":11030},{"name":"SKILL.md","path":"skills/fine-tuning-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fine-tuning-expert/SKILL.md","title":"fine-tuning-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffine-tuning-expert%2FSKILL.md -->","frontmatter":{"name":"fine-tuning-expert","description":"Use when fine-tuning LLMs, training custom models, or adapting foundation models for specific tasks. Invoke for configuring LoRA/QLoRA adapters, preparing JSONL training datasets, setting hyperparameters for fine-tuning runs, adapter training, transfer learning, finetuning with Hugging Face PEFT, OpenAI fine-tuning, instruction tuning, RLHF, DPO, or quantizing and deploying fine-tuned models. Trigger terms include: LoRA, QLoRA, PEFT, finetuning, fine-tuning, adapter tuning, LLM training, model training, custom model.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"data-ml","triggers":"fine-tuning, fine tuning, finetuning, LoRA, QLoRA, PEFT, adapter tuning, transfer learning, model training, custom model, LLM training, instruction tuning, RLHF, model optimization, quantization","role":"expert","scope":"implementation","output-format":"code","related-skills":"devops-engineer"}},"isInternal":false,"tokens":1658,"sizeBytes":6805},{"name":"bloc-state.md","path":"skills/flutter-expert/references/bloc-state.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/references/bloc-state.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fbloc-state.md -->","isInternal":false,"tokens":1153,"sizeBytes":4836},{"name":"gorouter-navigation.md","path":"skills/flutter-expert/references/gorouter-navigation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/references/gorouter-navigation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fgorouter-navigation.md -->","isInternal":false,"tokens":630,"sizeBytes":2615},{"name":"performance.md","path":"skills/flutter-expert/references/performance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/references/performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fperformance.md -->","isInternal":false,"tokens":494,"sizeBytes":2043},{"name":"project-structure.md","path":"skills/flutter-expert/references/project-structure.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/references/project-structure.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fproject-structure.md -->","isInternal":false,"tokens":735,"sizeBytes":2775},{"name":"riverpod-state.md","path":"skills/flutter-expert/references/riverpod-state.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/references/riverpod-state.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Friverpod-state.md -->","isInternal":false,"tokens":720,"sizeBytes":3102},{"name":"widget-patterns.md","path":"skills/flutter-expert/references/widget-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/references/widget-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2Freferences%2Fwidget-patterns.md -->","isInternal":false,"tokens":574,"sizeBytes":2570},{"name":"SKILL.md","path":"skills/flutter-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/flutter-expert/SKILL.md","title":"flutter-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fflutter-expert%2FSKILL.md -->","frontmatter":{"name":"flutter-expert","description":"Use when building cross-platform applications with Flutter 3+ and Dart. Invoke for widget development, Riverpod/Bloc state management, GoRouter navigation, platform-specific implementations, performance optimization.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"Flutter, Dart, widget, Riverpod, Bloc, GoRouter, cross-platform","role":"specialist","scope":"implementation","output-format":"code","related-skills":"react-native-expert, test-master, fullstack-guardian"}},"isInternal":false,"tokens":1302,"sizeBytes":5824},{"name":"api-design-standards.md","path":"skills/fullstack-guardian/references/api-design-standards.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/api-design-standards.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fapi-design-standards.md -->","isInternal":false,"tokens":1990,"sizeBytes":7804},{"name":"architecture-decisions.md","path":"skills/fullstack-guardian/references/architecture-decisions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/architecture-decisions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Farchitecture-decisions.md -->","isInternal":false,"tokens":2785,"sizeBytes":11854},{"name":"backend-patterns.md","path":"skills/fullstack-guardian/references/backend-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/backend-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fbackend-patterns.md -->","isInternal":false,"tokens":1465,"sizeBytes":6057},{"name":"common-patterns.md","path":"skills/fullstack-guardian/references/common-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/common-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fcommon-patterns.md -->","isInternal":false,"tokens":805,"sizeBytes":3212},{"name":"deliverables-checklist.md","path":"skills/fullstack-guardian/references/deliverables-checklist.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/deliverables-checklist.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fdeliverables-checklist.md -->","isInternal":false,"tokens":2135,"sizeBytes":8812},{"name":"design-template.md","path":"skills/fullstack-guardian/references/design-template.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/design-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fdesign-template.md -->","isInternal":false,"tokens":497,"sizeBytes":2132},{"name":"error-handling.md","path":"skills/fullstack-guardian/references/error-handling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/error-handling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Ferror-handling.md -->","isInternal":false,"tokens":815,"sizeBytes":3397},{"name":"frontend-patterns.md","path":"skills/fullstack-guardian/references/frontend-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/frontend-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Ffrontend-patterns.md -->","isInternal":false,"tokens":2100,"sizeBytes":8805},{"name":"integration-patterns.md","path":"skills/fullstack-guardian/references/integration-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/integration-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fintegration-patterns.md -->","isInternal":false,"tokens":2088,"sizeBytes":8254},{"name":"security-checklist.md","path":"skills/fullstack-guardian/references/security-checklist.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/references/security-checklist.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2Freferences%2Fsecurity-checklist.md -->","isInternal":false,"tokens":620,"sizeBytes":2533},{"name":"SKILL.md","path":"skills/fullstack-guardian/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/fullstack-guardian/SKILL.md","title":"fullstack-guardian","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ffullstack-guardian%2FSKILL.md -->","frontmatter":{"name":"fullstack-guardian","description":"Builds security-focused full-stack web applications by implementing integrated frontend and backend components with layered security at every level. Covers the complete stack from database to UI, enforcing auth, input validation, output encoding, and parameterized queries across all layers. Use when implementing features across frontend and backend, building REST APIs with corresponding UI, connecting frontend components to backend endpoints, creating end-to-end data flows from database to UI, or implementing CRUD operations with UI forms. Distinct from frontend-only, backend-only, or API-only skills in that it simultaneously addresses all three perspectives—Frontend, Backend, and Security—within a single implementation workflow. Invoke for full-stack feature work, web app development, authenticated API routes with views, microservices, real-time features, monorepo architecture, or technology selection decisions.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"security","triggers":"fullstack, implement feature, build feature, create API, frontend and backend, full stack, new feature, implement, microservices, websocket, real-time, deployment pipeline, monorepo, architecture decision, technology selection, end-to-end","role":"expert","scope":"implementation","output-format":"code","related-skills":"feature-forge, test-master, devops-engineer, secure-code-guardian, architecture-designer, react-expert, typescript-pro"}},"isInternal":false,"tokens":1276,"sizeBytes":5998},{"name":"ecs-patterns.md","path":"skills/game-developer/references/ecs-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/game-developer/references/ecs-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fecs-patterns.md -->","isInternal":false,"tokens":2236,"sizeBytes":10329},{"name":"multiplayer-networking.md","path":"skills/game-developer/references/multiplayer-networking.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/game-developer/references/multiplayer-networking.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fmultiplayer-networking.md -->","isInternal":false,"tokens":2645,"sizeBytes":12678},{"name":"performance-optimization.md","path":"skills/game-developer/references/performance-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/game-developer/references/performance-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Fperformance-optimization.md -->","isInternal":false,"tokens":2318,"sizeBytes":10285},{"name":"unity-patterns.md","path":"skills/game-developer/references/unity-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/game-developer/references/unity-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Funity-patterns.md -->","isInternal":false,"tokens":1318,"sizeBytes":6489},{"name":"unreal-cpp.md","path":"skills/game-developer/references/unreal-cpp.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/game-developer/references/unreal-cpp.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2Freferences%2Funreal-cpp.md -->","isInternal":false,"tokens":2017,"sizeBytes":8779},{"name":"SKILL.md","path":"skills/game-developer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/game-developer/SKILL.md","title":"game-developer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgame-developer%2FSKILL.md -->","frontmatter":{"name":"game-developer","description":"Use when building game systems, implementing Unity/Unreal Engine features, or optimizing game performance. Invoke to implement ECS architecture, configure physics systems and colliders, set up multiplayer networking with lag compensation, optimize frame rates to 60+ FPS targets, develop shaders, or apply game design patterns such as object pooling and state machines. Trigger keywords: Unity, Unreal Engine, game development, ECS architecture, game physics, multiplayer networking, game optimization, shader programming, game AI.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"specialized","triggers":"Unity, Unreal Engine, game development, ECS architecture, game physics, multiplayer networking, game optimization, shader programming, game AI","role":"specialist","scope":"implementation","output-format":"code","related-skills":""}},"isInternal":false,"tokens":1271,"sizeBytes":5900},{"name":"concurrency.md","path":"skills/golang-pro/references/concurrency.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/golang-pro/references/concurrency.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fconcurrency.md -->","isInternal":false,"tokens":1726,"sizeBytes":7438},{"name":"generics.md","path":"skills/golang-pro/references/generics.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/golang-pro/references/generics.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fgenerics.md -->","isInternal":false,"tokens":2551,"sizeBytes":8741},{"name":"interfaces.md","path":"skills/golang-pro/references/interfaces.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/golang-pro/references/interfaces.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Finterfaces.md -->","isInternal":false,"tokens":2085,"sizeBytes":8361},{"name":"project-structure.md","path":"skills/golang-pro/references/project-structure.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/golang-pro/references/project-structure.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Fproject-structure.md -->","isInternal":false,"tokens":2710,"sizeBytes":10430},{"name":"testing.md","path":"skills/golang-pro/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/golang-pro/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2Freferences%2Ftesting.md -->","isInternal":false,"tokens":2610,"sizeBytes":9461},{"name":"SKILL.md","path":"skills/golang-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/golang-pro/SKILL.md","title":"golang-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgolang-pro%2FSKILL.md -->","frontmatter":{"name":"golang-pro","description":"Implements concurrent Go patterns using goroutines and channels, designs and builds microservices with gRPC or REST, optimizes Go application performance with pprof, and enforces idiomatic Go with generics, interfaces, and robust error handling. Use when building Go applications requiring concurrent programming, microservices architecture, or high-performance systems. Invoke for goroutines, channels, Go generics, gRPC integration, CLI tools, benchmarks, or table-driven testing.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"language","triggers":"Go, Golang, goroutines, channels, gRPC, microservices Go, Go generics, concurrent programming, Go interfaces","role":"specialist","scope":"implementation","output-format":"code","related-skills":"devops-engineer, microservices-architect, test-master"}},"isInternal":false,"tokens":1164,"sizeBytes":5208},{"name":"federation.md","path":"skills/graphql-architect/references/federation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/references/federation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Ffederation.md -->","isInternal":false,"tokens":2395,"sizeBytes":9233},{"name":"migration-from-rest.md","path":"skills/graphql-architect/references/migration-from-rest.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/references/migration-from-rest.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fmigration-from-rest.md -->","isInternal":false,"tokens":5966,"sizeBytes":25024},{"name":"resolvers.md","path":"skills/graphql-architect/references/resolvers.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/references/resolvers.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fresolvers.md -->","isInternal":false,"tokens":2377,"sizeBytes":10239},{"name":"schema-design.md","path":"skills/graphql-architect/references/schema-design.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/references/schema-design.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fschema-design.md -->","isInternal":false,"tokens":1657,"sizeBytes":6531},{"name":"security.md","path":"skills/graphql-architect/references/security.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/references/security.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fsecurity.md -->","isInternal":false,"tokens":3121,"sizeBytes":13199},{"name":"subscriptions.md","path":"skills/graphql-architect/references/subscriptions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/references/subscriptions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2Freferences%2Fsubscriptions.md -->","isInternal":false,"tokens":2685,"sizeBytes":12154},{"name":"SKILL.md","path":"skills/graphql-architect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/graphql-architect/SKILL.md","title":"graphql-architect","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fgraphql-architect%2FSKILL.md -->","frontmatter":{"name":"graphql-architect","description":"Use when designing GraphQL schemas, implementing Apollo Federation, or building real-time subscriptions. Invoke for schema design, resolvers with DataLoader, query optimization, federation directives.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"api-architecture","triggers":"GraphQL, Apollo Federation, GraphQL schema, API graph, GraphQL subscriptions, Apollo Server, schema design, GraphQL resolvers, DataLoader","role":"architect","scope":"design","output-format":"schema","related-skills":"api-designer, microservices-architect, database-optimizer"}},"isInternal":false,"tokens":1170,"sizeBytes":5271},{"name":"jpa-optimization.md","path":"skills/java-architect/references/jpa-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/java-architect/references/jpa-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fjpa-optimization.md -->","isInternal":false,"tokens":2224,"sizeBytes":10853},{"name":"reactive-webflux.md","path":"skills/java-architect/references/reactive-webflux.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/java-architect/references/reactive-webflux.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Freactive-webflux.md -->","isInternal":false,"tokens":1913,"sizeBytes":9410},{"name":"spring-boot-setup.md","path":"skills/java-architect/references/spring-boot-setup.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/java-architect/references/spring-boot-setup.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fspring-boot-setup.md -->","isInternal":false,"tokens":1626,"sizeBytes":7798},{"name":"spring-security.md","path":"skills/java-architect/references/spring-security.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/java-architect/references/spring-security.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Fspring-security.md -->","isInternal":false,"tokens":2626,"sizeBytes":15177},{"name":"testing-patterns.md","path":"skills/java-architect/references/testing-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/java-architect/references/testing-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2Freferences%2Ftesting-patterns.md -->","isInternal":false,"tokens":2857,"sizeBytes":14838},{"name":"SKILL.md","path":"skills/java-architect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/java-architect/SKILL.md","title":"java-architect","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjava-architect%2FSKILL.md -->","frontmatter":{"name":"java-architect","description":"Use when building, configuring, or debugging enterprise Java applications with Spring Boot 3.x, microservices, or reactive programming. Invoke to implement WebFlux endpoints, optimize JPA queries and database performance, configure Spring Security with OAuth2/JWT, or resolve authentication issues and async processing challenges in cloud-native Spring applications.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"Spring Boot, Java, microservices, Spring Cloud, JPA, Hibernate, WebFlux, reactive, Java Enterprise","role":"architect","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, api-designer, devops-engineer, database-optimizer"}},"isInternal":false,"tokens":1287,"sizeBytes":5885},{"name":"async-patterns.md","path":"skills/javascript-pro/references/async-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/javascript-pro/references/async-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fasync-patterns.md -->","isInternal":false,"tokens":1980,"sizeBytes":8012},{"name":"browser-apis.md","path":"skills/javascript-pro/references/browser-apis.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/javascript-pro/references/browser-apis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fbrowser-apis.md -->","isInternal":false,"tokens":2197,"sizeBytes":9507},{"name":"modern-syntax.md","path":"skills/javascript-pro/references/modern-syntax.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/javascript-pro/references/modern-syntax.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fmodern-syntax.md -->","isInternal":false,"tokens":1609,"sizeBytes":6078},{"name":"modules.md","path":"skills/javascript-pro/references/modules.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/javascript-pro/references/modules.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fmodules.md -->","isInternal":false,"tokens":1976,"sizeBytes":7679},{"name":"node-essentials.md","path":"skills/javascript-pro/references/node-essentials.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/javascript-pro/references/node-essentials.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2Freferences%2Fnode-essentials.md -->","isInternal":false,"tokens":2808,"sizeBytes":11230},{"name":"SKILL.md","path":"skills/javascript-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/javascript-pro/SKILL.md","title":"javascript-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fjavascript-pro%2FSKILL.md -->","frontmatter":{"name":"javascript-pro","description":"Writes, debugs, and refactors JavaScript code using modern ES2023+ features, async/await patterns, ESM module systems, and Node.js APIs. Use when building vanilla JavaScript applications, implementing Promise-based async flows, optimising browser or Node.js performance, working with Web Workers or Fetch API, or reviewing .js/.mjs/.cjs files for correctness and best practices.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"JavaScript, ES2023, async await, Node.js, vanilla JavaScript, Web Workers, Fetch API, browser API, module system","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian"}},"isInternal":false,"tokens":1194,"sizeBytes":4998},{"name":"android-compose.md","path":"skills/kotlin-specialist/references/android-compose.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kotlin-specialist/references/android-compose.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fandroid-compose.md -->","isInternal":false,"tokens":2101,"sizeBytes":9758},{"name":"coroutines-flow.md","path":"skills/kotlin-specialist/references/coroutines-flow.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kotlin-specialist/references/coroutines-flow.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fcoroutines-flow.md -->","isInternal":false,"tokens":1512,"sizeBytes":6695},{"name":"dsl-idioms.md","path":"skills/kotlin-specialist/references/dsl-idioms.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kotlin-specialist/references/dsl-idioms.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fdsl-idioms.md -->","isInternal":false,"tokens":2573,"sizeBytes":9764},{"name":"ktor-server.md","path":"skills/kotlin-specialist/references/ktor-server.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kotlin-specialist/references/ktor-server.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fktor-server.md -->","isInternal":false,"tokens":2293,"sizeBytes":11491},{"name":"multiplatform-kmp.md","path":"skills/kotlin-specialist/references/multiplatform-kmp.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kotlin-specialist/references/multiplatform-kmp.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2Freferences%2Fmultiplatform-kmp.md -->","isInternal":false,"tokens":1974,"sizeBytes":8674},{"name":"SKILL.md","path":"skills/kotlin-specialist/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kotlin-specialist/SKILL.md","title":"kotlin-specialist","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkotlin-specialist%2FSKILL.md -->","frontmatter":{"name":"kotlin-specialist","description":"Provides idiomatic Kotlin implementation patterns including coroutine concurrency, Flow stream handling, multiplatform architecture, Compose UI construction, Ktor server setup, and type-safe DSL design. Use when building Kotlin applications requiring coroutines, multiplatform development, or Android with Compose. Invoke for Flow API, KMP projects, Ktor servers, DSL design, sealed classes, suspend function, Android Kotlin, Kotlin Multiplatform.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"Kotlin, coroutines, Kotlin Multiplatform, KMP, Jetpack Compose, Ktor, Flow, Android Kotlin, suspend function","role":"specialist","scope":"implementation","output-format":"code","related-skills":"test-master"}},"isInternal":false,"tokens":1419,"sizeBytes":6111},{"name":"configuration.md","path":"skills/kubernetes-specialist/references/configuration.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/configuration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fconfiguration.md -->","isInternal":false,"tokens":2269,"sizeBytes":8814},{"name":"cost-optimization.md","path":"skills/kubernetes-specialist/references/cost-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/cost-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fcost-optimization.md -->","isInternal":false,"tokens":2586,"sizeBytes":9874},{"name":"custom-operators.md","path":"skills/kubernetes-specialist/references/custom-operators.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/custom-operators.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fcustom-operators.md -->","isInternal":false,"tokens":4041,"sizeBytes":15917},{"name":"gitops.md","path":"skills/kubernetes-specialist/references/gitops.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/gitops.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fgitops.md -->","isInternal":false,"tokens":3003,"sizeBytes":11081},{"name":"helm-charts.md","path":"skills/kubernetes-specialist/references/helm-charts.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/helm-charts.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fhelm-charts.md -->","isInternal":false,"tokens":5458,"sizeBytes":20445},{"name":"multi-cluster.md","path":"skills/kubernetes-specialist/references/multi-cluster.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/multi-cluster.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fmulti-cluster.md -->","isInternal":false,"tokens":2932,"sizeBytes":11364},{"name":"networking.md","path":"skills/kubernetes-specialist/references/networking.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/networking.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fnetworking.md -->","isInternal":false,"tokens":2246,"sizeBytes":8049},{"name":"service-mesh.md","path":"skills/kubernetes-specialist/references/service-mesh.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/service-mesh.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fservice-mesh.md -->","isInternal":false,"tokens":2500,"sizeBytes":9546},{"name":"storage.md","path":"skills/kubernetes-specialist/references/storage.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/storage.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fstorage.md -->","isInternal":false,"tokens":2685,"sizeBytes":9723},{"name":"troubleshooting.md","path":"skills/kubernetes-specialist/references/troubleshooting.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/troubleshooting.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Ftroubleshooting.md -->","isInternal":false,"tokens":3132,"sizeBytes":11616},{"name":"workloads.md","path":"skills/kubernetes-specialist/references/workloads.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/references/workloads.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2Freferences%2Fworkloads.md -->","isInternal":false,"tokens":2142,"sizeBytes":8716},{"name":"SKILL.md","path":"skills/kubernetes-specialist/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/kubernetes-specialist/SKILL.md","title":"kubernetes-specialist","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fkubernetes-specialist%2FSKILL.md -->","frontmatter":{"name":"kubernetes-specialist","description":"Use when deploying or managing Kubernetes workloads. Invoke to create deployment manifests, configure pod security policies, set up service accounts, define network isolation rules, debug pod crashes, analyze resource limits, inspect container logs, or right-size workloads. Use for Helm charts, RBAC policies, NetworkPolicies, storage configuration, performance optimization, GitOps pipelines, and multi-cluster management.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.2","domain":"infrastructure","triggers":"Kubernetes, K8s, kubectl, Helm, container orchestration, pod deployment, RBAC, NetworkPolicy, Ingress, StatefulSet, Operator, CRD, CustomResourceDefinition, ArgoCD, Flux, GitOps, Istio, Linkerd, service mesh, multi-cluster, cost optimization, VPA, spot instances","role":"specialist","scope":"infrastructure","output-format":"manifests","related-skills":"devops-engineer, cloud-architect, sre-engineer, terraform-engineer, security-reviewer, chaos-engineer"}},"isInternal":false,"tokens":1957,"sizeBytes":8343},{"name":"eloquent.md","path":"skills/laravel-specialist/references/eloquent.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/laravel-specialist/references/eloquent.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Feloquent.md -->","isInternal":false,"tokens":1821,"sizeBytes":7513},{"name":"livewire.md","path":"skills/laravel-specialist/references/livewire.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/laravel-specialist/references/livewire.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Flivewire.md -->","isInternal":false,"tokens":2664,"sizeBytes":11177},{"name":"queues.md","path":"skills/laravel-specialist/references/queues.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/laravel-specialist/references/queues.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Fqueues.md -->","isInternal":false,"tokens":2243,"sizeBytes":9395},{"name":"routing.md","path":"skills/laravel-specialist/references/routing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/laravel-specialist/references/routing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Frouting.md -->","isInternal":false,"tokens":1989,"sizeBytes":8635},{"name":"testing.md","path":"skills/laravel-specialist/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/laravel-specialist/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2Freferences%2Ftesting.md -->","isInternal":false,"tokens":2972,"sizeBytes":12336},{"name":"SKILL.md","path":"skills/laravel-specialist/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/laravel-specialist/SKILL.md","title":"laravel-specialist","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flaravel-specialist%2FSKILL.md -->","frontmatter":{"name":"laravel-specialist","description":"Build and configure Laravel 10+ applications, including creating Eloquent models and relationships, implementing Sanctum authentication, configuring Horizon queues, designing RESTful APIs with API resources, and building reactive interfaces with Livewire. Use when creating Laravel models, setting up queue workers, implementing Sanctum auth flows, building Livewire components, optimising Eloquent queries, or writing Pest/PHPUnit tests for Laravel features.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":"Laravel, Eloquent, PHP framework, Laravel API, Artisan, Blade templates, Laravel queues, Livewire, Laravel testing, Sanctum, Horizon","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, test-master, devops-engineer, security-reviewer"}},"isInternal":false,"tokens":1939,"sizeBytes":8524},{"name":"legacy-testing.md","path":"skills/legacy-modernizer/references/legacy-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/legacy-modernizer/references/legacy-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Flegacy-testing.md -->","isInternal":false,"tokens":2604,"sizeBytes":11490},{"name":"migration-strategies.md","path":"skills/legacy-modernizer/references/migration-strategies.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/legacy-modernizer/references/migration-strategies.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fmigration-strategies.md -->","isInternal":false,"tokens":2676,"sizeBytes":11802},{"name":"refactoring-patterns.md","path":"skills/legacy-modernizer/references/refactoring-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/legacy-modernizer/references/refactoring-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Frefactoring-patterns.md -->","isInternal":false,"tokens":2542,"sizeBytes":12202},{"name":"strangler-fig-pattern.md","path":"skills/legacy-modernizer/references/strangler-fig-pattern.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/legacy-modernizer/references/strangler-fig-pattern.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fstrangler-fig-pattern.md -->","isInternal":false,"tokens":1904,"sizeBytes":8433},{"name":"system-assessment.md","path":"skills/legacy-modernizer/references/system-assessment.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/legacy-modernizer/references/system-assessment.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2Freferences%2Fsystem-assessment.md -->","isInternal":false,"tokens":3305,"sizeBytes":15140},{"name":"SKILL.md","path":"skills/legacy-modernizer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/legacy-modernizer/SKILL.md","title":"legacy-modernizer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Flegacy-modernizer%2FSKILL.md -->","frontmatter":{"name":"legacy-modernizer","description":"Designs incremental migration strategies, identifies service boundaries, produces dependency maps and migration roadmaps, and generates API facade designs for aging codebases. Use when modernizing legacy systems, implementing strangler fig pattern or branch by abstraction, decomposing monoliths, upgrading frameworks or languages, or reducing technical debt without disrupting business operations.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"specialized","triggers":"legacy modernization, strangler fig, incremental migration, technical debt, legacy refactoring, system migration, legacy system, modernize codebase","role":"specialist","scope":"architecture","output-format":"analysis-and-code","related-skills":"test-master, devops-engineer"}},"isInternal":false,"tokens":1326,"sizeBytes":6289},{"name":"protocol.md","path":"skills/mcp-developer/references/protocol.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/mcp-developer/references/protocol.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fprotocol.md -->","isInternal":false,"tokens":1300,"sizeBytes":5055},{"name":"python-sdk.md","path":"skills/mcp-developer/references/python-sdk.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/mcp-developer/references/python-sdk.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fpython-sdk.md -->","isInternal":false,"tokens":2108,"sizeBytes":9832},{"name":"resources.md","path":"skills/mcp-developer/references/resources.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/mcp-developer/references/resources.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Fresources.md -->","isInternal":false,"tokens":2933,"sizeBytes":12214},{"name":"tools.md","path":"skills/mcp-developer/references/tools.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/mcp-developer/references/tools.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Ftools.md -->","isInternal":false,"tokens":2589,"sizeBytes":10417},{"name":"typescript-sdk.md","path":"skills/mcp-developer/references/typescript-sdk.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/mcp-developer/references/typescript-sdk.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2Freferences%2Ftypescript-sdk.md -->","isInternal":false,"tokens":1788,"sizeBytes":8009},{"name":"SKILL.md","path":"skills/mcp-developer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/mcp-developer/SKILL.md","title":"mcp-developer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmcp-developer%2FSKILL.md -->","frontmatter":{"name":"mcp-developer","description":"Use when building, debugging, or extending MCP servers or clients that connect AI systems with external tools and data sources. Invoke to implement tool handlers, configure resource providers, set up stdio/HTTP/SSE transport layers, validate schemas with Zod or Pydantic, debug protocol compliance issues, or scaffold complete MCP server/client projects using TypeScript or Python SDKs.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"api-architecture","triggers":"MCP, Model Context Protocol, MCP server, MCP client, Claude integration, AI tools, context protocol, JSON-RPC","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fastapi-expert, typescript-pro, security-reviewer, devops-engineer"}},"isInternal":false,"tokens":1377,"sizeBytes":5900},{"name":"communication.md","path":"skills/microservices-architect/references/communication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/microservices-architect/references/communication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fcommunication.md -->","isInternal":false,"tokens":2445,"sizeBytes":10101},{"name":"data.md","path":"skills/microservices-architect/references/data.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/microservices-architect/references/data.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fdata.md -->","isInternal":false,"tokens":3896,"sizeBytes":15710},{"name":"decomposition.md","path":"skills/microservices-architect/references/decomposition.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/microservices-architect/references/decomposition.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fdecomposition.md -->","isInternal":false,"tokens":1767,"sizeBytes":8273},{"name":"observability.md","path":"skills/microservices-architect/references/observability.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/microservices-architect/references/observability.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fobservability.md -->","isInternal":false,"tokens":4399,"sizeBytes":17589},{"name":"patterns.md","path":"skills/microservices-architect/references/patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/microservices-architect/references/patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2Freferences%2Fpatterns.md -->","isInternal":false,"tokens":3265,"sizeBytes":14143},{"name":"SKILL.md","path":"skills/microservices-architect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/microservices-architect/SKILL.md","title":"microservices-architect","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmicroservices-architect%2FSKILL.md -->","frontmatter":{"name":"microservices-architect","description":"Designs distributed system architectures, decomposes monoliths into bounded-context services, recommends communication patterns, and produces service boundary diagrams and resilience strategies. Use when designing distributed systems, decomposing monoliths, or implementing microservices patterns — including service boundaries, DDD, saga patterns, event sourcing, CQRS, service mesh, or distributed tracing.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"api-architecture","triggers":"microservices, service mesh, distributed systems, service boundaries, domain-driven design, event sourcing, CQRS, saga pattern, Kubernetes microservices, Istio, distributed tracing","role":"architect","scope":"system-design","output-format":"architecture","related-skills":"devops-engineer, kubernetes-specialist, graphql-architect, architecture-designer, monitoring-expert"}},"isInternal":false,"tokens":1561,"sizeBytes":7074},{"name":"experiment-tracking.md","path":"skills/ml-pipeline/references/experiment-tracking.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/ml-pipeline/references/experiment-tracking.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fexperiment-tracking.md -->","isInternal":false,"tokens":4760,"sizeBytes":21891},{"name":"feature-engineering.md","path":"skills/ml-pipeline/references/feature-engineering.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/ml-pipeline/references/feature-engineering.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Ffeature-engineering.md -->","isInternal":false,"tokens":3822,"sizeBytes":17771},{"name":"model-validation.md","path":"skills/ml-pipeline/references/model-validation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/ml-pipeline/references/model-validation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fmodel-validation.md -->","isInternal":false,"tokens":6482,"sizeBytes":30485},{"name":"pipeline-orchestration.md","path":"skills/ml-pipeline/references/pipeline-orchestration.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/ml-pipeline/references/pipeline-orchestration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Fpipeline-orchestration.md -->","isInternal":false,"tokens":5596,"sizeBytes":23555},{"name":"training-pipelines.md","path":"skills/ml-pipeline/references/training-pipelines.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/ml-pipeline/references/training-pipelines.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2Freferences%2Ftraining-pipelines.md -->","isInternal":false,"tokens":4949,"sizeBytes":22504},{"name":"SKILL.md","path":"skills/ml-pipeline/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/ml-pipeline/SKILL.md","title":"ml-pipeline","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fml-pipeline%2FSKILL.md -->","frontmatter":{"name":"ml-pipeline","description":"Designs and implements production-grade ML pipeline infrastructure: configures experiment tracking with MLflow or Weights & Biases, creates Kubeflow or Airflow DAGs for training orchestration, builds feature store schemas with Feast, deploys model registries, and automates retraining and validation workflows. Use when building ML pipelines, orchestrating training workflows, automating model lifecycle, implementing feature stores, managing experiment tracking systems, setting up DVC for data versioning, tuning hyperparameters, or configuring MLOps tooling like Kubeflow, Airflow, MLflow, or Prefect.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"data-ml","triggers":"ML pipeline, MLflow, Kubeflow, feature engineering, model training, experiment tracking, feature store, hyperparameter tuning, pipeline orchestration, model registry, training workflow, MLOps, model deployment, data pipeline, model versioning","role":"expert","scope":"implementation","output-format":"code","related-skills":"devops-engineer, kubernetes-specialist, cloud-architect, python-pro"}},"isInternal":false,"tokens":1613,"sizeBytes":7423},{"name":"alerting-rules.md","path":"skills/monitoring-expert/references/alerting-rules.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/alerting-rules.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Falerting-rules.md -->","isInternal":false,"tokens":879,"sizeBytes":3459},{"name":"application-profiling.md","path":"skills/monitoring-expert/references/application-profiling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/application-profiling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fapplication-profiling.md -->","isInternal":false,"tokens":1675,"sizeBytes":6376},{"name":"capacity-planning.md","path":"skills/monitoring-expert/references/capacity-planning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/capacity-planning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fcapacity-planning.md -->","isInternal":false,"tokens":2252,"sizeBytes":8269},{"name":"dashboards.md","path":"skills/monitoring-expert/references/dashboards.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/dashboards.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fdashboards.md -->","isInternal":false,"tokens":786,"sizeBytes":3812},{"name":"opentelemetry.md","path":"skills/monitoring-expert/references/opentelemetry.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/opentelemetry.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fopentelemetry.md -->","isInternal":false,"tokens":768,"sizeBytes":3345},{"name":"performance-testing.md","path":"skills/monitoring-expert/references/performance-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/performance-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fperformance-testing.md -->","isInternal":false,"tokens":1686,"sizeBytes":5911},{"name":"prometheus-metrics.md","path":"skills/monitoring-expert/references/prometheus-metrics.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/prometheus-metrics.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fprometheus-metrics.md -->","isInternal":false,"tokens":764,"sizeBytes":3108},{"name":"structured-logging.md","path":"skills/monitoring-expert/references/structured-logging.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/references/structured-logging.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2Freferences%2Fstructured-logging.md -->","isInternal":false,"tokens":728,"sizeBytes":2852},{"name":"SKILL.md","path":"skills/monitoring-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/monitoring-expert/SKILL.md","title":"monitoring-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fmonitoring-expert%2FSKILL.md -->","frontmatter":{"name":"monitoring-expert","description":"Configures monitoring systems, implements structured logging pipelines, creates Prometheus/Grafana dashboards, defines alerting rules, and instruments distributed tracing. Implements Prometheus/Grafana stacks, conducts load testing, performs application profiling, and plans infrastructure capacity. Use when setting up application monitoring, adding observability to services, debugging production issues with logs/metrics/traces, running load tests with k6 or Artillery, profiling CPU/memory bottlenecks, or forecasting capacity needs.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"devops","triggers":"monitoring, observability, logging, metrics, tracing, alerting, Prometheus, Grafana, DataDog, APM, performance testing, load testing, profiling, capacity planning, bottleneck","role":"specialist","scope":"implementation","output-format":"code","related-skills":"devops-engineer, debugging-wizard, architecture-designer"}},"isInternal":false,"tokens":1555,"sizeBytes":6352},{"name":"authentication.md","path":"skills/nestjs-expert/references/authentication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/references/authentication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fauthentication.md -->","isInternal":false,"tokens":926,"sizeBytes":4021},{"name":"controllers-routing.md","path":"skills/nestjs-expert/references/controllers-routing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/references/controllers-routing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fcontrollers-routing.md -->","isInternal":false,"tokens":814,"sizeBytes":3094},{"name":"dtos-validation.md","path":"skills/nestjs-expert/references/dtos-validation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/references/dtos-validation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fdtos-validation.md -->","isInternal":false,"tokens":937,"sizeBytes":3628},{"name":"migration-from-express.md","path":"skills/nestjs-expert/references/migration-from-express.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/references/migration-from-express.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fmigration-from-express.md -->","isInternal":false,"tokens":6932,"sizeBytes":31048},{"name":"services-di.md","path":"skills/nestjs-expert/references/services-di.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/references/services-di.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Fservices-di.md -->","isInternal":false,"tokens":741,"sizeBytes":3382},{"name":"testing-patterns.md","path":"skills/nestjs-expert/references/testing-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/references/testing-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2Freferences%2Ftesting-patterns.md -->","isInternal":false,"tokens":1104,"sizeBytes":4717},{"name":"SKILL.md","path":"skills/nestjs-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nestjs-expert/SKILL.md","title":"nestjs-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnestjs-expert%2FSKILL.md -->","frontmatter":{"name":"nestjs-expert","description":"Creates and configures NestJS modules, controllers, services, DTOs, guards, and interceptors for enterprise-grade TypeScript backend applications. Use when building NestJS REST APIs or GraphQL services, implementing dependency injection, scaffolding modular architecture, adding JWT/Passport authentication, integrating TypeORM or Prisma, or working with .module.ts, .controller.ts, and .service.ts files. Invoke for guards, interceptors, pipes, validation, Swagger documentation, and unit/E2E testing in NestJS projects.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":"NestJS, Nest, Node.js backend, TypeScript backend, dependency injection, controller, service, module, guard, interceptor","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, test-master, devops-engineer"}},"isInternal":false,"tokens":1739,"sizeBytes":7856},{"name":"app-router.md","path":"skills/nextjs-developer/references/app-router.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nextjs-developer/references/app-router.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fapp-router.md -->","isInternal":false,"tokens":1775,"sizeBytes":6862},{"name":"data-fetching.md","path":"skills/nextjs-developer/references/data-fetching.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nextjs-developer/references/data-fetching.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fdata-fetching.md -->","isInternal":false,"tokens":2722,"sizeBytes":10389},{"name":"deployment.md","path":"skills/nextjs-developer/references/deployment.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nextjs-developer/references/deployment.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fdeployment.md -->","isInternal":false,"tokens":2850,"sizeBytes":10683},{"name":"server-actions.md","path":"skills/nextjs-developer/references/server-actions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nextjs-developer/references/server-actions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fserver-actions.md -->","isInternal":false,"tokens":2409,"sizeBytes":9658},{"name":"server-components.md","path":"skills/nextjs-developer/references/server-components.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nextjs-developer/references/server-components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2Freferences%2Fserver-components.md -->","isInternal":false,"tokens":2009,"sizeBytes":8273},{"name":"SKILL.md","path":"skills/nextjs-developer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/nextjs-developer/SKILL.md","title":"nextjs-developer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fnextjs-developer%2FSKILL.md -->","frontmatter":{"name":"nextjs-developer","description":"Use when building Next.js 14+ applications with App Router, server components, or server actions. Invoke to configure route handlers, implement middleware, set up API routes, add streaming SSR, write generateMetadata for SEO, scaffold loading.tsx/error.tsx boundaries, or deploy to Vercel. Triggers on: Next.js, Next.js 14, App Router, RSC, use server, Server Components, Server Actions, React Server Components, generateMetadata, loading.tsx, Next.js deployment, Vercel, Next.js performance.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"Next.js, Next.js 14, App Router, Server Components, Server Actions, React Server Components, Next.js deployment, Vercel, Next.js performance","role":"specialist","scope":"implementation","output-format":"code","related-skills":"typescript-pro"}},"isInternal":false,"tokens":1341,"sizeBytes":5687},{"name":"aggregation-groupby.md","path":"skills/pandas-pro/references/aggregation-groupby.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/pandas-pro/references/aggregation-groupby.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Faggregation-groupby.md -->","isInternal":false,"tokens":3551,"sizeBytes":13206},{"name":"data-cleaning.md","path":"skills/pandas-pro/references/data-cleaning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/pandas-pro/references/data-cleaning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fdata-cleaning.md -->","isInternal":false,"tokens":3498,"sizeBytes":12969},{"name":"dataframe-operations.md","path":"skills/pandas-pro/references/dataframe-operations.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/pandas-pro/references/dataframe-operations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fdataframe-operations.md -->","isInternal":false,"tokens":2685,"sizeBytes":9667},{"name":"merging-joining.md","path":"skills/pandas-pro/references/merging-joining.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/pandas-pro/references/merging-joining.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fmerging-joining.md -->","isInternal":false,"tokens":4076,"sizeBytes":13517},{"name":"performance-optimization.md","path":"skills/pandas-pro/references/performance-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/pandas-pro/references/performance-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2Freferences%2Fperformance-optimization.md -->","isInternal":false,"tokens":3989,"sizeBytes":15068},{"name":"SKILL.md","path":"skills/pandas-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/pandas-pro/SKILL.md","title":"pandas-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpandas-pro%2FSKILL.md -->","frontmatter":{"name":"pandas-pro","description":"Performs pandas DataFrame operations for data analysis, manipulation, and transformation. Use when working with pandas DataFrames, data cleaning, aggregation, merging, or time series analysis. Invoke for data manipulation tasks such as joining DataFrames on multiple keys, pivoting tables, resampling time series, handling NaN values with interpolation or forward-fill, groupby aggregations, type conversion, or performance optimization of large datasets.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"data-ml","triggers":"pandas, DataFrame, data manipulation, data cleaning, aggregation, groupby, merge, join, time series, data wrangling, pivot table, data transformation","role":"expert","scope":"implementation","output-format":"code","related-skills":"python-pro"}},"isInternal":false,"tokens":1492,"sizeBytes":6177},{"name":"async-patterns.md","path":"skills/php-pro/references/async-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/php-pro/references/async-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fasync-patterns.md -->","isInternal":false,"tokens":2379,"sizeBytes":9057},{"name":"laravel-patterns.md","path":"skills/php-pro/references/laravel-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/php-pro/references/laravel-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Flaravel-patterns.md -->","isInternal":false,"tokens":2010,"sizeBytes":8919},{"name":"modern-php-features.md","path":"skills/php-pro/references/modern-php-features.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/php-pro/references/modern-php-features.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fmodern-php-features.md -->","isInternal":false,"tokens":1672,"sizeBytes":6403},{"name":"symfony-patterns.md","path":"skills/php-pro/references/symfony-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/php-pro/references/symfony-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Fsymfony-patterns.md -->","isInternal":false,"tokens":2523,"sizeBytes":11358},{"name":"testing-quality.md","path":"skills/php-pro/references/testing-quality.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/php-pro/references/testing-quality.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2Freferences%2Ftesting-quality.md -->","isInternal":false,"tokens":2585,"sizeBytes":11491},{"name":"SKILL.md","path":"skills/php-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/php-pro/SKILL.md","title":"php-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fphp-pro%2FSKILL.md -->","frontmatter":{"name":"php-pro","description":"Use when building PHP applications with modern PHP 8.3+ features, Laravel, or Symfony frameworks. Invokes strict typing, PHPStan level 9, async patterns with Swoole, and PSR standards. Creates controllers, configures middleware, generates migrations, writes PHPUnit/Pest tests, defines typed DTOs and value objects, sets up dependency injection, and scaffolds REST/GraphQL APIs. Use when working with Eloquent, Doctrine, Composer, Psalm, ReactPHP, or any PHP API development.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"PHP, Laravel, Symfony, Composer, PHPStan, PSR, PHP API, Eloquent, Doctrine","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, fastapi-expert"}},"isInternal":false,"tokens":1464,"sizeBytes":6259},{"name":"api-mocking.md","path":"skills/playwright-expert/references/api-mocking.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/playwright-expert/references/api-mocking.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fapi-mocking.md -->","isInternal":false,"tokens":781,"sizeBytes":3145},{"name":"configuration.md","path":"skills/playwright-expert/references/configuration.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/playwright-expert/references/configuration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fconfiguration.md -->","isInternal":false,"tokens":857,"sizeBytes":3286},{"name":"debugging-flaky.md","path":"skills/playwright-expert/references/debugging-flaky.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/playwright-expert/references/debugging-flaky.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fdebugging-flaky.md -->","isInternal":false,"tokens":807,"sizeBytes":3207},{"name":"page-object-model.md","path":"skills/playwright-expert/references/page-object-model.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/playwright-expert/references/page-object-model.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fpage-object-model.md -->","isInternal":false,"tokens":844,"sizeBytes":3695},{"name":"selectors-locators.md","path":"skills/playwright-expert/references/selectors-locators.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/playwright-expert/references/selectors-locators.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2Freferences%2Fselectors-locators.md -->","isInternal":false,"tokens":723,"sizeBytes":2798},{"name":"SKILL.md","path":"skills/playwright-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/playwright-expert/SKILL.md","title":"playwright-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fplaywright-expert%2FSKILL.md -->","frontmatter":{"name":"playwright-expert","description":"Use when writing E2E tests with Playwright, setting up test infrastructure, or debugging flaky browser tests. Invoke to write test scripts, create page objects, configure test fixtures, set up reporters, add CI integration, implement API mocking, or perform visual regression testing. Trigger terms: Playwright, E2E test, end-to-end, browser testing, automation, UI testing, visual testing, Page Object Model, test flakiness.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"quality","triggers":"Playwright, E2E test, end-to-end, browser testing, automation, UI testing, visual testing","role":"specialist","scope":"testing","output-format":"code","related-skills":"test-master, react-expert, devops-engineer"}},"isInternal":false,"tokens":1360,"sizeBytes":5759},{"name":"extensions.md","path":"skills/postgres-pro/references/extensions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/postgres-pro/references/extensions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fextensions.md -->","isInternal":false,"tokens":2486,"sizeBytes":9590},{"name":"jsonb.md","path":"skills/postgres-pro/references/jsonb.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/postgres-pro/references/jsonb.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fjsonb.md -->","isInternal":false,"tokens":2094,"sizeBytes":8152},{"name":"maintenance.md","path":"skills/postgres-pro/references/maintenance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/postgres-pro/references/maintenance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fmaintenance.md -->","isInternal":false,"tokens":3399,"sizeBytes":12114},{"name":"performance.md","path":"skills/postgres-pro/references/performance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/postgres-pro/references/performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Fperformance.md -->","isInternal":false,"tokens":1819,"sizeBytes":7085},{"name":"replication.md","path":"skills/postgres-pro/references/replication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/postgres-pro/references/replication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2Freferences%2Freplication.md -->","isInternal":false,"tokens":2680,"sizeBytes":10035},{"name":"SKILL.md","path":"skills/postgres-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/postgres-pro/SKILL.md","title":"postgres-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpostgres-pro%2FSKILL.md -->","frontmatter":{"name":"postgres-pro","description":"Use when optimizing PostgreSQL queries, configuring replication, or implementing advanced database features. Invoke for EXPLAIN analysis, JSONB operations, extension usage, VACUUM tuning, performance monitoring.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"infrastructure","triggers":"PostgreSQL, Postgres, EXPLAIN ANALYZE, pg_stat, JSONB, streaming replication, logical replication, VACUUM, PostGIS, pgvector","role":"specialist","scope":"implementation","output-format":"code","related-skills":"database-optimizer, devops-engineer, sre-engineer"}},"isInternal":false,"tokens":1500,"sizeBytes":5997},{"name":"context-management.md","path":"skills/prompt-engineer/references/context-management.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/references/context-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fcontext-management.md -->","isInternal":false,"tokens":1605,"sizeBytes":7570},{"name":"evaluation-frameworks.md","path":"skills/prompt-engineer/references/evaluation-frameworks.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/references/evaluation-frameworks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fevaluation-frameworks.md -->","isInternal":false,"tokens":5231,"sizeBytes":23737},{"name":"prompt-optimization.md","path":"skills/prompt-engineer/references/prompt-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/references/prompt-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fprompt-optimization.md -->","isInternal":false,"tokens":3840,"sizeBytes":17619},{"name":"prompt-patterns.md","path":"skills/prompt-engineer/references/prompt-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/references/prompt-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fprompt-patterns.md -->","isInternal":false,"tokens":3433,"sizeBytes":16180},{"name":"structured-outputs.md","path":"skills/prompt-engineer/references/structured-outputs.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/references/structured-outputs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fstructured-outputs.md -->","isInternal":false,"tokens":4337,"sizeBytes":19093},{"name":"system-prompts.md","path":"skills/prompt-engineer/references/system-prompts.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/references/system-prompts.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2Freferences%2Fsystem-prompts.md -->","isInternal":false,"tokens":3674,"sizeBytes":18970},{"name":"SKILL.md","path":"skills/prompt-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/prompt-engineer/SKILL.md","title":"prompt-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fprompt-engineer%2FSKILL.md -->","frontmatter":{"name":"prompt-engineer","description":"Writes, refactors, and evaluates prompts for LLMs — generating optimized prompt templates, structured output schemas, evaluation rubrics, and test suites. Use when designing prompts for new LLM applications, refactoring existing prompts for better accuracy or token efficiency, implementing chain-of-thought or few-shot learning, creating system prompts with personas and guardrails, building JSON/function-calling schemas, or developing prompt evaluation frameworks to measure and improve model performance.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.2.0","domain":"data-ml","triggers":"prompt engineering, prompt optimization, chain-of-thought, few-shot learning, prompt testing, LLM prompts, prompt evaluation, system prompts, structured outputs, prompt design, context management, lost-in-the-middle, context degradation, token optimization, attention budget","role":"expert","scope":"design","output-format":"document","related-skills":"test-master, rag-architect, debugging-wizard"}},"isInternal":false,"tokens":1248,"sizeBytes":6046},{"name":"async-patterns.md","path":"skills/python-pro/references/async-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/python-pro/references/async-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fasync-patterns.md -->","isInternal":false,"tokens":2175,"sizeBytes":9333},{"name":"packaging.md","path":"skills/python-pro/references/packaging.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/python-pro/references/packaging.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fpackaging.md -->","isInternal":false,"tokens":2697,"sizeBytes":9652},{"name":"standard-library.md","path":"skills/python-pro/references/standard-library.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/python-pro/references/standard-library.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Fstandard-library.md -->","isInternal":false,"tokens":2488,"sizeBytes":9063},{"name":"testing.md","path":"skills/python-pro/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/python-pro/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Ftesting.md -->","isInternal":false,"tokens":2387,"sizeBytes":9920},{"name":"type-system.md","path":"skills/python-pro/references/type-system.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/python-pro/references/type-system.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2Freferences%2Ftype-system.md -->","isInternal":false,"tokens":1728,"sizeBytes":6638},{"name":"SKILL.md","path":"skills/python-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/python-pro/SKILL.md","title":"python-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fpython-pro%2FSKILL.md -->","frontmatter":{"name":"python-pro","description":"Use when building Python 3.11+ applications requiring type safety, async programming, or robust error handling. Generates type-annotated Python code, configures mypy in strict mode, writes pytest test suites with fixtures and mocking, and validates code with black and ruff. Invoke for type hints, async/await patterns, dataclasses, dependency injection, logging configuration, and structured error handling.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"Python development, type hints, async Python, pytest, mypy, dataclasses, Python best practices, Pythonic code","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fastapi-expert, devops-engineer"}},"isInternal":false,"tokens":1461,"sizeBytes":6270},{"name":"chunking-strategies.md","path":"skills/rag-architect/references/chunking-strategies.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rag-architect/references/chunking-strategies.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fchunking-strategies.md -->","isInternal":false,"tokens":5383,"sizeBytes":25083},{"name":"embedding-models.md","path":"skills/rag-architect/references/embedding-models.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rag-architect/references/embedding-models.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fembedding-models.md -->","isInternal":false,"tokens":3830,"sizeBytes":15342},{"name":"rag-evaluation.md","path":"skills/rag-architect/references/rag-evaluation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rag-architect/references/rag-evaluation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Frag-evaluation.md -->","isInternal":false,"tokens":5794,"sizeBytes":24288},{"name":"retrieval-optimization.md","path":"skills/rag-architect/references/retrieval-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rag-architect/references/retrieval-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fretrieval-optimization.md -->","isInternal":false,"tokens":5019,"sizeBytes":21926},{"name":"vector-databases.md","path":"skills/rag-architect/references/vector-databases.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rag-architect/references/vector-databases.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2Freferences%2Fvector-databases.md -->","isInternal":false,"tokens":3498,"sizeBytes":14274},{"name":"SKILL.md","path":"skills/rag-architect/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rag-architect/SKILL.md","title":"rag-architect","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frag-architect%2FSKILL.md -->","frontmatter":{"name":"rag-architect","description":"Designs and implements production-grade RAG systems by chunking documents, generating embeddings, configuring vector stores, building hybrid search pipelines, applying reranking, and evaluating retrieval quality. Use when building RAG systems, vector databases, or knowledge-grounded AI applications requiring semantic search, document retrieval, context augmentation, similarity search, or embedding-based indexing.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"data-ml","triggers":"RAG, retrieval-augmented generation, vector search, embeddings, semantic search, vector database, document retrieval, knowledge base, context retrieval, similarity search","role":"architect","scope":"system-design","output-format":"architecture","related-skills":"python-pro, database-optimizer, monitoring-expert, api-designer"}},"isInternal":false,"tokens":1861,"sizeBytes":8049},{"name":"active-record.md","path":"skills/rails-expert/references/active-record.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rails-expert/references/active-record.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Factive-record.md -->","isInternal":false,"tokens":1459,"sizeBytes":5512},{"name":"api-development.md","path":"skills/rails-expert/references/api-development.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rails-expert/references/api-development.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fapi-development.md -->","isInternal":false,"tokens":2202,"sizeBytes":8952},{"name":"background-jobs.md","path":"skills/rails-expert/references/background-jobs.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rails-expert/references/background-jobs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fbackground-jobs.md -->","isInternal":false,"tokens":1476,"sizeBytes":5442},{"name":"hotwire-turbo.md","path":"skills/rails-expert/references/hotwire-turbo.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rails-expert/references/hotwire-turbo.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Fhotwire-turbo.md -->","isInternal":false,"tokens":1301,"sizeBytes":5155},{"name":"rspec-testing.md","path":"skills/rails-expert/references/rspec-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rails-expert/references/rspec-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2Freferences%2Frspec-testing.md -->","isInternal":false,"tokens":2256,"sizeBytes":8564},{"name":"SKILL.md","path":"skills/rails-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rails-expert/SKILL.md","title":"rails-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frails-expert%2FSKILL.md -->","frontmatter":{"name":"rails-expert","description":"Rails 7+ specialist that optimizes Active Record queries with includes/eager_load, implements Turbo Frames and Turbo Streams for partial page updates, configures Action Cable for WebSocket connections, sets up Sidekiq workers for background job processing, and writes comprehensive RSpec test suites. Use when building Rails 7+ web applications with Hotwire, real-time features, or background job processing. Invoke for Active Record optimization, Turbo Frames/Streams, Action Cable, Sidekiq, RSpec Rails.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":"Rails, Ruby on Rails, Hotwire, Turbo Frames, Turbo Streams, Action Cable, Active Record, Sidekiq, RSpec Rails","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, database-optimizer"}},"isInternal":false,"tokens":1390,"sizeBytes":5685},{"name":"hooks-patterns.md","path":"skills/react-expert/references/hooks-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/hooks-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fhooks-patterns.md -->","isInternal":false,"tokens":880,"sizeBytes":3724},{"name":"migration-class-to-modern.md","path":"skills/react-expert/references/migration-class-to-modern.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/migration-class-to-modern.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fmigration-class-to-modern.md -->","isInternal":false,"tokens":5685,"sizeBytes":24024},{"name":"performance.md","path":"skills/react-expert/references/performance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fperformance.md -->","isInternal":false,"tokens":991,"sizeBytes":4041},{"name":"react-19-features.md","path":"skills/react-expert/references/react-19-features.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/react-19-features.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Freact-19-features.md -->","isInternal":false,"tokens":978,"sizeBytes":3851},{"name":"server-components.md","path":"skills/react-expert/references/server-components.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/server-components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fserver-components.md -->","isInternal":false,"tokens":766,"sizeBytes":3149},{"name":"state-management.md","path":"skills/react-expert/references/state-management.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/state-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Fstate-management.md -->","isInternal":false,"tokens":1038,"sizeBytes":4134},{"name":"testing-react.md","path":"skills/react-expert/references/testing-react.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/references/testing-react.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2Freferences%2Ftesting-react.md -->","isInternal":false,"tokens":965,"sizeBytes":4036},{"name":"SKILL.md","path":"skills/react-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-expert/SKILL.md","title":"react-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-expert%2FSKILL.md -->","frontmatter":{"name":"react-expert","description":"Use when building React 18+ applications in .jsx or .tsx files, Next.js App Router projects, or create-react-app setups. Creates components, implements custom hooks, debugs rendering issues, migrates class components to functional, and implements state management. Invoke for Server Components, Suspense boundaries, useActionState forms, performance optimization, or React 19 features.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"React, JSX, hooks, useState, useEffect, useContext, Server Components, React 19, Suspense, TanStack Query, Redux, Zustand, component, frontend","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, playwright-expert, test-master"}},"isInternal":false,"tokens":1258,"sizeBytes":5436},{"name":"expo-router.md","path":"skills/react-native-expert/references/expo-router.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-native-expert/references/expo-router.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fexpo-router.md -->","isInternal":false,"tokens":1026,"sizeBytes":4078},{"name":"list-optimization.md","path":"skills/react-native-expert/references/list-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-native-expert/references/list-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Flist-optimization.md -->","isInternal":false,"tokens":1073,"sizeBytes":4434},{"name":"platform-handling.md","path":"skills/react-native-expert/references/platform-handling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-native-expert/references/platform-handling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fplatform-handling.md -->","isInternal":false,"tokens":940,"sizeBytes":3881},{"name":"project-structure.md","path":"skills/react-native-expert/references/project-structure.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-native-expert/references/project-structure.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fproject-structure.md -->","isInternal":false,"tokens":1127,"sizeBytes":4116},{"name":"storage-hooks.md","path":"skills/react-native-expert/references/storage-hooks.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-native-expert/references/storage-hooks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2Freferences%2Fstorage-hooks.md -->","isInternal":false,"tokens":1069,"sizeBytes":4370},{"name":"SKILL.md","path":"skills/react-native-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/react-native-expert/SKILL.md","title":"react-native-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Freact-native-expert%2FSKILL.md -->","frontmatter":{"name":"react-native-expert","description":"Builds, optimizes, and debugs cross-platform mobile applications with React Native and Expo. Implements navigation hierarchies (tabs, stacks, drawers), configures native modules, optimizes FlatList rendering with memo and useCallback, and handles platform-specific code for iOS and Android. Use when building a React Native or Expo mobile app, setting up navigation, integrating native modules, improving scroll performance, handling SafeArea or keyboard input, or configuring Expo SDK projects.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"React Native, Expo, mobile app, iOS, Android, cross-platform, native module","role":"specialist","scope":"implementation","output-format":"code","related-skills":"react-expert, flutter-expert, test-master"}},"isInternal":false,"tokens":1577,"sizeBytes":6688},{"name":"async.md","path":"skills/rust-engineer/references/async.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rust-engineer/references/async.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Fasync.md -->","isInternal":false,"tokens":2671,"sizeBytes":10697},{"name":"error-handling.md","path":"skills/rust-engineer/references/error-handling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rust-engineer/references/error-handling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ferror-handling.md -->","isInternal":false,"tokens":2179,"sizeBytes":8135},{"name":"ownership.md","path":"skills/rust-engineer/references/ownership.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rust-engineer/references/ownership.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Fownership.md -->","isInternal":false,"tokens":1622,"sizeBytes":6080},{"name":"testing.md","path":"skills/rust-engineer/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rust-engineer/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ftesting.md -->","isInternal":false,"tokens":2622,"sizeBytes":9800},{"name":"traits.md","path":"skills/rust-engineer/references/traits.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rust-engineer/references/traits.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2Freferences%2Ftraits.md -->","isInternal":false,"tokens":2026,"sizeBytes":7821},{"name":"SKILL.md","path":"skills/rust-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/rust-engineer/SKILL.md","title":"rust-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Frust-engineer%2FSKILL.md -->","frontmatter":{"name":"rust-engineer","description":"Writes, reviews, and debugs idiomatic Rust code with memory safety and zero-cost abstractions. Implements ownership patterns, manages lifetimes, designs trait hierarchies, builds async applications with tokio, and structures error handling with Result/Option. Use when building Rust applications, solving ownership or borrowing issues, designing trait-based APIs, implementing async/await concurrency, creating FFI bindings, or optimizing for performance and memory safety. Invoke for Rust, Cargo, ownership, borrowing, lifetimes, async Rust, tokio, zero-cost abstractions, memory safety, systems programming.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"Rust, Cargo, ownership, borrowing, lifetimes, async Rust, tokio, zero-cost abstractions, memory safety, systems programming","role":"specialist","scope":"implementation","output-format":"code","related-skills":"test-master"}},"isInternal":false,"tokens":1467,"sizeBytes":6187},{"name":"apex-development.md","path":"skills/salesforce-developer/references/apex-development.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/salesforce-developer/references/apex-development.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fapex-development.md -->","isInternal":false,"tokens":5020,"sizeBytes":23559},{"name":"deployment-devops.md","path":"skills/salesforce-developer/references/deployment-devops.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/salesforce-developer/references/deployment-devops.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fdeployment-devops.md -->","isInternal":false,"tokens":5278,"sizeBytes":21318},{"name":"integration-patterns.md","path":"skills/salesforce-developer/references/integration-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/salesforce-developer/references/integration-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fintegration-patterns.md -->","isInternal":false,"tokens":5500,"sizeBytes":27872},{"name":"lightning-web-components.md","path":"skills/salesforce-developer/references/lightning-web-components.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/salesforce-developer/references/lightning-web-components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Flightning-web-components.md -->","isInternal":false,"tokens":4930,"sizeBytes":24090},{"name":"soql-sosl.md","path":"skills/salesforce-developer/references/soql-sosl.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/salesforce-developer/references/soql-sosl.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2Freferences%2Fsoql-sosl.md -->","isInternal":false,"tokens":4044,"sizeBytes":17018},{"name":"SKILL.md","path":"skills/salesforce-developer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/salesforce-developer/SKILL.md","title":"salesforce-developer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsalesforce-developer%2FSKILL.md -->","frontmatter":{"name":"salesforce-developer","description":"Writes and debugs Apex code, builds Lightning Web Components, optimizes SOQL queries, implements triggers, batch jobs, platform events, and integrations on the Salesforce platform. Use when developing Salesforce applications, customizing CRM workflows, managing governor limits, bulk processing, or setting up Salesforce DX and CI/CD pipelines.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"platform","triggers":"Salesforce, Apex, Lightning Web Components, LWC, SOQL, SOSL, Visualforce, Salesforce DX, governor limits, triggers, platform events, CRM integration, Sales Cloud, Service Cloud","role":"expert","scope":"implementation","output-format":"code","related-skills":"api-designer, java-architect, cloud-architect, devops-engineer"}},"isInternal":false,"tokens":1757,"sizeBytes":7691},{"name":"authentication.md","path":"skills/secure-code-guardian/references/authentication.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/secure-code-guardian/references/authentication.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fauthentication.md -->","isInternal":false,"tokens":874,"sizeBytes":3349},{"name":"input-validation.md","path":"skills/secure-code-guardian/references/input-validation.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/secure-code-guardian/references/input-validation.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Finput-validation.md -->","isInternal":false,"tokens":853,"sizeBytes":3372},{"name":"owasp-prevention.md","path":"skills/secure-code-guardian/references/owasp-prevention.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/secure-code-guardian/references/owasp-prevention.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fowasp-prevention.md -->","isInternal":false,"tokens":850,"sizeBytes":3335},{"name":"security-headers.md","path":"skills/secure-code-guardian/references/security-headers.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/secure-code-guardian/references/security-headers.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fsecurity-headers.md -->","isInternal":false,"tokens":804,"sizeBytes":3020},{"name":"xss-csrf.md","path":"skills/secure-code-guardian/references/xss-csrf.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/secure-code-guardian/references/xss-csrf.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2Freferences%2Fxss-csrf.md -->","isInternal":false,"tokens":854,"sizeBytes":3213},{"name":"SKILL.md","path":"skills/secure-code-guardian/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/secure-code-guardian/SKILL.md","title":"secure-code-guardian","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecure-code-guardian%2FSKILL.md -->","frontmatter":{"name":"secure-code-guardian","description":"Use when implementing authentication/authorization, securing user input, or preventing OWASP Top 10 vulnerabilities — including custom security implementations such as hashing passwords with bcrypt/argon2, sanitizing SQL queries with parameterized statements, configuring CORS/CSP headers, validating input with Zod, and setting up JWT tokens. Invoke for authentication, authorization, input validation, encryption, OWASP Top 10 prevention, secure session management, and security hardening. For pre-built OAuth/SSO integrations or standalone security audits, consider a more specialized skill.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"security","triggers":"security, authentication, authorization, encryption, OWASP, vulnerability, secure coding, password, JWT, OAuth","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, security-reviewer, architecture-designer"}},"isInternal":false,"tokens":1685,"sizeBytes":7220},{"name":"infrastructure-security.md","path":"skills/security-reviewer/references/infrastructure-security.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/references/infrastructure-security.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Finfrastructure-security.md -->","isInternal":false,"tokens":1556,"sizeBytes":5910},{"name":"penetration-testing.md","path":"skills/security-reviewer/references/penetration-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/references/penetration-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fpenetration-testing.md -->","isInternal":false,"tokens":1587,"sizeBytes":5980},{"name":"report-template.md","path":"skills/security-reviewer/references/report-template.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/references/report-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Freport-template.md -->","isInternal":false,"tokens":1002,"sizeBytes":3752},{"name":"sast-tools.md","path":"skills/security-reviewer/references/sast-tools.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/references/sast-tools.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fsast-tools.md -->","isInternal":false,"tokens":688,"sizeBytes":2412},{"name":"secret-scanning.md","path":"skills/security-reviewer/references/secret-scanning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/references/secret-scanning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fsecret-scanning.md -->","isInternal":false,"tokens":912,"sizeBytes":2994},{"name":"vulnerability-patterns.md","path":"skills/security-reviewer/references/vulnerability-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/references/vulnerability-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2Freferences%2Fvulnerability-patterns.md -->","isInternal":false,"tokens":957,"sizeBytes":3917},{"name":"SKILL.md","path":"skills/security-reviewer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/security-reviewer/SKILL.md","title":"security-reviewer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsecurity-reviewer%2FSKILL.md -->","frontmatter":{"name":"security-reviewer","description":"Identifies security vulnerabilities, generates structured audit reports with severity ratings, and provides actionable remediation guidance. Use when conducting security audits, reviewing code for vulnerabilities, or analyzing infrastructure security. Invoke for SAST scans, penetration testing, DevSecOps practices, cloud security reviews, dependency audits, secrets scanning, or compliance checks. Produces vulnerability reports, prioritized recommendations, and compliance checklists.","license":"MIT","allowed-tools":"Read Grep Glob","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"security","triggers":"security review, vulnerability scan, SAST, security audit, penetration test, code audit, security analysis, infrastructure security, DevSecOps, cloud security, compliance audit","role":"specialist","scope":"review","output-format":"report","related-skills":"secure-code-guardian, code-reviewer, devops-engineer, cloud-architect, kubernetes-specialist, api-designer, mcp-developer"},"allowedTools":["Read","Grep","Glob"]},"allowedTools":["Read","Grep","Glob"],"isInternal":false,"tokens":1114,"sizeBytes":5046},{"name":"app-development.md","path":"skills/shopify-expert/references/app-development.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/shopify-expert/references/app-development.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fapp-development.md -->","isInternal":false,"tokens":4610,"sizeBytes":20299},{"name":"checkout-customization.md","path":"skills/shopify-expert/references/checkout-customization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/shopify-expert/references/checkout-customization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fcheckout-customization.md -->","isInternal":false,"tokens":4914,"sizeBytes":22026},{"name":"liquid-templating.md","path":"skills/shopify-expert/references/liquid-templating.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/shopify-expert/references/liquid-templating.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fliquid-templating.md -->","isInternal":false,"tokens":4498,"sizeBytes":19454},{"name":"performance-optimization.md","path":"skills/shopify-expert/references/performance-optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/shopify-expert/references/performance-optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fperformance-optimization.md -->","isInternal":false,"tokens":4594,"sizeBytes":18399},{"name":"storefront-api.md","path":"skills/shopify-expert/references/storefront-api.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/shopify-expert/references/storefront-api.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2Freferences%2Fstorefront-api.md -->","isInternal":false,"tokens":4445,"sizeBytes":19166},{"name":"SKILL.md","path":"skills/shopify-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/shopify-expert/SKILL.md","title":"shopify-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fshopify-expert%2FSKILL.md -->","frontmatter":{"name":"shopify-expert","description":"Builds and debugs Shopify themes (.liquid files, theme.json, sections), develops custom Shopify apps (shopify.app.toml, OAuth, webhooks), and implements Storefront API integrations for headless storefronts. Use when building or customizing Shopify themes, creating Hydrogen or custom React storefronts, developing Shopify apps, implementing checkout UI extensions or Shopify Functions, optimizing performance, or integrating third-party services. Invoke for Liquid templating, Storefront API, app development, checkout customization, Shopify Plus features, App Bridge, Polaris, or Shopify CLI workflows.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"platform","triggers":"Shopify, Liquid, Storefront API, Shopify Plus, Hydrogen, Shopify app, checkout extensions, Shopify Functions, App Bridge, theme development, e-commerce, Polaris","role":"expert","scope":"implementation","output-format":"code","related-skills":"react-expert, graphql-architect, api-designer"}},"isInternal":false,"tokens":1591,"sizeBytes":7298},{"name":"partitioning-caching.md","path":"skills/spark-engineer/references/partitioning-caching.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spark-engineer/references/partitioning-caching.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fpartitioning-caching.md -->","isInternal":false,"tokens":3758,"sizeBytes":14840},{"name":"performance-tuning.md","path":"skills/spark-engineer/references/performance-tuning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spark-engineer/references/performance-tuning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fperformance-tuning.md -->","isInternal":false,"tokens":4049,"sizeBytes":15875},{"name":"rdd-operations.md","path":"skills/spark-engineer/references/rdd-operations.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spark-engineer/references/rdd-operations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Frdd-operations.md -->","isInternal":false,"tokens":4260,"sizeBytes":16382},{"name":"spark-sql-dataframes.md","path":"skills/spark-engineer/references/spark-sql-dataframes.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spark-engineer/references/spark-sql-dataframes.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fspark-sql-dataframes.md -->","isInternal":false,"tokens":3468,"sizeBytes":13842},{"name":"streaming-patterns.md","path":"skills/spark-engineer/references/streaming-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spark-engineer/references/streaming-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2Freferences%2Fstreaming-patterns.md -->","isInternal":false,"tokens":5168,"sizeBytes":20501},{"name":"SKILL.md","path":"skills/spark-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spark-engineer/SKILL.md","title":"spark-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspark-engineer%2FSKILL.md -->","frontmatter":{"name":"spark-engineer","description":"Use when writing Spark jobs, debugging performance issues, or configuring cluster settings for Apache Spark applications, distributed data processing pipelines, or big data workloads. Invoke to write DataFrame transformations, optimize Spark SQL queries, implement RDD pipelines, tune shuffle operations, configure executor memory, process .parquet files, handle data partitioning, or build structured streaming analytics.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"data-ml","triggers":"Apache Spark, PySpark, Spark SQL, distributed computing, big data, DataFrame API, RDD, Spark Streaming, structured streaming, data partitioning, Spark performance, cluster computing, data processing pipeline","role":"expert","scope":"implementation","output-format":"code","related-skills":"python-pro, sql-pro, devops-engineer"}},"isInternal":false,"tokens":1507,"sizeBytes":6714},{"name":"analysis-checklist.md","path":"skills/spec-miner/references/analysis-checklist.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spec-miner/references/analysis-checklist.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fanalysis-checklist.md -->","isInternal":false,"tokens":534,"sizeBytes":2040},{"name":"analysis-process.md","path":"skills/spec-miner/references/analysis-process.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spec-miner/references/analysis-process.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fanalysis-process.md -->","isInternal":false,"tokens":442,"sizeBytes":1280},{"name":"ears-format.md","path":"skills/spec-miner/references/ears-format.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spec-miner/references/ears-format.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fears-format.md -->","isInternal":false,"tokens":457,"sizeBytes":1835},{"name":"specification-template.md","path":"skills/spec-miner/references/specification-template.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spec-miner/references/specification-template.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2Freferences%2Fspecification-template.md -->","isInternal":false,"tokens":568,"sizeBytes":2475},{"name":"SKILL.md","path":"skills/spec-miner/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spec-miner/SKILL.md","title":"spec-miner","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspec-miner%2FSKILL.md -->","frontmatter":{"name":"spec-miner","description":"Reverse-engineering specialist that extracts specifications from existing codebases. Use when working with legacy or undocumented systems, inherited projects, or old codebases with no documentation. Invoke to map code dependencies, generate API documentation from source, identify undocumented business logic, figure out what code does, or create architecture documentation from implementation. Trigger phrases: reverse engineer, old codebase, no docs, no documentation, figure out how this works, inherited project, legacy analysis, code archaeology, undocumented features.","license":"MIT","allowed-tools":"Read Grep Glob","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"workflow","triggers":"reverse engineer, legacy code, code analysis, undocumented, understand codebase, existing system","role":"specialist","scope":"review","output-format":"document","related-skills":"feature-forge, fullstack-guardian, architecture-designer"},"allowedTools":["Read","Grep","Glob"]},"allowedTools":["Read","Grep","Glob"],"isInternal":false,"tokens":1030,"sizeBytes":4763},{"name":"cloud.md","path":"skills/spring-boot-engineer/references/cloud.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spring-boot-engineer/references/cloud.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fcloud.md -->","isInternal":false,"tokens":2741,"sizeBytes":12339},{"name":"data.md","path":"skills/spring-boot-engineer/references/data.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spring-boot-engineer/references/data.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fdata.md -->","isInternal":false,"tokens":2278,"sizeBytes":11023},{"name":"security.md","path":"skills/spring-boot-engineer/references/security.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spring-boot-engineer/references/security.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fsecurity.md -->","isInternal":false,"tokens":2662,"sizeBytes":15275},{"name":"testing.md","path":"skills/spring-boot-engineer/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spring-boot-engineer/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Ftesting.md -->","isInternal":false,"tokens":2973,"sizeBytes":14985},{"name":"web.md","path":"skills/spring-boot-engineer/references/web.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spring-boot-engineer/references/web.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2Freferences%2Fweb.md -->","isInternal":false,"tokens":1733,"sizeBytes":8990},{"name":"SKILL.md","path":"skills/spring-boot-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/spring-boot-engineer/SKILL.md","title":"spring-boot-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fspring-boot-engineer%2FSKILL.md -->","frontmatter":{"name":"spring-boot-engineer","description":"Generates Spring Boot 3.x configurations, creates REST controllers, implements Spring Security 6 authentication flows, sets up Spring Data JPA repositories, and configures reactive WebFlux endpoints. Use when building Spring Boot 3.x applications, microservices, or reactive Java applications; invoke for Spring Data JPA, Spring Security 6, WebFlux, Spring Cloud integration, Java REST API design, or Microservices Java architecture.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"backend","triggers":"Spring Boot, Spring Framework, Spring Cloud, Spring Security, Spring Data JPA, Spring WebFlux, Microservices Java, Java REST API, Reactive Java","role":"specialist","scope":"implementation","output-format":"code","related-skills":"java-architect, database-optimizer, microservices-architect, devops-engineer"}},"isInternal":false,"tokens":1597,"sizeBytes":7523},{"name":"database-design.md","path":"skills/sql-pro/references/database-design.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sql-pro/references/database-design.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fdatabase-design.md -->","isInternal":false,"tokens":2855,"sizeBytes":12540},{"name":"dialect-differences.md","path":"skills/sql-pro/references/dialect-differences.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sql-pro/references/dialect-differences.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fdialect-differences.md -->","isInternal":false,"tokens":3101,"sizeBytes":11951},{"name":"optimization.md","path":"skills/sql-pro/references/optimization.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sql-pro/references/optimization.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Foptimization.md -->","isInternal":false,"tokens":2728,"sizeBytes":10820},{"name":"query-patterns.md","path":"skills/sql-pro/references/query-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sql-pro/references/query-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fquery-patterns.md -->","isInternal":false,"tokens":1871,"sizeBytes":7439},{"name":"window-functions.md","path":"skills/sql-pro/references/window-functions.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sql-pro/references/window-functions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2Freferences%2Fwindow-functions.md -->","isInternal":false,"tokens":2313,"sizeBytes":8860},{"name":"SKILL.md","path":"skills/sql-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sql-pro/SKILL.md","title":"sql-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsql-pro%2FSKILL.md -->","frontmatter":{"name":"sql-pro","description":"Optimizes SQL queries, designs database schemas, and troubleshoots performance issues. Use when a user asks why their query is slow, needs help writing complex joins or aggregations, mentions database performance issues, or wants to design or migrate a schema. Invoke for complex queries, window functions, CTEs, indexing strategies, query plan analysis, covering index creation, recursive queries, EXPLAIN/ANALYZE interpretation, before/after query benchmarking, or migrating queries between database dialects (PostgreSQL, MySQL, SQL Server, Oracle).","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"SQL optimization, query performance, database design, PostgreSQL, MySQL, SQL Server, window functions, CTEs, query tuning, EXPLAIN plan, database indexing","role":"specialist","scope":"implementation","output-format":"code","related-skills":"devops-engineer"}},"isInternal":false,"tokens":1227,"sizeBytes":5400},{"name":"automation-toil.md","path":"skills/sre-engineer/references/automation-toil.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sre-engineer/references/automation-toil.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fautomation-toil.md -->","isInternal":false,"tokens":3368,"sizeBytes":14820},{"name":"error-budget-policy.md","path":"skills/sre-engineer/references/error-budget-policy.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sre-engineer/references/error-budget-policy.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Ferror-budget-policy.md -->","isInternal":false,"tokens":2398,"sizeBytes":9852},{"name":"incident-chaos.md","path":"skills/sre-engineer/references/incident-chaos.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sre-engineer/references/incident-chaos.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fincident-chaos.md -->","isInternal":false,"tokens":3860,"sizeBytes":16791},{"name":"monitoring-alerting.md","path":"skills/sre-engineer/references/monitoring-alerting.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sre-engineer/references/monitoring-alerting.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fmonitoring-alerting.md -->","isInternal":false,"tokens":2567,"sizeBytes":11553},{"name":"slo-sli-management.md","path":"skills/sre-engineer/references/slo-sli-management.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sre-engineer/references/slo-sli-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2Freferences%2Fslo-sli-management.md -->","isInternal":false,"tokens":1709,"sizeBytes":6774},{"name":"SKILL.md","path":"skills/sre-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/sre-engineer/SKILL.md","title":"sre-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fsre-engineer%2FSKILL.md -->","frontmatter":{"name":"sre-engineer","description":"Defines service level objectives, creates error budget policies, designs incident response procedures, develops capacity models, and produces monitoring configurations and automation scripts for production systems. Use when defining SLIs/SLOs, managing error budgets, building reliable systems at scale, incident management, chaos engineering, toil reduction, or capacity planning.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"devops","triggers":"SRE, site reliability, SLO, SLI, error budget, incident management, chaos engineering, toil reduction, on-call, MTTR","role":"specialist","scope":"implementation","output-format":"code","related-skills":"devops-engineer, cloud-architect, kubernetes-specialist"}},"isInternal":false,"tokens":1685,"sizeBytes":6933},{"name":"async-concurrency.md","path":"skills/swift-expert/references/async-concurrency.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/swift-expert/references/async-concurrency.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fasync-concurrency.md -->","isInternal":false,"tokens":1851,"sizeBytes":8467},{"name":"memory-performance.md","path":"skills/swift-expert/references/memory-performance.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/swift-expert/references/memory-performance.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fmemory-performance.md -->","isInternal":false,"tokens":1934,"sizeBytes":8256},{"name":"protocol-oriented.md","path":"skills/swift-expert/references/protocol-oriented.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/swift-expert/references/protocol-oriented.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fprotocol-oriented.md -->","isInternal":false,"tokens":1751,"sizeBytes":7360},{"name":"swiftui-patterns.md","path":"skills/swift-expert/references/swiftui-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/swift-expert/references/swiftui-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Fswiftui-patterns.md -->","isInternal":false,"tokens":1575,"sizeBytes":6806},{"name":"testing-patterns.md","path":"skills/swift-expert/references/testing-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/swift-expert/references/testing-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2Freferences%2Ftesting-patterns.md -->","isInternal":false,"tokens":2008,"sizeBytes":9060},{"name":"SKILL.md","path":"skills/swift-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/swift-expert/SKILL.md","title":"swift-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fswift-expert%2FSKILL.md -->","frontmatter":{"name":"swift-expert","description":"Builds iOS/macOS/watchOS/tvOS applications, implements SwiftUI views and state management, designs protocol-oriented architectures, handles async/await concurrency, implements actors for thread safety, and debugs Swift-specific issues. Use when building iOS/macOS applications with Swift 5.9+, SwiftUI, or async/await concurrency. Invoke for protocol-oriented programming, SwiftUI state management, actors, server-side Swift, UIKit integration, Combine, or Vapor.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"Swift, SwiftUI, iOS development, macOS development, async/await Swift, Combine, UIKit, Vapor","role":"specialist","scope":"implementation","output-format":"code","related-skills":""}},"isInternal":false,"tokens":1368,"sizeBytes":6022},{"name":"best-practices.md","path":"skills/terraform-engineer/references/best-practices.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/terraform-engineer/references/best-practices.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fbest-practices.md -->","isInternal":false,"tokens":3518,"sizeBytes":12558},{"name":"module-patterns.md","path":"skills/terraform-engineer/references/module-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/terraform-engineer/references/module-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fmodule-patterns.md -->","isInternal":false,"tokens":1670,"sizeBytes":5900},{"name":"providers.md","path":"skills/terraform-engineer/references/providers.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/terraform-engineer/references/providers.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fproviders.md -->","isInternal":false,"tokens":2418,"sizeBytes":9121},{"name":"state-management.md","path":"skills/terraform-engineer/references/state-management.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/terraform-engineer/references/state-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Fstate-management.md -->","isInternal":false,"tokens":1974,"sizeBytes":7913},{"name":"testing.md","path":"skills/terraform-engineer/references/testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/terraform-engineer/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2Freferences%2Ftesting.md -->","isInternal":false,"tokens":2702,"sizeBytes":9816},{"name":"SKILL.md","path":"skills/terraform-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/terraform-engineer/SKILL.md","title":"terraform-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fterraform-engineer%2FSKILL.md -->","frontmatter":{"name":"terraform-engineer","description":"Use when implementing infrastructure as code with Terraform across AWS, Azure, or GCP. Invoke for module development (create reusable modules, manage module versioning), state management (migrate backends, import existing resources, resolve state conflicts), provider configuration, multi-environment workflows, and infrastructure testing.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"infrastructure","triggers":"Terraform, infrastructure as code, IaC, terraform module, terraform state, AWS provider, Azure provider, GCP provider, terraform plan, terraform apply","role":"specialist","scope":"implementation","output-format":"code","related-skills":"cloud-architect, devops-engineer, kubernetes-specialist"}},"isInternal":false,"tokens":1328,"sizeBytes":5779},{"name":"automation-frameworks.md","path":"skills/test-master/references/automation-frameworks.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/automation-frameworks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fautomation-frameworks.md -->","isInternal":false,"tokens":1885,"sizeBytes":7414},{"name":"e2e-testing.md","path":"skills/test-master/references/e2e-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/e2e-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fe2e-testing.md -->","isInternal":false,"tokens":830,"sizeBytes":3391},{"name":"integration-testing.md","path":"skills/test-master/references/integration-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/integration-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fintegration-testing.md -->","isInternal":false,"tokens":668,"sizeBytes":2748},{"name":"performance-testing.md","path":"skills/test-master/references/performance-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/performance-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fperformance-testing.md -->","isInternal":false,"tokens":790,"sizeBytes":2627},{"name":"qa-methodology.md","path":"skills/test-master/references/qa-methodology.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/qa-methodology.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fqa-methodology.md -->","isInternal":false,"tokens":1715,"sizeBytes":6375},{"name":"security-testing.md","path":"skills/test-master/references/security-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/security-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Fsecurity-testing.md -->","isInternal":false,"tokens":841,"sizeBytes":3368},{"name":"tdd-iron-laws.md","path":"skills/test-master/references/tdd-iron-laws.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/tdd-iron-laws.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftdd-iron-laws.md -->","isInternal":false,"tokens":1047,"sizeBytes":4366},{"name":"test-reports.md","path":"skills/test-master/references/test-reports.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/test-reports.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftest-reports.md -->","isInternal":false,"tokens":657,"sizeBytes":2399},{"name":"testing-anti-patterns.md","path":"skills/test-master/references/testing-anti-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/testing-anti-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Ftesting-anti-patterns.md -->","isInternal":false,"tokens":1487,"sizeBytes":6516},{"name":"unit-testing.md","path":"skills/test-master/references/unit-testing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/references/unit-testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2Freferences%2Funit-testing.md -->","isInternal":false,"tokens":636,"sizeBytes":2721},{"name":"SKILL.md","path":"skills/test-master/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/test-master/SKILL.md","title":"test-master","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftest-master%2FSKILL.md -->","frontmatter":{"name":"test-master","description":"Generates test files, creates mocking strategies, analyzes code coverage, designs test architectures, and produces test plans and defect reports across functional, performance, and security testing disciplines. Use when writing unit tests, integration tests, or E2E tests; creating test strategies or automation frameworks; analyzing coverage gaps; performance testing with k6 or Artillery; security testing with OWASP methods; debugging flaky tests; or working on QA, regression, test automation, quality gates, shift-left testing, or test maintenance.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"quality","triggers":"test, testing, QA, unit test, integration test, E2E, coverage, performance test, security test, regression, test strategy, test automation, test framework, quality metrics, defect, exploratory, usability, accessibility, localization, manual testing, shift-left, quality gate, flaky test, test maintenance","role":"specialist","scope":"testing","output-format":"report","related-skills":"fullstack-guardian, playwright-expert, devops-engineer, debugging-wizard, code-reviewer, feature-forge"}},"isInternal":false,"tokens":1159,"sizeBytes":5210},{"name":"dialectic-synthesis.md","path":"skills/the-fool/references/dialectic-synthesis.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/references/dialectic-synthesis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fdialectic-synthesis.md -->","isInternal":false,"tokens":1434,"sizeBytes":6456},{"name":"evidence-audit.md","path":"skills/the-fool/references/evidence-audit.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/references/evidence-audit.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fevidence-audit.md -->","isInternal":false,"tokens":1912,"sizeBytes":8453},{"name":"mode-selection-guide.md","path":"skills/the-fool/references/mode-selection-guide.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/references/mode-selection-guide.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fmode-selection-guide.md -->","isInternal":false,"tokens":987,"sizeBytes":4604},{"name":"pre-mortem-analysis.md","path":"skills/the-fool/references/pre-mortem-analysis.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/references/pre-mortem-analysis.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fpre-mortem-analysis.md -->","isInternal":false,"tokens":1769,"sizeBytes":8064},{"name":"red-team-adversarial.md","path":"skills/the-fool/references/red-team-adversarial.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/references/red-team-adversarial.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fred-team-adversarial.md -->","isInternal":false,"tokens":1658,"sizeBytes":7169},{"name":"socratic-questioning.md","path":"skills/the-fool/references/socratic-questioning.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/references/socratic-questioning.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2Freferences%2Fsocratic-questioning.md -->","isInternal":false,"tokens":1224,"sizeBytes":5289},{"name":"SKILL.md","path":"skills/the-fool/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/the-fool/SKILL.md","title":"the-fool","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fthe-fool%2FSKILL.md -->","frontmatter":{"name":"the-fool","description":"Use when challenging ideas, plans, decisions, or proposals using structured critical reasoning. Invoke to play devil's advocate, run a pre-mortem, red team, or audit evidence and assumptions.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.0.0","domain":"workflow","triggers":"play the fool, devil's advocate, challenge this, stress test, poke holes, what could go wrong, red team, pre-mortem, test my assumptions","role":"expert","scope":"review","output-format":"report","related-skills":"architecture-designer, code-reviewer, feature-forge"}},"isInternal":false,"tokens":1420,"sizeBytes":6381},{"name":"advanced-types.md","path":"skills/typescript-pro/references/advanced-types.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/typescript-pro/references/advanced-types.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fadvanced-types.md -->","isInternal":false,"tokens":1797,"sizeBytes":6449},{"name":"configuration.md","path":"skills/typescript-pro/references/configuration.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/typescript-pro/references/configuration.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fconfiguration.md -->","isInternal":false,"tokens":2403,"sizeBytes":8965},{"name":"patterns.md","path":"skills/typescript-pro/references/patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/typescript-pro/references/patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Fpatterns.md -->","isInternal":false,"tokens":2854,"sizeBytes":11213},{"name":"type-guards.md","path":"skills/typescript-pro/references/type-guards.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/typescript-pro/references/type-guards.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Ftype-guards.md -->","isInternal":false,"tokens":1979,"sizeBytes":7929},{"name":"utility-types.md","path":"skills/typescript-pro/references/utility-types.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/typescript-pro/references/utility-types.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2Freferences%2Futility-types.md -->","isInternal":false,"tokens":2305,"sizeBytes":8602},{"name":"SKILL.md","path":"skills/typescript-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/typescript-pro/SKILL.md","title":"typescript-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Ftypescript-pro%2FSKILL.md -->","frontmatter":{"name":"typescript-pro","description":"Implements advanced TypeScript type systems, creates custom type guards, utility types, and branded types, and configures tRPC for end-to-end type safety. Use when building TypeScript applications requiring advanced generics, conditional or mapped types, discriminated unions, monorepo setup, or full-stack type safety with tRPC.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"language","triggers":"TypeScript, generics, type safety, conditional types, mapped types, tRPC, tsconfig, type guards, discriminated unions","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fullstack-guardian, api-designer"}},"isInternal":false,"tokens":1317,"sizeBytes":5627},{"name":"component-architecture.md","path":"skills/vue-expert-js/references/component-architecture.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert-js/references/component-architecture.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fcomponent-architecture.md -->","isInternal":false,"tokens":1296,"sizeBytes":4724},{"name":"composables-patterns.md","path":"skills/vue-expert-js/references/composables-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert-js/references/composables-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fcomposables-patterns.md -->","isInternal":false,"tokens":1057,"sizeBytes":4268},{"name":"jsdoc-typing.md","path":"skills/vue-expert-js/references/jsdoc-typing.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert-js/references/jsdoc-typing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fjsdoc-typing.md -->","isInternal":false,"tokens":2922,"sizeBytes":11062},{"name":"state-management.md","path":"skills/vue-expert-js/references/state-management.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert-js/references/state-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Fstate-management.md -->","isInternal":false,"tokens":1391,"sizeBytes":5543},{"name":"testing-patterns.md","path":"skills/vue-expert-js/references/testing-patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert-js/references/testing-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2Freferences%2Ftesting-patterns.md -->","isInternal":false,"tokens":1465,"sizeBytes":5777},{"name":"SKILL.md","path":"skills/vue-expert-js/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert-js/SKILL.md","title":"vue-expert-js","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert-js%2FSKILL.md -->","frontmatter":{"name":"vue-expert-js","description":"Creates Vue 3 components, builds vanilla JS composables, configures Vite projects, and sets up routing and state management using JavaScript only — no TypeScript. Generates JSDoc-typed code with @typedef, @param, and @returns annotations for full type coverage without a TS compiler. Use when building Vue 3 applications with JavaScript only (no TypeScript), when projects require JSDoc-based type hints, when migrating from Vue 2 Options API to Composition API in JS, or when teams prefer vanilla JavaScript, .mjs modules, or need quick prototypes without TypeScript setup.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"Vue JavaScript, Vue without TypeScript, Vue JSDoc, Vue JS only, Vue vanilla JavaScript, .mjs Vue, Vue no TS","role":"specialist","scope":"implementation","output-format":"code","related-skills":"vue-expert, javascript-pro"}},"isInternal":false,"tokens":1566,"sizeBytes":6258},{"name":"build-tooling.md","path":"skills/vue-expert/references/build-tooling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/build-tooling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fbuild-tooling.md -->","isInternal":false,"tokens":2489,"sizeBytes":9632},{"name":"components.md","path":"skills/vue-expert/references/components.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/components.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fcomponents.md -->","isInternal":false,"tokens":2376,"sizeBytes":9039},{"name":"composition-api.md","path":"skills/vue-expert/references/composition-api.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/composition-api.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fcomposition-api.md -->","isInternal":false,"tokens":1450,"sizeBytes":5942},{"name":"mobile-hybrid.md","path":"skills/vue-expert/references/mobile-hybrid.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/mobile-hybrid.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fmobile-hybrid.md -->","isInternal":false,"tokens":3429,"sizeBytes":13867},{"name":"nuxt.md","path":"skills/vue-expert/references/nuxt.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/nuxt.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fnuxt.md -->","isInternal":false,"tokens":3780,"sizeBytes":14406},{"name":"state-management.md","path":"skills/vue-expert/references/state-management.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/state-management.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Fstate-management.md -->","isInternal":false,"tokens":2461,"sizeBytes":9850},{"name":"typescript.md","path":"skills/vue-expert/references/typescript.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/references/typescript.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2Freferences%2Ftypescript.md -->","isInternal":false,"tokens":2870,"sizeBytes":10944},{"name":"SKILL.md","path":"skills/vue-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/vue-expert/SKILL.md","title":"vue-expert","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fvue-expert%2FSKILL.md -->","frontmatter":{"name":"vue-expert","description":"Builds Vue 3 components with Composition API patterns, configures Nuxt 3 SSR/SSG projects, sets up Pinia stores, scaffolds Quasar/Capacitor mobile apps, implements PWA features, and optimises Vite builds. Use when creating Vue 3 applications with Composition API, writing reusable composables, managing state with Pinia, building hybrid mobile apps with Quasar or Capacitor, configuring service workers, or tuning Vite configuration and TypeScript integration.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"frontend","triggers":"Vue 3, Composition API, Nuxt, Pinia, Vue composables, reactive, ref, Vue Router, Vite Vue, Quasar, Capacitor, PWA, service worker, Fastify SSR, sourcemap, Vite config, build optimization","role":"specialist","scope":"implementation","output-format":"code","related-skills":"typescript-pro, fullstack-guardian"}},"isInternal":false,"tokens":1062,"sizeBytes":4459},{"name":"alternatives.md","path":"skills/websocket-engineer/references/alternatives.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/websocket-engineer/references/alternatives.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Falternatives.md -->","isInternal":false,"tokens":2103,"sizeBytes":8505},{"name":"patterns.md","path":"skills/websocket-engineer/references/patterns.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/websocket-engineer/references/patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fpatterns.md -->","isInternal":false,"tokens":2316,"sizeBytes":9654},{"name":"protocol.md","path":"skills/websocket-engineer/references/protocol.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/websocket-engineer/references/protocol.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fprotocol.md -->","isInternal":false,"tokens":1411,"sizeBytes":4896},{"name":"scaling.md","path":"skills/websocket-engineer/references/scaling.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/websocket-engineer/references/scaling.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fscaling.md -->","isInternal":false,"tokens":1947,"sizeBytes":7548},{"name":"security.md","path":"skills/websocket-engineer/references/security.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/websocket-engineer/references/security.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2Freferences%2Fsecurity.md -->","isInternal":false,"tokens":2518,"sizeBytes":10336},{"name":"SKILL.md","path":"skills/websocket-engineer/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/websocket-engineer/SKILL.md","title":"websocket-engineer","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwebsocket-engineer%2FSKILL.md -->","frontmatter":{"name":"websocket-engineer","description":"Use when building real-time communication systems with WebSockets or Socket.IO. Invoke for bidirectional messaging, horizontal scaling with Redis, presence tracking, room management.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.1","domain":"api-architecture","triggers":"WebSocket, Socket.IO, real-time communication, bidirectional messaging, pub/sub, server push, live updates, chat systems, presence tracking","role":"specialist","scope":"implementation","output-format":"code","related-skills":"fastapi-expert, nestjs-expert, devops-engineer, monitoring-expert, security-reviewer"}},"isInternal":false,"tokens":1497,"sizeBytes":6586},{"name":"gutenberg-blocks.md","path":"skills/wordpress-pro/references/gutenberg-blocks.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/wordpress-pro/references/gutenberg-blocks.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fgutenberg-blocks.md -->","isInternal":false,"tokens":5657,"sizeBytes":26735},{"name":"hooks-filters.md","path":"skills/wordpress-pro/references/hooks-filters.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/wordpress-pro/references/hooks-filters.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fhooks-filters.md -->","isInternal":false,"tokens":5361,"sizeBytes":22161},{"name":"performance-security.md","path":"skills/wordpress-pro/references/performance-security.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/wordpress-pro/references/performance-security.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fperformance-security.md -->","isInternal":false,"tokens":6527,"sizeBytes":26063},{"name":"plugin-architecture.md","path":"skills/wordpress-pro/references/plugin-architecture.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/wordpress-pro/references/plugin-architecture.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Fplugin-architecture.md -->","isInternal":false,"tokens":7130,"sizeBytes":32773},{"name":"theme-development.md","path":"skills/wordpress-pro/references/theme-development.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/wordpress-pro/references/theme-development.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2Freferences%2Ftheme-development.md -->","isInternal":false,"tokens":5767,"sizeBytes":26017},{"name":"SKILL.md","path":"skills/wordpress-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/skills/wordpress-pro/SKILL.md","title":"wordpress-pro","category":"anthropic-skill","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=skills%2Fwordpress-pro%2FSKILL.md -->","frontmatter":{"name":"wordpress-pro","description":"Develops custom WordPress themes and plugins, creates and registers Gutenberg blocks and block patterns, configures WooCommerce stores, implements WordPress REST API endpoints, applies security hardening (nonces, sanitization, escaping, capability checks), and optimizes performance through caching and query tuning. Use when building WordPress themes, writing plugins, customizing Gutenberg blocks, extending WooCommerce, working with ACF, using the WordPress REST API, applying hooks and filters, or improving WordPress performance and security.","license":"MIT","metadata":{"author":"https://github.com/Jeffallan","company":"https://synergetic.solutions","version":"1.1.0","domain":"platform","triggers":"WordPress, WooCommerce, Gutenberg, WordPress theme, WordPress plugin, custom blocks, ACF, WordPress REST API, hooks, filters, WordPress performance, WordPress security","role":"expert","scope":"implementation","output-format":"code","related-skills":"php-pro, laravel-specialist, fullstack-guardian, security-reviewer"}},"isInternal":false,"tokens":1478,"sizeBytes":6454},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/.claude-plugin/marketplace.json","title":"fullstack-dev-skills","category":"marketplace","format":"json","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=.claude-plugin%2Fmarketplace.json -->","frontmatter":{"name":"fullstack-dev-skills","owner":{"name":"jeffallan"},"metadata":{"description":"Comprehensive skill pack for full-stack developers covering frameworks, workflows, and security","version":"0.4.20"},"plugins":[{"name":"fullstack-dev-skills","source":{"source":"github","repo":"jeffallan/claude-skills","ref":"plugin"},"description":"67 specialized skills for full-stack development: 12 language experts (Python, TypeScript, Go, Rust, C++, Swift, Kotlin, C#, PHP, Java, SQL, JavaScript), 8 backend frameworks, 7 frontend/mobile, plus infrastructure, DevOps, security, and testing skills. Includes 9 project workflow commands for epic planning, discovery, execution, and retrospectives.","version":"0.4.20","author":{"name":"jeffallan","email":"jeff@synergetic.solutions","url":"https://github.com/jeffallan"},"homepage":"https://jeffallan.github.io/claude-skills/","repository":"https://github.com/jeffallan/claude-skills","license":"MIT","keywords":["claude-skill","claude-code","fullstack","typescript","python","go","rust","cpp","swift","kotlin","csharp","php","java","sql","dart","react","nextjs","vue","angular","react-native","flutter","nestjs","django","fastapi","spring-boot","laravel","rails","dotnet","kubernetes","terraform","graphql","microservices","debugging","monitoring","architecture","security","code-review","testing","playwright","devops","sre","model-invoked","project-management","epic-planning","jira","confluence","sprint","discovery","retrospectives"],"category":"development","tags":["fullstack","development","frameworks","security","testing","workflows"],"skills":"./skills/"}]},"isInternal":false,"tokens":597,"sizeBytes":2344},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/Jeffallan/claude-skills/HEAD/.claude-plugin/plugin.json","title":"fullstack-dev-skills","category":"plugin-manifest","format":"json","content":"<!-- Skill available on-demand via /api/skills/Jeffallan/claude-skills?path=.claude-plugin%2Fplugin.json -->","frontmatter":{"name":"fullstack-dev-skills","version":"0.4.20","description":"Comprehensive skill pack with 67 specialized skills for full-stack developers: 12 language experts (Python, TypeScript, Go, Rust, C++, Swift, Kotlin, C#, PHP, Java, SQL, JavaScript), 8 backend frameworks, 7 frontend/mobile, plus infrastructure, DevOps, security, and testing. Features progressive disclosure architecture for 50% faster loading.","author":{"name":"jeffallan","email":"jeff@synergetic.solutions","url":"https://github.com/jeffallan"},"homepage":"https://jeffallan.github.io/claude-skills/","documentationUrl":"https://jeffallan.github.io/claude-skills/","supportUrl":"https://github.com/jeffallan/claude-skills/issues","privacyPolicyUrl":"https://jeffallan.github.io/claude-skills/privacy/","license":"MIT","repository":"https://github.com/jeffallan/claude-skills","keywords":["claude-skill","claude-code","fullstack","typescript","python","go","rust","cpp","swift","kotlin","csharp","php","java","sql","dart","react","nextjs","vue","angular","react-native","flutter","nestjs","django","fastapi","spring-boot","laravel","rails","dotnet","kubernetes","terraform","graphql","microservices","debugging","monitoring","architecture","security","code-review","testing","playwright","devops","sre","model-invoked","project-management","epic-planning","jira","confluence","sprint","discovery","retrospectives"],"skills":"./skills/","commands":{"common-ground":{"source":"./commands/common-ground.md"},"project:discovery:approve-synthesis":{"source":"./commands/project/discovery/approve-synthesis.md"},"project:discovery:create-epic-discovery":{"source":"./commands/project/discovery/create-epic-discovery.md"},"project:discovery:synthesize-discovery":{"source":"./commands/project/discovery/synthesize-discovery.md"},"project:execution:complete-ticket":{"source":"./commands/project/execution/complete-ticket.md"},"project:execution:execute-ticket":{"source":"./commands/project/execution/execute-ticket.md"},"project:planning:create-epic-plan":{"source":"./commands/project/planning/create-epic-plan.md"},"project:planning:create-implementation-plan":{"source":"./commands/project/planning/create-implementation-plan.md"},"project:retrospectives:complete-epic":{"source":"./commands/project/retrospectives/complete-epic.md"},"project:retrospectives:complete-sprint":{"source":"./commands/project/retrospectives/complete-sprint.md"}}},"isInternal":false,"tokens":801,"sizeBytes":2923}],"systemPromptSnippet":"<yakaai_skills repo=\"Jeffallan/claude-skills\">\n<!-- File: CLAUDE.md (Tokens: ~3420 | Category: claude-rule) -->\n# Claude Skills Project Configuration\n\n> This file governs Claude's behavior when working on the claude-skills repository.\n\n---\n\n## Skill Authorship Standards\n\nSkills follow the [Agent Skills specification](https://agentskills.io/specification). This section covers project-specific conventions that go beyond the base spec.\n\n### The Description Trap\n\n**Critical:** Never put process steps or workflow sequences in descriptions. When descriptions contain step-by-step instructions, agents follow the brief description instead of reading the full skill content. This defeats the purpose of detailed skills.\n\nBrief capability statements (what it does) and trigger conditions (when to use it) are both appropriate. Process steps (how it works) are not.\n\n**BAD - Process steps in description:**\n```yaml\ndescription: Use for debugging. First investigate root cause, then analyze\npatterns, test hypotheses, and implement fixes with tests.\n```\n\n**GOOD - Capability + trigger:**\n```yaml\ndescription: Diagnoses bugs through root cause analysis and pattern matching.\nUse when encountering errors or unexpected behavior requiring investigation.\n```\n\n**Format:** `[Brief capability statement]. Use when [triggering conditions].`\n\nDescriptions tell WHAT the skill does and WHEN to use it. The SKILL.md body tells HOW.\n\n---\n\n### Frontmatter Requirements\n\nPer the [Agent Skills specification](https://agentskills.io/specification), only `name` and `description` are top-level required fields. Custom fields go under `metadata`.\n\n```yaml\n---\nname: skill-name-with-hyphens\ndescription: [Brief capability statement]. Use when [triggering conditions] - max 1024 chars\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.0.0\"\n  domain: frontend\n  triggers: keyword1, keyword2, keyword3\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: fullstack-guardian, test-master, devops-engineer\n---\n```\n\n**Top-level fields (spec-defined):**\n- `name`: Letters, numbers, and hyphens only (no parentheses or special characters)\n- `description`: Maximum 1024 characters. Capability statement + trigger conditions. No process steps.\n- `license`: Always `MIT` for this project\n- `allowed-tools`: Space-delimited tool list (only on skills that restrict tools)\n\n**Metadata fields (project-specific):**\n- `author`: GitHub profile URL of the skill author\n- `company`: Optional. URL of the author's company (`https://synergetic.solutions` on skills authored by @jeffallan); omit for contributed skills unless the contributor supplies one\n- `version`: Semantic version string (quoted, e.g., `\"1.0.0\"`)\n- `domain`: Category from the domain list below\n- `triggers`: Comma-separated searchable keywords\n- `role`: `specialist` | `expert` | `architect` | `engineer`\n- `scope`: `implementation` | `review` | `design` | `system-design` | `testing` | `analysis` | `infrastructure` | `optimization` | `architecture`\n- `output-format`: `code` | `document` | `report` | `architecture` | `specification` | `schema` | `manifests` | `analysis` | `analysis-and-code`\n- `related-skills`: Comma-separated skill directory names (e.g., `fullstack-guardian, test-master`). Must resolve to existing skill directories.\n\n**Domain values:**\n`language` · `backend` · `frontend` · `infrastructure` · `api-architecture` · `quality` · `devops` · `security` · `data-ml` · `platform` · `specialized` · `workflow`\n\n---\n\n### Reference File Standards\n\nReference files follow the [Agent Skills specification](https://agentskills.io/specification). No specific headers are required.\n\n**Guidelines:**\n- 100-600 lines per reference file\n- Keep files focused on a single topic\n- Complete, working code examples with TypeScript types\n- Cross-reference related skills where relevant\n- Include \"when to use\" and \"when not to use\" guidance\n- Practical patterns over theoretical explanations\n\n### Framework Idiom Principle\n\nReference files for framework-specific skills must reflect the idiomatic best practices of that framework, not generic patterns applied uniformly across all skills. If a framework provides a built-in mechanism (e.g., global error handling, middleware, dependency injection), reference examples should use it rather than duplicating that behavior manually. Each framework's conventions for error handling, architecture, and code organization take precedence over cross-project consistency.\n\n---\n\n### Documentation Backlink\n\nEvery `SKILL.md` MUST end with a single canonical Documentation link pointing back to the docs site:\n\n```\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- `{domain}` is `metadata.domain` from the frontmatter and must be one of the `domains` in `pyproject.toml` (`[tool.skill-linter]`); the docs site build fails on any other value.\n- `{skill-name}` is the skill's directory name.\n- Format is exactly markdown link syntax (`[text](url)`), not a bare URL. Strict CommonMark renderers do not auto-link bare URLs, and the line's purpose is to render as a real `<a href>` on aggregators (skills.sh, etc.) for SEO backlinks.\n\n**Why two surfaces, two behaviors:**\n\n- Aggregators consume raw `SKILL.md` from GitHub and render the line as a hyperlink. This is the SEO point.\n- The docs site itself would render this line as a self-link, which is redundant. The site's content sync (`site/src/components/site-sync/`, configured by `site/scripts/sync-content.mjs`) strips the line, and the credit line above it, at build time so the docs site, public markdown mirrors, `llms.txt`, and `llms-full.txt` never show it.\n\n**When adding or renaming a skill:** update both the directory name (which becomes `{skill-name}`) and the `metadata.domain` consistently with this URL formula, otherwise the backlink will 404.\n\n### Maintainer Credit\n\nSkills authored by @jeffallan (those with `metadata.company: https://synergetic.solutions`) carry one more line, directly above the Documentation backlink, separated by a blank line. It matches the docs site footer credit:\n\n```\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/{domain}/{skill-name}/)\n```\n\n- The Documentation backlink stays the last line\n- Contributed skills do not get this line unless their `metadata.company` is set to the same URL\n- `syncSkillPages` strips it at build time alongside the Documentation backlink, so the docs site, markdown mirrors, and `llms` files never show it (the site footer shows the same credit once per page)\n\n---\n\n### Progressive Disclosure Architecture\n\n**Tier 1 - SKILL.md (~80-100 lines)**\n- Role definition and expertise level\n- When-to-use guidance (triggers)\n- Core workflow (5 steps)\n- Constraints (MUST DO / MUST NOT DO)\n- Routing table to references\n\n**Tier 2 - Reference Files (100-600 lines each)**\n- Deep technical content\n- Complete code examples\n- Edge cases and anti-patterns\n- Loaded only when context requires\n\n**Goal:** 50% token reduction through selective loading.\n\n---\n\n## Project Workflow\n\n### When Creating New Skills\n\n1. Check existing skills for overlap\n2. Write SKILL.md with capability + trigger description (no process steps)\n3. Create reference files for deep content (100+ lines)\n4. Add routing table linking topics to references\n5. Append the canonical Documentation backlink as the last line (see Documentation Backlink above)\n6. Test skill triggers with realistic prompts\n7. Add the skill to SKILLS_GUIDE.md in its category\n\n### When Modifying Skills\n\n1. Read the full current skill before editing\n2. Maintain capability + trigger description format (no process steps)\n3. Preserve progressive disclosure structure\n4. Update related cross-references\n5. Verify routing table accuracy\n\n### When Accepting Contributions\n\nEvery external contribution (issue or PR) gets the `gratitude` label, whatever its outcome.\n\nFor each PR:\n\n1. Discuss fit with project goals before any merge decision (scope, overlap with existing skills, promotional content), then verify the content\n2. Squash-merge with subject `type(scope): title (#N)`\n   - Conflicted fork PRs: squash locally (`git merge --squash`), add `Co-authored-by:` with the contributor's commit email, then close the PR with a comment pointing at the commit\n3. Record it in a separate commit, `docs(changelog): record #N <summary> under Unreleased`, adding an entry under `[Unreleased]` → Added/Changed/Fixed that ends with `(#N)` (append `closing #ISSUE` when it resolves one)\n4. If skill or reference counts changed, run `uvx skill-linter@0.2.0 stamp` in the same push, otherwise `stamp --check` fails CI on main\n5. Post-merge corrections go in their own commit with their own `### Changed` entry\n\nAt release time, credit contributors (including issue reporters) in a `### Contributors` section of the version entry:\n\n```markdown\n### Contributors\n- @handle: What they contributed (#N)\n```\n\n---\n\n## Shared Code from the Handbook\n\nThe site code under `site/src/components/site-*/`, the shared CI workflows and `.github/workflows/handbook-update.yml` come from the private handbook through Copier (`.copier-answers.yml`). Change them in the handbook, not here.\n\nThe versions of the shared site packages come from the handbook too (`personal-sites/package.json`): `astro`, `@astrojs/starlight`, `@jdevalk/astro-seo-graph`, `astro-og-canvas`, `starlight-links-validator`, the `@fontsource` fonts, `js-yaml`, `smol-toml`, `sharp`, `prettier` and `prettier-plugin-astro`. Don't bump them in `site/package.json`. Bump them in the handbook. **They reach this repo only with a handbook release**, as the update PR that `handbook-update.yml` opens, carrying the new `site/package.json` and lockfile.\n\nThe same goes for the actions in the workflows the handbook delivers (`handbook-update.yml`, `docs-site.yml`, `docs-deploy.yml`). Dependabot here skips those files (`exclude-paths`), and the handbook bumps them.\n\n## Release Checklist\n\nWhen releasing a new version, follow these steps.\n\n### 1. Update Version and Counts\n\nVersion and counts are managed through `version.json`:\n\n```json\n{\n  \"version\": \"0.4.2\",\n  \"skillCount\": 65,\n  \"workflowCount\": 9,\n  \"referenceFileCount\": 355\n}\n```\n\n**To release a new version:**\n\n1. Update the `version` field in `version.json`\n2. Stamp the counts and version:\n\n```bash\nuvx skill-linter@0.2.0 stamp\n```\n\nThis computes the counts from the filesystem (skills, per-domain skills, references, workflows), writes them to `version.json`, and stamps the version and counts into the files listed under `[[tool.skill-linter.stamp.files]]` in `pyproject.toml` (README.md, QUICKSTART.md, plugin.json, marketplace.json, the docs site and others).\n\n`uvx skill-linter@0.2.0 stamp --check` reports stale files without writing (CI runs it).\n\n### 2. Update CHANGELOG.md\n\nAdd new version entry at the top following Keep a Changelog format:\n\n```markdown\n## [X.Y.Z] - YYYY-MM-DD\n\n### Added\n- New features, skills, commands\n\n### Changed\n- Modified functionality, updated skills\n\n### Fixed\n- Bug fixes\n```\n\nAdd version comparison link at bottom:\n```markdown\n[X.Y.Z]: https://github.com/jeffallan/claude-skills/compare/vPREVIOUS...vX.Y.Z\n```\n\n### 3. Update Documentation for New/Modified Content\n\n**For new skills:**\n- Add to `SKILLS_GUIDE.md` in appropriate category\n- Add to decision trees if applicable\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For new commands:**\n- Add to `docs/WORKFLOW_COMMANDS.md`\n- Run `uvx skill-linter@0.2.0 stamp` to update counts\n\n**For modified skills/commands:**\n- Update any cross-references\n- Update SKILLS_GUIDE.md if triggers changed\n\n### 4. Generate Social Preview (conditional)\n\nThe social preview embeds skill, workflow, and reference counts but **not** the version. Only regenerate when at least one of `skillCount`, `workflowCount`, or `referenceFileCount` in `version.json` changed in this release — patch releases that only fix docs or a single skill normally do not need it.\n\nWhen regeneration is needed:\n\n```bash\nnpx -y -p puppeteer node ./assets/capture-screenshot.js\n```\n\nThis creates `assets/social-preview.png` from `assets/social-preview.html`.\n\n### 5. Validate\n\n**Critical:** Run validation before release to prevent broken skills from being published.\n\n```bash\nmake validate\n```\n\nThis runs [skill-linter](https://pypi.org/project/skill-linter/), configured in `pyproject.toml` (`[tool.skill-linter]`), and is the same check CI runs:\n\n- `skill-linter release` checks the shipped plugin tree (manifest, commands map, marketplace entry), then runs every other rule: frontmatter, names and descriptions, sections and references, Markdown syntax (unclosed fences, table separators, comments and column counts), commands, the changelog and stamped counts\n- `skill-linter stamp --check` fails when a stamped count or version is stale\n\n**Options:**\n```bash\nuvx skill-linter@0.2.0 check --skill react-expert   # One skill's rules only\nuvx skill-linter@0.2.0 check --only markdown        # One rule group\nuvx skill-linter@0.2.0 check --format json          # JSON output\nuvx skill-linter@0.2.0 rules                        # Every rule, its group and severity\n```\n\n**Exit codes:** 0 = no errors (warnings OK), 1 = errors found\n\n### 6. Final Verification\n\nAfter running validation, manually verify:\n\n```bash\n# Check no old version references remain (except historical changelog)\ngrep -r \"OLD_VERSION\" --include=\"*.md\" --include=\"*.json\" --include=\"*.html\"\n```\n\n### 7. Plugin Branch\n\nThe plugin ships from the `plugin` branch, not from `main`. Pushing the `vX.Y.Z` tag runs `.github/workflows/publish-plugin.yml`, which rebuilds that branch from `scripts/build-plugin-dist.sh` (only `.claude-plugin/plugin.json`, `skills/`, `commands/`, `references/`, `README.md`, `LICENSE`). Both the marketplace entry and the Claude plugin directory read from it.\n\n- Never edit the `plugin` branch by hand; the next publish overwrites it\n- After the tag push, confirm the workflow succeeded and the branch's latest commit names the new tag\n- A file the plugin needs at runtime must live under one of the shipped paths, or it will be missing from installs\n\n---\n\n## Attribution\n\nBehavioral patterns and process discipline adapted from:\n- **[obra/superpowers](https://github.com/obra/superpowers)** by Jesse Vincent (@obra)\n- License: MIT\n\nResearch documented in: `research/superpowers.md`\n\n<!-- File: skills/angular-architect/references/components.md (Tokens: ~1568 | Category: anthropic-skill) -->\n# Standalone Components & Signals\n\n## Standalone Component Pattern\n\n```typescript\nimport { Component, signal, computed, effect } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n@Component({\n  selector: 'app-user-profile',\n  standalone: true,\n  imports: [CommonModule, FormsModule],\n  templateUrl: './user-profile.component.html',\n  styleUrl: './user-profile.component.scss',\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserProfileComponent {\n  // Signal-based state\n  count = signal(0);\n  doubleCount = computed(() => this.count() * 2);\n\n  constructor() {\n    // Side effects\n    effect(() => {\n      console.log(`Count is: ${this.count()}`);\n    });\n  }\n\n  increment() {\n    this.count.update(value => value + 1);\n  }\n}\n```\n\n## Input/Output with Signals\n\n```typescript\nimport { Component, input, output, model } from '@angular/core';\n\n@Component({\n  selector: 'app-search-box',\n  standalone: true,\n  template: `\n    <input\n      [value]=\"query()\"\n      (input)=\"onQueryChange($event)\"\n      [placeholder]=\"placeholder()\" />\n  `\n})\nexport class SearchBoxComponent {\n  // Signal inputs (Angular 17.1+)\n  placeholder = input<string>('Search...');\n  initialQuery = input<string>('');\n\n  // Signal outputs\n  queryChange = output<string>();\n\n  // Two-way binding with model signal\n  query = model<string>('');\n\n  onQueryChange(event: Event) {\n    const value = (event.target as HTMLInputElement).value;\n    this.query.set(value);\n    this.queryChange.emit(value);\n  }\n}\n\n// Parent usage\n@Component({\n  template: `\n    <app-search-box\n      [(query)]=\"searchQuery\"\n      [placeholder]=\"'Find users...'\"\n      (queryChange)=\"onSearch($event)\" />\n  `\n})\nexport class ParentComponent {\n  searchQuery = signal('');\n\n  onSearch(query: string) {\n    console.log('Searching:', query);\n  }\n}\n```\n\n## Smart vs Dumb Components\n\n```typescript\n// Smart Component (Container)\n@Component({\n  selector: 'app-users-container',\n  standalone: true,\n  imports: [UserListComponent],\n  template: `\n    <app-user-list\n      [users]=\"users()\"\n      [loading]=\"loading()\"\n      (userSelected)=\"onUserSelected($event)\" />\n  `\n})\nexport class UsersContainerComponent {\n  private usersService = inject(UsersService);\n\n  users = signal<User[]>([]);\n  loading = signal(true);\n\n  constructor() {\n    effect(() => {\n      this.usersService.getUsers().subscribe({\n        next: users => {\n          this.users.set(users);\n          this.loading.set(false);\n        },\n        error: err => console.error(err)\n      });\n    });\n  }\n\n  onUserSelected(user: User) {\n    // Handle business logic\n  }\n}\n\n// Dumb Component (Presentational)\n@Component({\n  selector: 'app-user-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div (click)=\"userSelected.emit(user)\">\n          {{ user.name }}\n        </div>\n      }\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class UserListComponent {\n  users = input.required<User[]>();\n  loading = input<boolean>(false);\n  userSelected = output<User>();\n}\n```\n\n## Content Projection\n\n```typescript\n// Card component with multiple slots\n@Component({\n  selector: 'app-card',\n  standalone: true,\n  template: `\n    <div class=\"card\">\n      <div class=\"card-header\">\n        <ng-content select=\"[header]\"></ng-content>\n      </div>\n      <div class=\"card-body\">\n        <ng-content></ng-content>\n      </div>\n      <div class=\"card-footer\">\n        <ng-content select=\"[footer]\"></ng-content>\n      </div>\n    </div>\n  `\n})\nexport class CardComponent {}\n\n// Usage\n@Component({\n  template: `\n    <app-card>\n      <h2 header>Card Title</h2>\n      <p>Card content goes here</p>\n      <button footer>Action</button>\n    </app-card>\n  `\n})\nexport class ParentComponent {}\n```\n\n## Dependency Injection\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { UserService } from './user.service';\n\n@Component({\n  selector: 'app-user-dashboard',\n  standalone: true\n})\nexport class UserDashboardComponent {\n  // Modern inject() API\n  private userService = inject(UserService);\n  private router = inject(Router);\n\n  // Optional dependency\n  private logger = inject(LoggerService, { optional: true });\n\n  users = signal<User[]>([]);\n\n  ngOnInit() {\n    this.loadUsers();\n  }\n\n  loadUsers() {\n    this.userService.getUsers().subscribe({\n      next: users => this.users.set(users),\n      error: err => this.logger?.error('Failed to load users', err)\n    });\n  }\n}\n```\n\n## New Control Flow (@if, @for)\n\n```typescript\n@Component({\n  template: `\n    <!-- @if instead of *ngIf -->\n    @if (user(); as currentUser) {\n      <div>Hello, {{ currentUser.name }}</div>\n    } @else if (loading()) {\n      <div>Loading...</div>\n    } @else {\n      <div>Please log in</div>\n    }\n\n    <!-- @for instead of *ngFor -->\n    @for (item of items(); track item.id) {\n      <div>{{ item.name }}</div>\n    } @empty {\n      <div>No items found</div>\n    }\n\n    <!-- @switch instead of *ngSwitch -->\n    @switch (status()) {\n      @case ('pending') {\n        <span>Pending...</span>\n      }\n      @case ('success') {\n        <span>Success!</span>\n      }\n      @default {\n        <span>Unknown</span>\n      }\n    }\n  `\n})\nexport class ModernControlFlowComponent {\n  user = signal<User | null>(null);\n  loading = signal(false);\n  items = signal<Item[]>([]);\n  status = signal<'pending' | 'success' | 'error'>('pending');\n}\n```\n\n## Performance: OnPush & TrackBy\n\n```typescript\n@Component({\n  selector: 'app-product-list',\n  standalone: true,\n  imports: [CommonModule],\n  template: `\n    @for (product of products(); track trackByProductId($index, product)) {\n      <app-product-card [product]=\"product\" />\n    }\n  `,\n  changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ProductListComponent {\n  products = input.required<Product[]>();\n\n  // TrackBy for optimal rendering\n  trackByProductId(index: number, product: Product): number {\n    return product.id;\n  }\n}\n```\n\n## Quick Reference\n\n| Pattern | Angular 17+ Approach |\n|---------|---------------------|\n| Component | Standalone by default |\n| State | Signals (`signal()`, `computed()`) |\n| Input | `input()`, `input.required()` |\n| Output | `output<T>()` |\n| Two-way | `model<T>()` |\n| DI | `inject()` function |\n| Control Flow | `@if`, `@for`, `@switch` |\n| Change Detection | `ChangeDetectionStrategy.OnPush` |\n\n<!-- File: skills/angular-architect/references/ngrx.md (Tokens: ~2247 | Category: anthropic-skill) -->\n# NgRx State Management\n\n## Store Setup\n\n```typescript\n// app.config.ts\nimport { provideStore } from '@ngrx/store';\nimport { provideEffects } from '@ngrx/effects';\nimport { provideStoreDevtools } from '@ngrx/store-devtools';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideStore({\n      users: usersReducer,\n      products: productsReducer\n    }),\n    provideEffects([UsersEffects, ProductsEffects]),\n    provideStoreDevtools({\n      maxAge: 25,\n      logOnly: !isDevMode()\n    })\n  ]\n};\n```\n\n## Actions (Modern)\n\n```typescript\n// users.actions.ts\nimport { createActionGroup, emptyProps, props } from '@ngrx/store';\nimport { User } from './user.model';\n\nexport const UsersActions = createActionGroup({\n  source: 'Users',\n  events: {\n    'Load Users': emptyProps(),\n    'Load Users Success': props<{ users: User[] }>(),\n    'Load Users Failure': props<{ error: string }>(),\n\n    'Add User': props<{ user: User }>(),\n    'Add User Success': props<{ user: User }>(),\n    'Add User Failure': props<{ error: string }>(),\n\n    'Update User': props<{ id: string; changes: Partial<User> }>(),\n    'Update User Success': props<{ user: User }>(),\n\n    'Delete User': props<{ id: string }>(),\n    'Delete User Success': props<{ id: string }>()\n  }\n});\n```\n\n## Reducer with Entity Adapter\n\n```typescript\n// users.reducer.ts\nimport { createReducer, on } from '@ngrx/store';\nimport { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';\nimport { UsersActions } from './users.actions';\nimport { User } from './user.model';\n\nexport interface UsersState extends EntityState<User> {\n  loading: boolean;\n  error: string | null;\n  selectedUserId: string | null;\n}\n\nexport const usersAdapter: EntityAdapter<User> = createEntityAdapter<User>({\n  selectId: (user: User) => user.id,\n  sortComparer: (a, b) => a.name.localeCompare(b.name)\n});\n\nconst initialState: UsersState = usersAdapter.getInitialState({\n  loading: false,\n  error: null,\n  selectedUserId: null\n});\n\nexport const usersReducer = createReducer(\n  initialState,\n\n  // Load users\n  on(UsersActions.loadUsers, (state) => ({\n    ...state,\n    loading: true,\n    error: null\n  })),\n\n  on(UsersActions.loadUsersSuccess, (state, { users }) =>\n    usersAdapter.setAll(users, {\n      ...state,\n      loading: false\n    })\n  ),\n\n  on(UsersActions.loadUsersFailure, (state, { error }) => ({\n    ...state,\n    loading: false,\n    error\n  })),\n\n  // Add user\n  on(UsersActions.addUserSuccess, (state, { user }) =>\n    usersAdapter.addOne(user, state)\n  ),\n\n  // Update user\n  on(UsersActions.updateUserSuccess, (state, { user }) =>\n    usersAdapter.updateOne(\n      { id: user.id, changes: user },\n      state\n    )\n  ),\n\n  // Delete user\n  on(UsersActions.deleteUserSuccess, (state, { id }) =>\n    usersAdapter.removeOne(id, state)\n  )\n);\n```\n\n## Selectors\n\n```typescript\n// users.selectors.ts\nimport { createFeatureSelector, createSelector } from '@ngrx/store';\nimport { usersAdapter, UsersState } from './users.reducer';\n\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\n\n// Entity adapter selectors\nconst {\n  selectIds,\n  selectEntities,\n  selectAll,\n  selectTotal\n} = usersAdapter.getSelectors();\n\nexport const selectUserIds = createSelector(\n  selectUsersState,\n  selectIds\n);\n\nexport const selectUserEntities = createSelector(\n  selectUsersState,\n  selectEntities\n);\n\nexport const selectAllUsers = createSelector(\n  selectUsersState,\n  selectAll\n);\n\nexport const selectUsersTotal = createSelector(\n  selectUsersState,\n  selectTotal\n);\n\nexport const selectUsersLoading = createSelector(\n  selectUsersState,\n  (state) => state.loading\n);\n\nexport const selectUsersError = createSelector(\n  selectUsersState,\n  (state) => state.error\n);\n\n// Parameterized selector\nexport const selectUserById = (id: string) =>\n  createSelector(\n    selectUserEntities,\n    (entities) => entities[id]\n  );\n\n// Composed selector\nexport const selectActiveUsers = createSelector(\n  selectAllUsers,\n  (users) => users.filter(user => user.isActive)\n);\n\n// Selector with multiple inputs\nexport const selectUserWithPosts = createSelector(\n  selectUserById,\n  selectAllPosts,\n  (user, posts) => ({\n    user,\n    posts: posts.filter(post => post.userId === user?.id)\n  })\n);\n```\n\n## Effects\n\n```typescript\n// users.effects.ts\nimport { Injectable, inject } from '@angular/core';\nimport { Actions, createEffect, ofType } from '@ngrx/effects';\nimport { catchError, map, mergeMap, exhaustMap } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\n\n@Injectable()\nexport class UsersEffects {\n  private actions$ = inject(Actions);\n  private usersService = inject(UsersService);\n\n  // Load users effect\n  loadUsers$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.loadUsers),\n      mergeMap(() =>\n        this.usersService.getAll().pipe(\n          map(users => UsersActions.loadUsersSuccess({ users })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Add user effect (exhaustMap prevents duplicate submits)\n  addUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.addUser),\n      exhaustMap(({ user }) =>\n        this.usersService.create(user).pipe(\n          map(createdUser => UsersActions.addUserSuccess({ user: createdUser })),\n          catchError(error =>\n            of(UsersActions.addUserFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Update user effect\n  updateUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.updateUser),\n      mergeMap(({ id, changes }) =>\n        this.usersService.update(id, changes).pipe(\n          map(user => UsersActions.updateUserSuccess({ user })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Delete user effect\n  deleteUser$ = createEffect(() =>\n    this.actions$.pipe(\n      ofType(UsersActions.deleteUser),\n      mergeMap(({ id }) =>\n        this.usersService.delete(id).pipe(\n          map(() => UsersActions.deleteUserSuccess({ id })),\n          catchError(error =>\n            of(UsersActions.loadUsersFailure({ error: error.message }))\n          )\n        )\n      )\n    )\n  );\n\n  // Non-dispatching effect (side effect only)\n  logUserActions$ = createEffect(\n    () =>\n      this.actions$.pipe(\n        ofType(\n          UsersActions.addUserSuccess,\n          UsersActions.updateUserSuccess,\n          UsersActions.deleteUserSuccess\n        ),\n        tap(action => console.log('User action:', action))\n      ),\n    { dispatch: false }\n  );\n}\n```\n\n## Component Integration\n\n```typescript\n// users-list.component.ts\nimport { Component, inject } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { UsersActions } from './store/users.actions';\nimport {\n  selectAllUsers,\n  selectUsersLoading,\n  selectUsersError\n} from './store/users.selectors';\n\n@Component({\n  selector: 'app-users-list',\n  standalone: true,\n  template: `\n    @if (loading()) {\n      <div>Loading...</div>\n    } @else if (error(); as err) {\n      <div>Error: {{ err }}</div>\n    } @else {\n      @for (user of users(); track user.id) {\n        <div>\n          {{ user.name }}\n          <button (click)=\"onDelete(user.id)\">Delete</button>\n        </div>\n      }\n    }\n  `\n})\nexport class UsersListComponent {\n  private store = inject(Store);\n\n  // Select data as signals\n  users = toSignal(this.store.select(selectAllUsers), { initialValue: [] });\n  loading = toSignal(this.store.select(selectUsersLoading), { initialValue: false });\n  error = toSignal(this.store.select(selectUsersError), { initialValue: null });\n\n  ngOnInit() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  onDelete(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n}\n```\n\n## Facade Pattern\n\n```typescript\n// users.facade.ts\n@Injectable({ providedIn: 'root' })\nexport class UsersFacade {\n  private store = inject(Store);\n\n  // Selectors\n  users$ = this.store.select(selectAllUsers);\n  loading$ = this.store.select(selectUsersLoading);\n  error$ = this.store.select(selectUsersError);\n\n  // Actions\n  loadUsers() {\n    this.store.dispatch(UsersActions.loadUsers());\n  }\n\n  addUser(user: User) {\n    this.store.dispatch(UsersActions.addUser({ user }));\n  }\n\n  updateUser(id: string, changes: Partial<User>) {\n    this.store.dispatch(UsersActions.updateUser({ id, changes }));\n  }\n\n  deleteUser(id: string) {\n    this.store.dispatch(UsersActions.deleteUser({ id }));\n  }\n\n  getUserById(id: string) {\n    return this.store.select(selectUserById(id));\n  }\n}\n\n// Usage in component\n@Component({\n  selector: 'app-users',\n  standalone: true\n})\nexport class UsersComponent {\n  private facade = inject(UsersFacade);\n\n  users = toSignal(this.facade.users$, { initialValue: [] });\n  loading = toSignal(this.facade.loading$, { initialValue: false });\n\n  ngOnInit() {\n    this.facade.loadUsers();\n  }\n\n  onAdd(user: User) {\n    this.facade.addUser(user);\n  }\n}\n```\n\n## Quick Reference\n\n| Concept | Usage |\n|---------|-------|\n| Actions | `createActionGroup()` |\n| Reducer | `createReducer()`, `on()` |\n| Entity | `createEntityAdapter()` |\n| Selectors | `createSelector()`, `createFeatureSelector()` |\n| Effects | `createEffect()`, `ofType()` |\n| Store | `inject(Store)`, `store.select()`, `store.dispatch()` |\n| DevTools | `provideStoreDevtools()` |\n| Testing | Mock store, marble testing |\n\n<!-- File: skills/angular-architect/references/routing.md (Tokens: ~2050 | Category: anthropic-skill) -->\n# Angular Routing\n\n## Routes Configuration\n\n```typescript\n// app.routes.ts\nimport { Routes } from '@angular/router';\nimport { HomeComponent } from './home/home.component';\n\nexport const routes: Routes = [\n  {\n    path: '',\n    redirectTo: '/home',\n    pathMatch: 'full'\n  },\n  {\n    path: 'home',\n    component: HomeComponent,\n    title: 'Home'\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./users/users.component').then(m => m.UsersComponent),\n    title: 'Users'\n  },\n  {\n    path: 'users/:id',\n    loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent),\n    canActivate: [authGuard],\n    resolve: { user: userResolver }\n  },\n  {\n    path: 'admin',\n    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),\n    canActivate: [authGuard, adminGuard]\n  },\n  {\n    path: '**',\n    loadComponent: () => import('./not-found/not-found.component').then(m => m.NotFoundComponent),\n    title: '404 Not Found'\n  }\n];\n\n// app.config.ts\nimport { provideRouter, withComponentInputBinding } from '@angular/router';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideRouter(\n      routes,\n      withComponentInputBinding(),  // Bind route params to @Input()\n      withViewTransitions(),        // Enable view transitions\n      withPreloading(PreloadAllModules)\n    )\n  ]\n};\n```\n\n## Lazy Loading\n\n```typescript\n// Feature routes\n// admin/admin.routes.ts\nimport { Routes } from '@angular/router';\n\nexport const ADMIN_ROUTES: Routes = [\n  {\n    path: '',\n    loadComponent: () => import('./admin-dashboard.component').then(m => m.AdminDashboardComponent)\n  },\n  {\n    path: 'users',\n    loadComponent: () => import('./admin-users.component').then(m => m.AdminUsersComponent)\n  },\n  {\n    path: 'settings',\n    loadComponent: () => import('./admin-settings.component').then(m => m.AdminSettingsComponent)\n  }\n];\n```\n\n## Functional Guards\n\n```typescript\n// guards/auth.guard.ts\nimport { inject } from '@angular/core';\nimport { Router, CanActivateFn } from '@angular/router';\nimport { AuthService } from '../services/auth.service';\n\nexport const authGuard: CanActivateFn = (route, state) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.isAuthenticated()) {\n    return true;\n  }\n\n  // Redirect to login with return URL\n  return router.createUrlTree(['/login'], {\n    queryParams: { returnUrl: state.url }\n  });\n};\n\n// Admin guard\nexport const adminGuard: CanActivateFn = () => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n\n  if (authService.hasRole('admin')) {\n    return true;\n  }\n\n  return router.createUrlTree(['/unauthorized']);\n};\n\n// Can deactivate (unsaved changes)\nexport const canDeactivateGuard: CanDeactivateFn<FormComponent> = (component) => {\n  if (component.hasUnsavedChanges()) {\n    return confirm('You have unsaved changes. Are you sure you want to leave?');\n  }\n  return true;\n};\n```\n\n## Resolvers\n\n```typescript\n// resolvers/user.resolver.ts\nimport { inject } from '@angular/core';\nimport { ResolveFn } from '@angular/router';\nimport { catchError, of } from 'rxjs';\nimport { User } from '../models/user.model';\nimport { UsersService } from '../services/users.service';\n\nexport const userResolver: ResolveFn<User | null> = (route, state) => {\n  const usersService = inject(UsersService);\n  const id = route.paramMap.get('id')!;\n\n  return usersService.getById(id).pipe(\n    catchError(() => of(null))\n  );\n};\n\n// Component receives resolved data\n@Component({\n  selector: 'app-user-detail',\n  standalone: true,\n  template: `\n    @if (user) {\n      <h1>{{ user.name }}</h1>\n    } @else {\n      <p>User not found</p>\n    }\n  `\n})\nexport class UserDetailComponent {\n  user = input<User | null>(null);  // Resolved data bound as input\n}\n```\n\n## Route Parameters\n\n```typescript\nimport { Component, inject, input } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n@Component({\n  selector: 'app-product-detail',\n  standalone: true\n})\nexport class ProductDetailComponent {\n  private route = inject(ActivatedRoute);\n  private router = inject(Router);\n\n  // Modern approach: route params as inputs\n  id = input.required<string>();\n\n  // Legacy approach: subscribe to params\n  ngOnInit() {\n    this.route.paramMap.subscribe(params => {\n      const id = params.get('id');\n      this.loadProduct(id);\n    });\n\n    // Query params\n    this.route.queryParamMap.subscribe(params => {\n      const filter = params.get('filter');\n      const sort = params.get('sort');\n    });\n  }\n\n  // Navigate programmatically\n  goToEdit() {\n    this.router.navigate(['/products', this.id(), 'edit']);\n  }\n\n  // Navigate with query params\n  applyFilter(filter: string) {\n    this.router.navigate([], {\n      relativeTo: this.route,\n      queryParams: { filter },\n      queryParamsHandling: 'merge'  // Preserve other params\n    });\n  }\n}\n```\n\n## Router Events\n\n```typescript\nimport { Component, inject } from '@angular/core';\nimport { Router, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\n@Component({\n  selector: 'app-root',\n  standalone: true\n})\nexport class AppComponent {\n  private router = inject(Router);\n  loading = signal(false);\n\n  constructor() {\n    // Show loading on navigation start\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationStart)\n    ).subscribe(() => {\n      this.loading.set(true);\n    });\n\n    // Hide loading on navigation end\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationEnd)\n    ).subscribe(() => {\n      this.loading.set(false);\n    });\n\n    // Handle navigation errors\n    this.router.events.pipe(\n      filter(event => event instanceof NavigationError)\n    ).subscribe((event: NavigationError) => {\n      console.error('Navigation error:', event.error);\n      this.loading.set(false);\n    });\n  }\n}\n```\n\n## Child Routes & Outlets\n\n```typescript\n// Parent route with child routes\nconst routes: Routes = [\n  {\n    path: 'dashboard',\n    component: DashboardComponent,\n    children: [\n      {\n        path: 'stats',\n        component: StatsComponent,\n        outlet: 'panel'  // Named outlet\n      },\n      {\n        path: 'charts',\n        component: ChartsComponent,\n        outlet: 'panel'\n      }\n    ]\n  }\n];\n\n// Dashboard component template\n@Component({\n  template: `\n    <div class=\"dashboard\">\n      <div class=\"main\">\n        <router-outlet></router-outlet>  <!-- Primary outlet -->\n      </div>\n      <div class=\"panel\">\n        <router-outlet name=\"panel\"></router-outlet>  <!-- Named outlet -->\n      </div>\n    </div>\n  `\n})\nexport class DashboardComponent {}\n\n// Navigate to named outlet\nthis.router.navigate(['/dashboard', { outlets: { panel: ['stats'] } }]);\n```\n\n## Preloading Strategies\n\n```typescript\n// Custom preloading strategy\nimport { Injectable } from '@angular/core';\nimport { PreloadingStrategy, Route } from '@angular/router';\nimport { Observable, of, timer } from 'rxjs';\nimport { mergeMap } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class CustomPreloadingStrategy implements PreloadingStrategy {\n  preload(route: Route, load: () => Observable<any>): Observable<any> {\n    // Only preload routes with data.preload = true\n    if (route.data?.['preload']) {\n      const delay = route.data?.['preloadDelay'] || 0;\n      return timer(delay).pipe(\n        mergeMap(() => load())\n      );\n    }\n    return of(null);\n  }\n}\n\n// Route config with preload data\nconst routes: Routes = [\n  {\n    path: 'important',\n    loadChildren: () => import('./important/important.routes'),\n    data: { preload: true, preloadDelay: 2000 }\n  }\n];\n\n// Register in app config\nprovideRouter(routes, withPreloading(CustomPreloadingStrategy))\n```\n\n## Route Guards with Observables\n\n```typescript\nexport const dataGuard: CanActivateFn = (route, state) => {\n  const dataService = inject(DataService);\n  const router = inject(Router);\n\n  return dataService.checkAccess(route.params['id']).pipe(\n    map(hasAccess => {\n      if (hasAccess) {\n        return true;\n      }\n      return router.createUrlTree(['/no-access']);\n    }),\n    catchError(() => {\n      return of(router.createUrlTree(['/error']));\n    })\n  );\n};\n```\n\n## Quick Reference\n\n| Feature | Usage |\n|---------|-------|\n| Routes | `Routes` array in app.routes.ts |\n| Lazy load | `loadComponent()`, `loadChildren()` |\n| Guards | `CanActivateFn`, `CanDeactivateFn` |\n| Resolvers | `ResolveFn<T>` |\n| Params | `route.paramMap`, `input<T>()` |\n| Query | `route.queryParamMap` |\n| Navigate | `router.navigate()`, `routerLink` |\n| Events | `router.events` |\n| Outlets | `<router-outlet name=\"...\">` |\n| Preload | `withPreloading()` |\n\n<!-- File: skills/angular-architect/references/rxjs.md (Tokens: ~1918 | Category: anthropic-skill) -->\n# RxJS Patterns\n\n## Essential Operators\n\n```typescript\nimport { Component, inject, signal } from '@angular/core';\nimport {\n  map, filter, switchMap, catchError,\n  debounceTime, distinctUntilChanged,\n  tap, shareReplay, takeUntil\n} from 'rxjs/operators';\nimport { Subject, of, EMPTY } from 'rxjs';\n\n@Component({\n  selector: 'app-search',\n  standalone: true\n})\nexport class SearchComponent {\n  private searchService = inject(SearchService);\n  private destroy$ = new Subject<void>();\n\n  searchTerm$ = new Subject<string>();\n  results = signal<SearchResult[]>([]);\n\n  ngOnInit() {\n    this.searchTerm$.pipe(\n      debounceTime(300),              // Wait 300ms after typing\n      distinctUntilChanged(),         // Only if value changed\n      filter(term => term.length > 2), // Minimum 3 characters\n      tap(() => this.loading.set(true)),\n      switchMap(term =>               // Cancel previous requests\n        this.searchService.search(term).pipe(\n          catchError(err => {\n            console.error(err);\n            return of([]);            // Return empty on error\n          })\n        )\n      ),\n      tap(() => this.loading.set(false)),\n      takeUntil(this.destroy$)        // Auto-unsubscribe\n    ).subscribe(results => this.results.set(results));\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Subject Types\n\n```typescript\nimport { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';\n\nexport class SubjectExamples {\n  // Subject: No initial value, only emits to future subscribers\n  private clickSubject = new Subject<MouseEvent>();\n  click$ = this.clickSubject.asObservable();\n\n  onClick(event: MouseEvent) {\n    this.clickSubject.next(event);\n  }\n\n  // BehaviorSubject: Has initial value, emits latest value to new subscribers\n  private loadingSubject = new BehaviorSubject<boolean>(false);\n  loading$ = this.loadingSubject.asObservable();\n\n  setLoading(loading: boolean) {\n    this.loadingSubject.next(loading);\n  }\n\n  // ReplaySubject: Replays N previous values to new subscribers\n  private activitySubject = new ReplaySubject<Activity>(3); // Last 3 activities\n  activity$ = this.activitySubject.asObservable();\n\n  // AsyncSubject: Only emits last value when completed\n  private finalResultSubject = new AsyncSubject<Result>();\n  finalResult$ = this.finalResultSubject.asObservable();\n}\n```\n\n## Higher-Order Operators\n\n```typescript\nimport { switchMap, mergeMap, concatMap, exhaustMap } from 'rxjs/operators';\n\nexport class HigherOrderExamples {\n  private http = inject(HttpClient);\n\n  // switchMap: Cancel previous, use latest (search, typeahead)\n  searchUsers(term$: Observable<string>) {\n    return term$.pipe(\n      switchMap(term => this.http.get<User[]>(`/api/users?q=${term}`))\n    );\n  }\n\n  // mergeMap: Process all concurrently (independent requests)\n  uploadFiles(files: File[]) {\n    return from(files).pipe(\n      mergeMap(file => this.http.post('/api/upload', file))\n    );\n  }\n\n  // concatMap: Process sequentially (order matters)\n  processQueue(tasks: Task[]) {\n    return from(tasks).pipe(\n      concatMap(task => this.http.post('/api/process', task))\n    );\n  }\n\n  // exhaustMap: Ignore new until current completes (prevent double-click)\n  saveForm(clicks$: Observable<void>, formData: any) {\n    return clicks$.pipe(\n      exhaustMap(() => this.http.post('/api/save', formData))\n    );\n  }\n}\n```\n\n## Error Handling\n\n```typescript\nimport { catchError, retry, retryWhen, delay, tap } from 'rxjs/operators';\nimport { throwError, of, timer } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class DataService {\n  private http = inject(HttpClient);\n\n  // Retry with exponential backoff\n  getData() {\n    return this.http.get<Data>('/api/data').pipe(\n      retryWhen(errors =>\n        errors.pipe(\n          mergeMap((error, index) => {\n            if (index >= 3) {\n              return throwError(() => error);\n            }\n            const delayMs = Math.pow(2, index) * 1000;\n            return timer(delayMs);\n          })\n        )\n      ),\n      catchError(err => {\n        console.error('Failed after retries:', err);\n        return of(null); // Fallback value\n      })\n    );\n  }\n\n  // Catch and rethrow with context\n  saveData(data: Data) {\n    return this.http.post('/api/data', data).pipe(\n      catchError(err => {\n        if (err.status === 401) {\n          // Handle auth error\n          return throwError(() => new Error('Unauthorized'));\n        }\n        return throwError(() => err);\n      })\n    );\n  }\n}\n```\n\n## Memory Management\n\n```typescript\nimport { Component, DestroyRef, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n@Component({\n  selector: 'app-auto-cleanup',\n  standalone: true\n})\nexport class AutoCleanupComponent {\n  private dataService = inject(DataService);\n  private destroyRef = inject(DestroyRef);\n\n  data = signal<Data[]>([]);\n\n  constructor() {\n    // Modern approach: takeUntilDestroyed\n    this.dataService.getData().pipe(\n      takeUntilDestroyed()  // Auto-cleanup on destroy\n    ).subscribe(data => this.data.set(data));\n\n    // Manual cleanup with DestroyRef\n    const subscription = this.dataService.getUpdates().subscribe();\n    this.destroyRef.onDestroy(() => subscription.unsubscribe());\n  }\n}\n\n// Legacy approach (still valid)\n@Component({\n  selector: 'app-manual-cleanup',\n  standalone: true\n})\nexport class ManualCleanupComponent implements OnDestroy {\n  private destroy$ = new Subject<void>();\n\n  ngOnInit() {\n    this.dataService.getData().pipe(\n      takeUntil(this.destroy$)\n    ).subscribe();\n  }\n\n  ngOnDestroy() {\n    this.destroy$.next();\n    this.destroy$.complete();\n  }\n}\n```\n\n## Combining Observables\n\n```typescript\nimport { combineLatest, forkJoin, merge, zip } from 'rxjs';\n\nexport class CombiningExamples {\n  private http = inject(HttpClient);\n\n  // combineLatest: Emit when any source emits (latest values)\n  getDashboard() {\n    return combineLatest({\n      user: this.http.get<User>('/api/user'),\n      stats: this.http.get<Stats>('/api/stats'),\n      notifications: this.http.get<Notification[]>('/api/notifications')\n    }).pipe(\n      map(({ user, stats, notifications }) => ({\n        user,\n        stats,\n        notifications\n      }))\n    );\n  }\n\n  // forkJoin: Emit when all sources complete (like Promise.all)\n  loadAllData() {\n    return forkJoin({\n      users: this.http.get<User[]>('/api/users'),\n      products: this.http.get<Product[]>('/api/products'),\n      orders: this.http.get<Order[]>('/api/orders')\n    });\n  }\n\n  // merge: Emit when any source emits (flattens all)\n  getActivityFeed() {\n    return merge(\n      this.http.get<Activity[]>('/api/recent'),\n      this.http.get<Activity[]>('/api/trending')\n    );\n  }\n}\n```\n\n## Custom Operators\n\n```typescript\nimport { Observable, OperatorFunction } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\n// Custom operator for logging\nexport function debug<T>(tag: string): OperatorFunction<T, T> {\n  return (source: Observable<T>) =>\n    source.pipe(\n      tap({\n        next: value => console.log(`[${tag}] Next:`, value),\n        error: err => console.error(`[${tag}] Error:`, err),\n        complete: () => console.log(`[${tag}] Complete`)\n      })\n    );\n}\n\n// Usage\nthis.http.get('/api/data').pipe(\n  debug('API Call'),\n  map(data => transform(data))\n).subscribe();\n```\n\n## ShareReplay for Caching\n\n```typescript\nimport { shareReplay } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class ConfigService {\n  private http = inject(HttpClient);\n\n  // Cache config, share with all subscribers\n  config$ = this.http.get<Config>('/api/config').pipe(\n    shareReplay({ bufferSize: 1, refCount: true })\n  );\n\n  // All components get same config without extra HTTP calls\n  getConfig() {\n    return this.config$;\n  }\n}\n```\n\n## Quick Reference\n\n| Use Case | Operator |\n|----------|----------|\n| Transform values | `map`, `pluck` |\n| Filter values | `filter`, `distinctUntilChanged` |\n| Time-based | `debounceTime`, `throttleTime`, `delay` |\n| Cancel previous | `switchMap` |\n| Process all | `mergeMap` |\n| Sequential | `concatMap` |\n| Ignore new | `exhaustMap` |\n| Combine latest | `combineLatest` |\n| Wait for all | `forkJoin` |\n| Error handling | `catchError`, `retry` |\n| Cleanup | `takeUntilDestroyed`, `takeUntil` |\n| Share result | `shareReplay` |\n\n<!-- File: skills/angular-architect/references/testing.md (Tokens: ~2605 | Category: anthropic-skill) -->\n# Angular Testing\n\n## Component Testing\n\n```typescript\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { signal } from '@angular/core';\nimport { UserListComponent } from './user-list.component';\nimport { UsersService } from './users.service';\nimport { of } from 'rxjs';\n\ndescribe('UserListComponent', () => {\n  let component: UserListComponent;\n  let fixture: ComponentFixture<UserListComponent>;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  const mockUsers = [\n    { id: '1', name: 'John Doe', email: 'john@example.com' },\n    { id: '2', name: 'Jane Smith', email: 'jane@example.com' }\n  ];\n\n  beforeEach(async () => {\n    // Create spy object\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll', 'delete']);\n\n    await TestBed.configureTestingModule({\n      imports: [UserListComponent],  // Standalone component\n      providers: [\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    }).compileComponents();\n\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n    fixture = TestBed.createComponent(UserListComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should create', () => {\n    expect(component).toBeTruthy();\n  });\n\n  it('should load users on init', () => {\n    usersService.getAll.and.returnValue(of(mockUsers));\n\n    fixture.detectChanges();  // Trigger ngOnInit\n\n    expect(usersService.getAll).toHaveBeenCalled();\n    expect(component.users()).toEqual(mockUsers);\n  });\n\n  it('should display users in template', () => {\n    component.users.set(mockUsers);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    const userElements = compiled.querySelectorAll('.user-item');\n\n    expect(userElements.length).toBe(2);\n    expect(userElements[0].textContent).toContain('John Doe');\n  });\n\n  it('should emit userSelected when user clicked', () => {\n    const emitSpy = spyOn(component.userSelected, 'emit');\n\n    component.onUserClick(mockUsers[0]);\n\n    expect(emitSpy).toHaveBeenCalledWith(mockUsers[0]);\n  });\n\n  it('should show loading state', () => {\n    component.loading.set(true);\n    fixture.detectChanges();\n\n    const compiled = fixture.nativeElement;\n    expect(compiled.querySelector('.loading')).toBeTruthy();\n  });\n});\n```\n\n## Service Testing\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';\nimport { UsersService } from './users.service';\nimport { User } from './user.model';\n\ndescribe('UsersService', () => {\n  let service: UsersService;\n  let httpMock: HttpTestingController;\n\n  const mockUsers: User[] = [\n    { id: '1', name: 'John', email: 'john@example.com' },\n    { id: '2', name: 'Jane', email: 'jane@example.com' }\n  ];\n\n  beforeEach(() => {\n    TestBed.configureTestingModule({\n      imports: [HttpClientTestingModule],\n      providers: [UsersService]\n    });\n\n    service = TestBed.inject(UsersService);\n    httpMock = TestBed.inject(HttpTestingController);\n  });\n\n  afterEach(() => {\n    httpMock.verify();  // Verify no outstanding requests\n  });\n\n  it('should fetch all users', (done) => {\n    service.getAll().subscribe(users => {\n      expect(users).toEqual(mockUsers);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('GET');\n    req.flush(mockUsers);\n  });\n\n  it('should create a user', (done) => {\n    const newUser: User = { id: '3', name: 'Bob', email: 'bob@example.com' };\n\n    service.create(newUser).subscribe(user => {\n      expect(user).toEqual(newUser);\n      done();\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    expect(req.request.method).toBe('POST');\n    expect(req.request.body).toEqual(newUser);\n    req.flush(newUser);\n  });\n\n  it('should handle error', (done) => {\n    service.getAll().subscribe({\n      next: () => fail('should have failed'),\n      error: (error) => {\n        expect(error.status).toBe(500);\n        done();\n      }\n    });\n\n    const req = httpMock.expectOne('/api/users');\n    req.flush('Server error', { status: 500, statusText: 'Internal Server Error' });\n  });\n});\n```\n\n## RxJS Marble Testing\n\n```typescript\nimport { TestScheduler } from 'rxjs/testing';\nimport { delay, map } from 'rxjs/operators';\n\ndescribe('RxJS Operators', () => {\n  let testScheduler: TestScheduler;\n\n  beforeEach(() => {\n    testScheduler = new TestScheduler((actual, expected) => {\n      expect(actual).toEqual(expected);\n    });\n  });\n\n  it('should map values', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--c--|', { a: 1, b: 2, c: 3 });\n      const expected = '    --x--y--z--|';\n      const result$ = source$.pipe(map(x => x * 10));\n\n      expectObservable(result$).toBe(expected, { x: 10, y: 20, z: 30 });\n    });\n  });\n\n  it('should delay emissions', () => {\n    testScheduler.run(({ cold, expectObservable }) => {\n      const source$ = cold('--a--b--|', { a: 1, b: 2 });\n      const expected = '    ----a--b--|';\n      const result$ = source$.pipe(delay(20));\n\n      expectObservable(result$).toBe(expected, { a: 1, b: 2 });\n    });\n  });\n});\n```\n\n## Testing with Signals\n\n```typescript\nimport { signal } from '@angular/core';\n\ndescribe('Counter Component', () => {\n  it('should update signal value', () => {\n    const count = signal(0);\n\n    expect(count()).toBe(0);\n\n    count.set(5);\n    expect(count()).toBe(5);\n\n    count.update(val => val + 1);\n    expect(count()).toBe(6);\n  });\n\n  it('should compute derived value', () => {\n    const count = signal(5);\n    const doubled = computed(() => count() * 2);\n\n    expect(doubled()).toBe(10);\n\n    count.set(10);\n    expect(doubled()).toBe(20);\n  });\n});\n```\n\n## Testing NgRx\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockStore, MockStore } from '@ngrx/store/testing';\nimport { UsersComponent } from './users.component';\nimport { selectAllUsers, selectUsersLoading } from './store/users.selectors';\n\ndescribe('UsersComponent with NgRx', () => {\n  let component: UsersComponent;\n  let fixture: ComponentFixture<UsersComponent>;\n  let store: MockStore;\n\n  const initialState = {\n    users: {\n      ids: ['1', '2'],\n      entities: {\n        '1': { id: '1', name: 'John' },\n        '2': { id: '2', name: 'Jane' }\n      },\n      loading: false\n    }\n  };\n\n  beforeEach(async () => {\n    await TestBed.configureTestingModule({\n      imports: [UsersComponent],\n      providers: [\n        provideMockStore({ initialState })\n      ]\n    }).compileComponents();\n\n    store = TestBed.inject(MockStore);\n    fixture = TestBed.createComponent(UsersComponent);\n    component = fixture.componentInstance;\n  });\n\n  it('should select users from store', () => {\n    store.overrideSelector(selectAllUsers, [\n      { id: '1', name: 'John' },\n      { id: '2', name: 'Jane' }\n    ]);\n\n    fixture.detectChanges();\n\n    expect(component.users().length).toBe(2);\n  });\n\n  it('should dispatch action on delete', () => {\n    const dispatchSpy = spyOn(store, 'dispatch');\n\n    component.onDelete('1');\n\n    expect(dispatchSpy).toHaveBeenCalledWith(\n      UsersActions.deleteUser({ id: '1' })\n    );\n  });\n});\n```\n\n## Testing Effects\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { provideMockActions } from '@ngrx/effects/testing';\nimport { Observable, of, throwError } from 'rxjs';\nimport { UsersEffects } from './users.effects';\nimport { UsersService } from './users.service';\nimport { UsersActions } from './users.actions';\nimport { hot, cold } from 'jasmine-marbles';\n\ndescribe('UsersEffects', () => {\n  let actions$: Observable<any>;\n  let effects: UsersEffects;\n  let usersService: jasmine.SpyObj<UsersService>;\n\n  beforeEach(() => {\n    const usersServiceSpy = jasmine.createSpyObj('UsersService', ['getAll']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        UsersEffects,\n        provideMockActions(() => actions$),\n        { provide: UsersService, useValue: usersServiceSpy }\n      ]\n    });\n\n    effects = TestBed.inject(UsersEffects);\n    usersService = TestBed.inject(UsersService) as jasmine.SpyObj<UsersService>;\n  });\n\n  it('should load users successfully', () => {\n    const users = [{ id: '1', name: 'John' }];\n    const action = UsersActions.loadUsers();\n    const outcome = UsersActions.loadUsersSuccess({ users });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-b|', { b: users });\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n\n  it('should handle load users failure', () => {\n    const action = UsersActions.loadUsers();\n    const error = new Error('Failed to load');\n    const outcome = UsersActions.loadUsersFailure({ error: error.message });\n\n    actions$ = hot('-a', { a: action });\n    const response = cold('-#|', {}, error);\n    const expected = cold('--c', { c: outcome });\n\n    usersService.getAll.and.returnValue(response);\n\n    expect(effects.loadUsers$).toBeObservable(expected);\n  });\n});\n```\n\n## Testing Guards\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { Router } from '@angular/router';\nimport { authGuard } from './auth.guard';\nimport { AuthService } from './auth.service';\n\ndescribe('authGuard', () => {\n  let authService: jasmine.SpyObj<AuthService>;\n  let router: jasmine.SpyObj<Router>;\n\n  beforeEach(() => {\n    const authServiceSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated']);\n    const routerSpy = jasmine.createSpyObj('Router', ['createUrlTree']);\n\n    TestBed.configureTestingModule({\n      providers: [\n        { provide: AuthService, useValue: authServiceSpy },\n        { provide: Router, useValue: routerSpy }\n      ]\n    });\n\n    authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;\n    router = TestBed.inject(Router) as jasmine.SpyObj<Router>;\n  });\n\n  it('should allow access when authenticated', () => {\n    authService.isAuthenticated.and.returnValue(true);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, {} as any)\n    );\n\n    expect(result).toBe(true);\n  });\n\n  it('should redirect when not authenticated', () => {\n    authService.isAuthenticated.and.returnValue(false);\n    const urlTree = {} as any;\n    router.createUrlTree.and.returnValue(urlTree);\n\n    const result = TestBed.runInInjectionContext(() =>\n      authGuard({} as any, { url: '/protected' } as any)\n    );\n\n    expect(result).toBe(urlTree);\n    expect(router.createUrlTree).toHaveBeenCalledWith(\n      ['/login'],\n      { queryParams: { returnUrl: '/protected' } }\n    );\n  });\n});\n```\n\n## Quick Reference\n\n| Test Type | Key Tools |\n|-----------|-----------|\n| Component | `TestBed`, `ComponentFixture`, `detectChanges()` |\n| Service | `HttpClientTestingModule`, `HttpTestingController` |\n| RxJS | `TestScheduler`, marble diagrams |\n| NgRx Store | `provideMockStore`, `MockStore` |\n| Effects | `provideMockActions`, jasmine-marbles |\n| Guards | `TestBed.runInInjectionContext()` |\n| Signals | Direct value checks with `()` |\n| Spies | `jasmine.createSpyObj()`, `spyOn()` |\n\n<!-- File: skills/angular-architect/SKILL.md (Tokens: ~1358 | Category: anthropic-skill) -->\n---\nname: angular-architect\ndescription: Generates Angular 17+ standalone components, configures advanced routing with lazy loading and guards, implements NgRx state management, applies RxJS patterns, and optimizes bundle performance. Use when building Angular 17+ applications with standalone components or signals, setting up NgRx stores, establishing RxJS reactive patterns, performance tuning, or writing Angular tests for enterprise apps.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: frontend\n  triggers: Angular, Angular 17, standalone components, signals, RxJS, NgRx, Angular performance, Angular routing, Angular testing\n  role: specialist\n  scope: implementation\n  output-format: code\n  related-skills: typescript-pro, test-master\n---\n\n# Angular Architect\n\nSenior Angular architect specializing in Angular 17+ with standalone components, signals, and enterprise-grade application development.\n\n## Core Workflow\n\n1. **Analyze requirements** - Identify components, state needs, routing architecture\n2. **Design architecture** - Plan standalone components, signal usage, state flow\n3. **Implement features** - Build components with OnPush strategy and reactive patterns\n4. **Manage state** - Setup NgRx store, effects, selectors as needed; verify store hydration and action flow with Redux DevTools before proceeding\n5. **Optimize** - Apply performance best practices and bundle optimization; run `ng build --configuration production` to verify bundle size and flag regressions\n6. **Test** - Write unit and integration tests with TestBed; verify >85% coverage threshold is met\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Components | `references/components.md` | Standalone components, signals, input/output |\n| RxJS | `references/rxjs.md` | Observables, operators, subjects, error handling |\n| NgRx | `references/ngrx.md` | Store, effects, selectors, entity adapter |\n| Routing | `references/routing.md` | Router config, guards, lazy loading, resolvers |\n| Testing | `references/testing.md` | TestBed, component tests, service tests |\n\n## Key Patterns\n\n### Standalone Component with OnPush and Signals\n\n```typescript\nimport { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n  selector: 'app-user-card',\n  standalone: true,\n  imports: [CommonModule],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  template: `\n    <div class=\"user-card\">\n      <h2>{{ fullName() }}</h2>\n      <button (click)=\"onSelect()\">Select</button>\n    </div>\n  `,\n})\nexport class UserCardComponent {\n  firstName = input.required<string>();\n  lastName = input.required<string>();\n  selected = output<string>();\n\n  fullName = computed(() => `${this.firstName()} ${this.lastName()}`);\n\n  onSelect(): void {\n    this.selected.emit(this.fullName());\n  }\n}\n```\n\n### RxJS Subscription Management with `takeUntilDestroyed`\n\n```typescript\nimport { Component, OnInit, inject } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { UserService } from './user.service';\n\n@Component({ selector: 'app-users', standalone: true, template: `...` })\nexport class UsersComponent implements OnInit {\n  private userService = inject(UserService);\n  // DestroyRef is captured at construction time for use in ngOnInit\n  private destroyRef = inject(DestroyRef);\n\n  ngOnInit(): void {\n    this.userService.getUsers()\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe({\n        next: (users) => { /* handle */ },\n        error: (err) => console.error('Failed to load users', err),\n      });\n  }\n}\n```\n\n### NgRx Action / Reducer / Selector\n\n```typescript\n// actions\nexport const loadUsers = createAction('[Users] Load Users');\nexport const loadUsersSuccess = createAction('[Users] Load Users Success', props<{ users: User[] }>());\nexport const loadUsersFailure = createAction('[Users] Load Users Failure', props<{ error: string }>());\n\n// reducer\nexport interface UsersState { users: User[]; loading: boolean; error: string | null; }\nconst initialState: UsersState = { users: [], loading: false, error: null };\n\nexport const usersReducer = createReducer(\n  initialState,\n  on(loadUsers, (state) => ({ ...state, loading: true, error: null })),\n  on(loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })),\n  on(loadUsersFailure, (state, { error }) => ({ ...state, error, loading: false })),\n);\n\n// selectors\nexport const selectUsersState = createFeatureSelector<UsersState>('users');\nexport const selectAllUsers = createSelector(selectUsersState, (s) => s.users);\nexport const selectUsersLoading = createSelector(selectUsersState, (s) => s.loading);\n```\n\n## Constraints\n\n### MUST DO\n- Use standalone components (Angular 17+ default)\n- Use signals for reactive state where appropriate\n- Use OnPush change detection strategy\n- Use strict TypeScript configuration\n- Implement proper error handling in RxJS streams\n- Use `trackBy` functions in `*ngFor` loops\n- Write tests with >85% coverage\n- Follow Angular style guide\n\n### MUST NOT DO\n- Use NgModule-based components (except when required for compatibility)\n- Forget to unsubscribe from observables (use `takeUntilDestroyed` or `async` pipe)\n- Use async operations without proper error handling\n- Skip accessibility attributes\n- Expose sensitive data in client-side code\n- Use `any` type without justification\n- Mutate state directly in NgRx\n- Skip unit tests for critical logic\n\n## Output Templates\n\nWhen implementing Angular features, provide:\n1. Component file with standalone configuration\n2. Service file if business logic is involved\n3. State management files if using NgRx\n4. Test file with comprehensive test cases\n5. Brief explanation of architectural decisions\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/frontend/angular-architect/)\n\n<!-- File: skills/api-designer/references/error-handling.md (Tokens: ~2934 | Category: anthropic-skill) -->\n# API Error Handling\n\n## Error Response Design\n\nConsistent, informative error responses are critical for API usability.\n\n## Standard Error Format\n\n### Basic Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 123 not found\",\n    \"details\": null\n  }\n}\n```\n\n### RFC 7807 Problem Details\n\nStandardized error format (application/problem+json):\n\n```http\nHTTP/1.1 404 Not Found\nContent-Type: application/problem+json\n\n{\n  \"type\": \"https://api.example.com/errors/resource-not-found\",\n  \"title\": \"Resource Not Found\",\n  \"status\": 404,\n  \"detail\": \"User with ID 123 does not exist\",\n  \"instance\": \"/users/123\"\n}\n```\n\n**Fields:**\n- `type` - URI reference identifying error type\n- `title` - Short, human-readable summary\n- `status` - HTTP status code\n- `detail` - Human-readable explanation specific to this occurrence\n- `instance` - URI reference for this specific occurrence\n\n### Extended Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be between 18 and 120\"\n      }\n    ],\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\",\n    \"documentation_url\": \"https://api.example.com/docs/errors#validation-error\"\n  }\n}\n```\n\n## Error Categories\n\n### 1. Validation Errors (400 Bad Request)\n\nClient sent invalid data.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"\",\n  \"email\": \"invalid-email\",\n  \"age\": 15\n}\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"name\",\n        \"code\": \"REQUIRED\",\n        \"message\": \"Name is required\"\n      },\n      {\n        \"field\": \"email\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Email must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Age must be at least 18\",\n        \"constraints\": {\n          \"min\": 18,\n          \"max\": 120\n        }\n      }\n    ]\n  }\n}\n```\n\n### 2. Authentication Errors (401 Unauthorized)\n\nMissing or invalid authentication credentials.\n\n```http\nGET /users/123\nAuthorization: Bearer <invalid-access-token>\n\nResponse: 401 Unauthorized\nWWW-Authenticate: Bearer realm=\"api\", error=\"invalid_token\"\n\n{\n  \"error\": {\n    \"code\": \"INVALID_TOKEN\",\n    \"message\": \"The access token is invalid or has expired\",\n    \"details\": {\n      \"reason\": \"token_expired\",\n      \"expired_at\": \"2024-01-15T10:00:00Z\"\n    }\n  }\n}\n```\n\n**Common auth error codes:**\n- `MISSING_TOKEN` - No auth token provided\n- `INVALID_TOKEN` - Token is malformed or invalid\n- `EXPIRED_TOKEN` - Token has expired\n- `REVOKED_TOKEN` - Token has been revoked\n\n### 3. Authorization Errors (403 Forbidden)\n\nAuthenticated but not authorized to perform action.\n\n```http\nDELETE /users/123\nAuthorization: Bearer <access-token>\n\nResponse: 403 Forbidden\n{\n  \"error\": {\n    \"code\": \"INSUFFICIENT_PERMISSIONS\",\n    \"message\": \"You do not have permission to delete this user\",\n    \"details\": {\n      \"required_permission\": \"users:delete\",\n      \"your_permissions\": [\"users:read\", \"users:update\"]\n    }\n  }\n}\n```\n\n### 4. Not Found Errors (404 Not Found)\n\nResource doesn't exist.\n\n```http\nGET /users/99999\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"User with ID 99999 not found\",\n    \"details\": {\n      \"resource_type\": \"User\",\n      \"resource_id\": \"99999\"\n    }\n  }\n}\n```\n\n### 5. Conflict Errors (409 Conflict)\n\nRequest conflicts with current state.\n\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"email\": \"existing@example.com\",\n  \"name\": \"John Doe\"\n}\n\nResponse: 409 Conflict\n{\n  \"error\": {\n    \"code\": \"RESOURCE_ALREADY_EXISTS\",\n    \"message\": \"User with email 'existing@example.com' already exists\",\n    \"details\": {\n      \"field\": \"email\",\n      \"value\": \"existing@example.com\",\n      \"existing_resource\": \"/users/123\"\n    }\n  }\n}\n```\n\n### 6. Rate Limiting (429 Too Many Requests)\n\nClient exceeded rate limit.\n\n```http\nGET /users\n\nResponse: 429 Too Many Requests\nRetry-After: 60\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1705320000\n\n{\n  \"error\": {\n    \"code\": \"RATE_LIMIT_EXCEEDED\",\n    \"message\": \"You have exceeded the rate limit\",\n    \"details\": {\n      \"limit\": 100,\n      \"window\": \"1 hour\",\n      \"retry_after\": 60,\n      \"reset_at\": \"2024-01-15T11:00:00Z\"\n    }\n  }\n}\n```\n\n### 7. Server Errors (500 Internal Server Error)\n\nUnexpected server error.\n\n```http\nGET /users/123\n\nResponse: 500 Internal Server Error\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\",\n    \"request_id\": \"req_123456\",\n    \"timestamp\": \"2024-01-15T10:30:00Z\"\n  }\n}\n```\n\n**Never expose:**\n- Stack traces\n- Database errors\n- Internal paths\n- Sensitive configuration\n\n### 8. Service Unavailable (503 Service Unavailable)\n\nService temporarily unavailable.\n\n```http\nGET /users\n\nResponse: 503 Service Unavailable\nRetry-After: 300\n\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service is temporarily unavailable due to maintenance\",\n    \"details\": {\n      \"retry_after\": 300,\n      \"maintenance_end\": \"2024-01-15T12:00:00Z\"\n    }\n  }\n}\n```\n\n## Error Code Catalog\n\nDefine standard error codes for your API:\n\n```json\n{\n  \"VALIDATION_ERROR\": {\n    \"status\": 400,\n    \"description\": \"Request validation failed\",\n    \"subcodes\": {\n      \"REQUIRED\": \"Required field is missing\",\n      \"INVALID_FORMAT\": \"Field has invalid format\",\n      \"OUT_OF_RANGE\": \"Value is out of allowed range\",\n      \"INVALID_ENUM\": \"Value is not in allowed set\"\n    }\n  },\n  \"AUTHENTICATION_ERROR\": {\n    \"status\": 401,\n    \"description\": \"Authentication failed\",\n    \"subcodes\": {\n      \"MISSING_TOKEN\": \"No authentication token provided\",\n      \"INVALID_TOKEN\": \"Token is invalid\",\n      \"EXPIRED_TOKEN\": \"Token has expired\"\n    }\n  },\n  \"AUTHORIZATION_ERROR\": {\n    \"status\": 403,\n    \"description\": \"Insufficient permissions\",\n    \"subcodes\": {\n      \"INSUFFICIENT_PERMISSIONS\": \"Missing required permission\",\n      \"RESOURCE_FORBIDDEN\": \"Access to resource is forbidden\"\n    }\n  },\n  \"RESOURCE_NOT_FOUND\": {\n    \"status\": 404,\n    \"description\": \"Resource not found\"\n  },\n  \"CONFLICT_ERROR\": {\n    \"status\": 409,\n    \"description\": \"Request conflicts with current state\",\n    \"subcodes\": {\n      \"RESOURCE_ALREADY_EXISTS\": \"Resource already exists\",\n      \"CONCURRENT_MODIFICATION\": \"Resource was modified by another request\"\n    }\n  },\n  \"RATE_LIMIT_EXCEEDED\": {\n    \"status\": 429,\n    \"description\": \"Rate limit exceeded\"\n  },\n  \"INTERNAL_SERVER_ERROR\": {\n    \"status\": 500,\n    \"description\": \"Internal server error\"\n  }\n}\n```\n\n## Validation Error Details\n\n### Field-Level Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"credit_card.number\",\n        \"code\": \"INVALID_FORMAT\",\n        \"message\": \"Credit card number must be 16 digits\",\n        \"value_provided\": \"1234\",\n        \"constraints\": {\n          \"pattern\": \"^[0-9]{16}$\"\n        }\n      },\n      {\n        \"field\": \"items[0].quantity\",\n        \"code\": \"OUT_OF_RANGE\",\n        \"message\": \"Quantity must be at least 1\",\n        \"value_provided\": 0,\n        \"constraints\": {\n          \"min\": 1,\n          \"max\": 1000\n        }\n      }\n    ]\n  }\n}\n```\n\n### Cross-Field Validation\n\n```json\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"fields\": [\"start_date\", \"end_date\"],\n        \"code\": \"INVALID_RANGE\",\n        \"message\": \"End date must be after start date\",\n        \"values_provided\": {\n          \"start_date\": \"2024-01-20\",\n          \"end_date\": \"2024-01-15\"\n        }\n      }\n    ]\n  }\n}\n```\n\n## Request ID Tracking\n\nAlways include request ID for debugging:\n\n```http\nResponse Headers:\nX-Request-ID: req_abc123\n\nResponse Body:\n{\n  \"error\": {\n    \"code\": \"INTERNAL_SERVER_ERROR\",\n    \"message\": \"An unexpected error occurred\",\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\nClients can reference request ID in support tickets.\n\n## Error Documentation\n\nDocument all possible errors for each endpoint:\n\n```yaml\n/users/{id}:\n  get:\n    responses:\n      '200':\n        description: Success\n      '401':\n        description: Authentication failed\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              missing_token:\n                value:\n                  error:\n                    code: MISSING_TOKEN\n                    message: No authentication token provided\n              invalid_token:\n                value:\n                  error:\n                    code: INVALID_TOKEN\n                    message: Token is invalid or expired\n      '404':\n        description: User not found\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Error'\n            examples:\n              not_found:\n                value:\n                  error:\n                    code: RESOURCE_NOT_FOUND\n                    message: User with ID 123 not found\n```\n\n## Retry Guidance\n\nHelp clients understand if they should retry:\n\n```json\n{\n  \"error\": {\n    \"code\": \"SERVICE_UNAVAILABLE\",\n    \"message\": \"Service temporarily unavailable\",\n    \"retry\": {\n      \"retryable\": true,\n      \"retry_after\": 60,\n      \"max_retries\": 3,\n      \"backoff\": \"exponential\"\n    }\n  }\n}\n```\n\n### Retryable Errors\n\n- 408 Request Timeout\n- 429 Too Many Requests (with Retry-After)\n- 500 Internal Server Error (sometimes)\n- 502 Bad Gateway\n- 503 Service Unavailable\n- 504 Gateway Timeout\n\n### Non-Retryable Errors\n\n- 400 Bad Request\n- 401 Unauthorized\n- 403 Forbidden\n- 404 Not Found\n- 409 Conflict\n- 422 Unprocessable Entity\n\n## Multi-Language Support\n\nSupport error messages in multiple languages:\n\n```http\nGET /users/invalid\nAccept-Language: es\n\nResponse: 404 Not Found\nContent-Language: es\n{\n  \"error\": {\n    \"code\": \"RESOURCE_NOT_FOUND\",\n    \"message\": \"Usuario con ID 'invalid' no encontrado\"\n  }\n}\n```\n\nAlways include `code` so clients can implement their own translations.\n\n## Best Practices\n\n1. **Use standard HTTP status codes** - Don't return 200 for errors\n2. **Include machine-readable codes** - Error codes for client logic\n3. **Provide human-readable messages** - Clear explanations\n4. **Be specific but safe** - Don't expose sensitive information\n5. **Include request ID** - For tracking and debugging\n6. **Document all errors** - Every possible error for each endpoint\n7. **Be consistent** - Same format across all endpoints\n8. **Help clients retry** - Indicate if error is retryable\n9. **Validate early** - Return validation errors immediately\n10. **Log errors server-side** - Track errors for monitoring\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Generic error messages** - \"Error occurred\" without details\n- **Exposing stack traces** - Security risk\n- **Inconsistent error format** - Different structure per endpoint\n- **Missing error codes** - Only human-readable messages\n- **Wrong status codes** - Returning 200 with error in body\n- **No request ID** - Makes debugging impossible\n- **Undocumented errors** - Clients don't know what to expect\n- **Too much information** - Exposing internal implementation\n\n<!-- File: skills/api-designer/references/openapi.md (Tokens: ~3772 | Category: anthropic-skill) -->\n# OpenAPI 3.1 Specification\n\n## What is OpenAPI?\n\nOpenAPI (formerly Swagger) is a standard for describing REST APIs. It enables:\n- Interactive documentation\n- Code generation (SDKs, clients, servers)\n- API testing tools\n- Contract validation\n- Mock servers\n\n## Basic Structure\n\n### Minimal OpenAPI 3.1 Spec\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 1.0.0\n  description: A sample API\n  contact:\n    name: API Support\n    email: support@example.com\n    url: https://example.com/support\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n\nservers:\n  - url: https://api.example.com/v1\n    description: Production server\n  - url: https://staging-api.example.com/v1\n    description: Staging server\n  - url: http://localhost:3000/v1\n    description: Local development\n\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users\n      operationId: listUsers\n      tags:\n        - Users\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  data:\n                    type: array\n                    items:\n                      $ref: '#/components/schemas/User'\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n      properties:\n        id:\n          type: integer\n          format: int64\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          example: John Doe\n```\n\n## Info Object\n\nMetadata about the API:\n\n```yaml\ninfo:\n  title: Users API\n  version: 1.0.0\n  description: |\n    # Users API\n\n    This API manages user accounts and profiles.\n\n    ## Features\n    - User CRUD operations\n    - Authentication with JWT\n    - Role-based authorization\n\n  termsOfService: https://example.com/terms\n\n  contact:\n    name: API Support Team\n    email: api-support@example.com\n    url: https://example.com/support\n\n  license:\n    name: MIT\n    url: https://opensource.org/licenses/MIT\n\n  x-api-id: users-api-v1\n  x-audience: external\n```\n\n## Servers\n\nDefine API base URLs:\n\n```yaml\nservers:\n  - url: https://api.example.com/v1\n    description: Production\n    variables:\n      version:\n        default: v1\n        enum:\n          - v1\n          - v2\n\n  - url: https://{environment}.example.com/v1\n    description: Dynamic environment\n    variables:\n      environment:\n        default: api\n        enum:\n          - api\n          - staging\n          - dev\n```\n\n## Paths and Operations\n\n### Complete Endpoint Example\n\n```yaml\npaths:\n  /users:\n    get:\n      summary: List users\n      description: Retrieve a paginated list of users with optional filtering\n      operationId: listUsers\n      tags:\n        - Users\n\n      parameters:\n        - name: offset\n          in: query\n          description: Number of items to skip\n          required: false\n          schema:\n            type: integer\n            minimum: 0\n            default: 0\n\n        - name: limit\n          in: query\n          description: Maximum number of items to return\n          required: false\n          schema:\n            type: integer\n            minimum: 1\n            maximum: 100\n            default: 20\n\n        - name: status\n          in: query\n          description: Filter by user status\n          required: false\n          schema:\n            type: string\n            enum:\n              - active\n              - inactive\n              - suspended\n\n      security:\n        - bearerAuth: []\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/UserListResponse'\n              examples:\n                success:\n                  $ref: '#/components/examples/UserListSuccess'\n\n        '401':\n          $ref: '#/components/responses/Unauthorized'\n\n        '429':\n          $ref: '#/components/responses/RateLimitExceeded'\n\n    post:\n      summary: Create user\n      description: Create a new user account\n      operationId: createUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/CreateUserRequest'\n            examples:\n              basic:\n                $ref: '#/components/examples/CreateUserBasic'\n\n      responses:\n        '201':\n          description: User created successfully\n          headers:\n            Location:\n              description: URL of the created user\n              schema:\n                type: string\n                format: uri\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '400':\n          $ref: '#/components/responses/ValidationError'\n\n        '409':\n          description: User already exists\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Error'\n\n  /users/{userId}:\n    parameters:\n      - name: userId\n        in: path\n        description: User ID\n        required: true\n        schema:\n          type: integer\n          format: int64\n\n    get:\n      summary: Get user\n      description: Retrieve a specific user by ID\n      operationId: getUser\n      tags:\n        - Users\n\n      responses:\n        '200':\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    put:\n      summary: Update user\n      description: Replace user data\n      operationId: updateUser\n      tags:\n        - Users\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/UpdateUserRequest'\n\n      responses:\n        '200':\n          description: User updated successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/User'\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n\n    delete:\n      summary: Delete user\n      description: Delete a user account\n      operationId: deleteUser\n      tags:\n        - Users\n\n      responses:\n        '204':\n          description: User deleted successfully\n\n        '404':\n          $ref: '#/components/responses/NotFound'\n```\n\n## Components\n\nReusable components for your API spec.\n\n### Schemas\n\n```yaml\ncomponents:\n  schemas:\n    User:\n      type: object\n      required:\n        - id\n        - email\n        - name\n      properties:\n        id:\n          type: integer\n          format: int64\n          readOnly: true\n          example: 123\n        email:\n          type: string\n          format: email\n          example: john@example.com\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n          example: John Doe\n        status:\n          type: string\n          enum:\n            - active\n            - inactive\n            - suspended\n          default: active\n        created_at:\n          type: string\n          format: date-time\n          readOnly: true\n          example: \"2024-01-15T10:30:00Z\"\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    CreateUserRequest:\n      type: object\n      required:\n        - email\n        - name\n      properties:\n        email:\n          type: string\n          format: email\n        name:\n          type: string\n          minLength: 1\n          maxLength: 100\n        metadata:\n          type: object\n          additionalProperties:\n            type: string\n\n    UserListResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/User'\n        pagination:\n          $ref: '#/components/schemas/Pagination'\n\n    Pagination:\n      type: object\n      properties:\n        offset:\n          type: integer\n          minimum: 0\n        limit:\n          type: integer\n          minimum: 1\n        total:\n          type: integer\n          minimum: 0\n        has_more:\n          type: boolean\n\n    Error:\n      type: object\n      required:\n        - error\n      properties:\n        error:\n          type: object\n          required:\n            - code\n            - message\n          properties:\n            code:\n              type: string\n              example: RESOURCE_NOT_FOUND\n            message:\n              type: string\n              example: User with ID 123 not found\n            details:\n              type: object\n            request_id:\n              type: string\n              example: req_abc123\n```\n\n### Security Schemes\n\n```yaml\ncomponents:\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n      description: JWT access token\n\n    apiKey:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      description: API key for authentication\n\n    oauth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://auth.example.com/oauth/authorize\n          tokenUrl: https://auth.example.com/oauth/token\n          scopes:\n            users:read: Read user data\n            users:write: Create and update users\n            users:delete: Delete users\n```\n\nApply security globally or per-operation:\n\n```yaml\n# Global security\nsecurity:\n  - bearerAuth: []\n\n# Or per-operation\npaths:\n  /users:\n    get:\n      security:\n        - bearerAuth: []\n        - apiKey: []  # Alternative auth method\n```\n\n### Responses\n\nReusable response definitions:\n\n```yaml\ncomponents:\n  responses:\n    NotFound:\n      description: Resource not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: RESOURCE_NOT_FOUND\n              message: The requested resource was not found\n\n    Unauthorized:\n      description: Authentication required\n      headers:\n        WWW-Authenticate:\n          schema:\n            type: string\n          description: Authentication method\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n\n    ValidationError:\n      description: Validation failed\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n          example:\n            error:\n              code: VALIDATION_ERROR\n              message: Request validation failed\n              details:\n                - field: email\n                  code: INVALID_FORMAT\n                  message: Email must be a valid email address\n\n    RateLimitExceeded:\n      description: Rate limit exceeded\n      headers:\n        X-RateLimit-Limit:\n          schema:\n            type: integer\n          description: Request limit per hour\n        X-RateLimit-Remaining:\n          schema:\n            type: integer\n          description: Remaining requests\n        X-RateLimit-Reset:\n          schema:\n            type: integer\n            format: int64\n          description: Time when limit resets (Unix timestamp)\n        Retry-After:\n          schema:\n            type: integer\n          description: Seconds to wait before retry\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n```\n\n### Examples\n\n```yaml\ncomponents:\n  examples:\n    UserListSuccess:\n      summary: Successful user list response\n      value:\n        data:\n          - id: 1\n            email: john@example.com\n            name: John Doe\n            status: active\n            created_at: \"2024-01-15T10:30:00Z\"\n          - id: 2\n            email: jane@example.com\n            name: Jane Smith\n            status: active\n            created_at: \"2024-01-16T14:20:00Z\"\n        pagination:\n          offset: 0\n          limit: 20\n          total: 150\n          has_more: true\n\n    CreateUserBasic:\n      summary: Create user with minimal fields\n      value:\n        email: newuser@example.com\n        name: New User\n```\n\n## Data Types\n\n### Primitive Types\n\n```yaml\n# String\ntype: string\nexample: \"Hello World\"\n\n# String with format\ntype: string\nformat: email\nexample: \"user@example.com\"\n\n# Integer\ntype: integer\nformat: int64\nexample: 123\n\n# Number (float)\ntype: number\nformat: double\nexample: 99.99\n\n# Boolean\ntype: boolean\nexample: true\n\n# Date-time\ntype: string\nformat: date-time\nexample: \"2024-01-15T10:30:00Z\"\n\n# Date\ntype: string\nformat: date\nexample: \"2024-01-15\"\n\n# UUID\ntype: string\nformat: uuid\nexample: \"550e8400-e29b-41d4-a716-446655440000\"\n\n# URI\ntype: string\nformat: uri\nexample: \"https://example.com/users/123\"\n```\n\n### Arrays\n\n```yaml\ntype: array\nitems:\n  type: string\nminItems: 1\nmaxItems: 10\nuniqueItems: true\nexample: [\"tag1\", \"tag2\", \"tag3\"]\n\n# Array of objects\ntype: array\nitems:\n  $ref: '#/components/schemas/User'\n```\n\n### Objects\n\n```yaml\ntype: object\nrequired:\n  - name\n  - email\nproperties:\n  name:\n    type: string\n  email:\n    type: string\n    format: email\n  age:\n    type: integer\n    minimum: 0\n    maximum: 120\n\n# Additional properties\nadditionalProperties: false  # Strict - no extra properties\nadditionalProperties: true   # Allow any extra properties\nadditionalProperties:        # Extra properties must be strings\n  type: string\n```\n\n### Enums\n\n```yaml\ntype: string\nenum:\n  - active\n  - inactive\n  - suspended\ndefault: active\n```\n\n### OneOf / AnyOf / AllOf\n\n```yaml\n# OneOf - exactly one schema matches\noneOf:\n  - $ref: '#/components/schemas/CreditCard'\n  - $ref: '#/components/schemas/BankAccount'\n\n# AnyOf - one or more schemas match\nanyOf:\n  - $ref: '#/components/schemas/User'\n  - $ref: '#/components/schemas/Organization'\n\n# AllOf - all schemas must match (inheritance)\nallOf:\n  - $ref: '#/components/schemas/BaseUser'\n  - type: object\n    properties:\n      admin_level:\n        type: integer\n```\n\n## Validation\n\n### String Validation\n\n```yaml\ntype: string\nminLength: 1\nmaxLength: 100\npattern: \"^[a-zA-Z0-9_-]+$\"\nformat: email\n```\n\n### Number Validation\n\n```yaml\ntype: integer\nminimum: 0\nmaximum: 100\nexclusiveMinimum: true  # > 0 instead of >= 0\nmultipleOf: 5\n```\n\n### Array Validation\n\n```yaml\ntype: array\nminItems: 1\nmaxItems: 10\nuniqueItems: true\n```\n\n## Tags\n\nOrganize endpoints into logical groups:\n\n```yaml\ntags:\n  - name: Users\n    description: User management operations\n  - name: Orders\n    description: Order management\n  - name: Products\n    description: Product catalog\n\npaths:\n  /users:\n    get:\n      tags:\n        - Users\n```\n\n## Documentation\n\n### Markdown Support\n\n```yaml\ndescription: |\n  # User Management\n\n  This endpoint allows you to manage users.\n\n  ## Features\n  - Create users\n  - Update profiles\n  - Delete accounts\n\n  ## Authentication\n  Requires JWT bearer token.\n\n  ## Example\n  ```json\n  {\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\"\n  }\n  ```\n```\n\n## Code Generation\n\nGenerate SDKs from OpenAPI spec:\n\n```bash\n# Generate TypeScript client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g typescript-axios \\\n  -o ./client\n\n# Generate Python client\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g python \\\n  -o ./python-client\n\n# Generate server stub\nopenapi-generator-cli generate \\\n  -i openapi.yaml \\\n  -g nodejs-express-server \\\n  -o ./server\n```\n\n## Validation Tools\n\nValidate OpenAPI spec:\n\n```bash\n# Using Swagger CLI\nswagger-cli validate openapi.yaml\n\n# Using Spectral (advanced linting)\nspectral lint openapi.yaml\n```\n\n## Best Practices\n\n1. **Use components** - Reuse schemas, responses, parameters\n2. **Add examples** - Include realistic examples for all schemas\n3. **Document thoroughly** - Every endpoint, parameter, response\n4. **Version your spec** - Track changes to the specification\n5. **Validate regularly** - Use tools to catch errors\n6. **Use $ref** - Reference components instead of duplicating\n7. **Include error responses** - Document all possible errors\n8. **Add operationId** - Unique ID for each operation (for code gen)\n9. **Tag endpoints** - Organize into logical groups\n10. **Provide security schemes** - Document authentication clearly\n\n<!-- File: skills/api-designer/references/pagination.md (Tokens: ~2899 | Category: anthropic-skill) -->\n# Pagination Patterns\n\n## Why Paginate?\n\nLarge collections can't be returned all at once due to:\n- Performance (slow queries, large payloads)\n- Memory constraints (server and client)\n- Network timeouts\n- Poor user experience\n\nAlways paginate collection endpoints.\n\n## Pagination Strategies\n\n### 1. Offset-Based Pagination\n\nMost common and intuitive. Uses `offset` (skip) and `limit` (page size).\n\n**Request:**\n```http\nGET /users?offset=20&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"offset\": 20,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=10&limit=10\",\n    \"next\": \"/users?offset=30&limit=10\",\n    \"last\": \"/users?offset=140&limit=10\"\n  }\n}\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to understand\n- Random access (jump to any page)\n- Shows total count\n\n**Disadvantages:**\n- Performance degrades with large offsets (database scans many rows)\n- Inconsistent results if data changes during pagination\n- Inefficient for real-time data\n- Database must count total rows (expensive)\n\n**Use when:**\n- Small to medium datasets\n- Data doesn't change frequently\n- Need random page access\n- Need total count\n\n### 2. Page-Based Pagination\n\nSimplified offset pagination using page numbers.\n\n**Request:**\n```http\nGET /users?page=3&per_page=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"page\": 3,\n    \"per_page\": 10,\n    \"total_pages\": 15,\n    \"total_count\": 150\n  },\n  \"links\": {\n    \"first\": \"/users?page=1&per_page=10\",\n    \"prev\": \"/users?page=2&per_page=10\",\n    \"next\": \"/users?page=4&per_page=10\",\n    \"last\": \"/users?page=15&per_page=10\"\n  }\n}\n```\n\n**Calculation:**\n- `offset = (page - 1) * per_page`\n- `total_pages = ceil(total_count / per_page)`\n\n**Same pros/cons as offset-based, but:**\n- More intuitive for users (page 1, page 2)\n- Common in web applications\n\n### 3. Cursor-Based Pagination\n\nUses an opaque cursor (pointer) to the next set of results.\n\n**Request:**\n```http\nGET /users?limit=10\nGET /users?cursor=<cursor>&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\"},\n    {\"id\": 22, \"name\": \"User 22\"}\n  ],\n  \"pagination\": {\n    \"next_cursor\": \"<next-cursor>\",\n    \"prev_cursor\": \"<prev-cursor>\",\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?cursor=<next-cursor>&limit=10\",\n    \"prev\": \"/users?cursor=<prev-cursor>&limit=10\"\n  }\n}\n```\n\n**Cursor structure (base64 encoded):**\n```json\n{\"id\": 30, \"sort\": \"created_at\"}\n```\n\n**Implementation:**\n```sql\n-- First page\nSELECT * FROM users ORDER BY created_at DESC LIMIT 10;\n\n-- Next page (cursor points to last item)\nSELECT * FROM users\nWHERE created_at < '2024-01-15T10:30:00Z'\nORDER BY created_at DESC\nLIMIT 10;\n```\n\n**Advantages:**\n- Consistent results (no skipped/duplicate items)\n- Efficient for large datasets\n- Works well with real-time data\n- No expensive COUNT query\n- Better database performance\n\n**Disadvantages:**\n- No random access (can't jump to page 10)\n- No total count\n- More complex to implement\n- Cursor is opaque (users can't modify it)\n\n**Use when:**\n- Large datasets\n- Data changes frequently\n- Infinite scroll UI\n- Real-time feeds\n- Performance is critical\n\n### 4. Keyset Pagination\n\nSimilar to cursor but uses actual field values instead of opaque cursor.\n\n**Request:**\n```http\nGET /users?after_id=20&limit=10\nGET /users?after_created_at=2024-01-15T10:30:00Z&limit=10\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\"id\": 21, \"name\": \"User 21\", \"created_at\": \"2024-01-15T11:00:00Z\"},\n    {\"id\": 22, \"name\": \"User 22\", \"created_at\": \"2024-01-15T11:30:00Z\"}\n  ],\n  \"pagination\": {\n    \"after_id\": 30,\n    \"limit\": 10,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/users?after_id=30&limit=10\"\n  }\n}\n```\n\n**Implementation:**\n```sql\nSELECT * FROM users\nWHERE id > 20\nORDER BY id ASC\nLIMIT 10;\n```\n\n**Advantages:**\n- Very efficient (uses index)\n- Transparent cursor (human readable)\n- Consistent results\n- Simple implementation\n\n**Disadvantages:**\n- Requires indexed column\n- No random access\n- Sorting limited to cursor field\n- Complex for multi-field sorting\n\n**Use when:**\n- Simple ordering (by ID, timestamp)\n- Need efficient pagination\n- Want transparent cursor\n- Have proper indexes\n\n### 5. Seek Pagination (Time-Based)\n\nSpecialized keyset pagination for time-series data.\n\n**Request:**\n```http\nGET /events?since=2024-01-15T10:00:00Z&until=2024-01-15T11:00:00Z&limit=100\n```\n\n**Response:**\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"since\": \"2024-01-15T10:00:00Z\",\n    \"until\": \"2024-01-15T11:00:00Z\",\n    \"limit\": 100,\n    \"has_more\": true\n  },\n  \"links\": {\n    \"next\": \"/events?since=2024-01-15T11:00:00Z&until=2024-01-15T12:00:00Z&limit=100\"\n  }\n}\n```\n\n**Use for:**\n- Time-series data\n- Logs and events\n- Activity streams\n- Analytics data\n\n## Default Limits\n\nAlways set reasonable defaults and maximum limits:\n\n```json\n{\n  \"default_limit\": 20,\n  \"max_limit\": 100,\n  \"min_limit\": 1\n}\n```\n\n**Validation:**\n```http\nGET /users?limit=1000\n\nResponse: 400 Bad Request\n{\n  \"error\": {\n    \"code\": \"INVALID_LIMIT\",\n    \"message\": \"Limit must be between 1 and 100. Default is 20.\"\n  }\n}\n```\n\n## Response Format\n\n### Standard Pagination Object\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"limit\": 10,\n    \"offset\": 20,\n    \"total\": 150,\n    \"has_more\": true,\n    \"has_previous\": true\n  }\n}\n```\n\n### Link Header (RFC 5988)\n\n```http\nLink: </users?offset=0&limit=10>; rel=\"first\",\n      </users?offset=10&limit=10>; rel=\"prev\",\n      </users?offset=30&limit=10>; rel=\"next\",\n      </users?offset=140&limit=10>; rel=\"last\"\n```\n\n**Used by:** GitHub API\n\n### Embedded Links\n\n```json\n{\n  \"data\": [...],\n  \"_links\": {\n    \"self\": { \"href\": \"/users?offset=20&limit=10\" },\n    \"first\": { \"href\": \"/users?offset=0&limit=10\" },\n    \"prev\": { \"href\": \"/users?offset=10&limit=10\" },\n    \"next\": { \"href\": \"/users?offset=30&limit=10\" },\n    \"last\": { \"href\": \"/users?offset=140&limit=10\" }\n  }\n}\n```\n\n## Sorting with Pagination\n\nAlways support sorting when paginating:\n\n```http\nGET /users?sort=created_at&order=desc&limit=10\nGET /users?sort=-created_at&limit=10                    # Descending\nGET /users?sort=last_name,first_name&limit=10           # Multi-field\n```\n\n**For cursor pagination, cursor must include sort fields:**\n```json\n{\n  \"cursor\": {\n    \"id\": 123,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"sort_fields\": [\"created_at\", \"id\"]\n  }\n}\n```\n\n## Filtering with Pagination\n\nCombine filtering with pagination:\n\n```http\nGET /users?status=active&role=admin&offset=0&limit=10\n```\n\n**Important:** Apply filters before pagination:\n1. Filter records\n2. Count filtered results\n3. Apply pagination\n4. Return paginated subset\n\n## Total Count\n\n### Include Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"total\": 1523,\n    \"limit\": 10,\n    \"offset\": 20\n  }\n}\n```\n\n**Pros:**\n- Clients know total results\n- Can calculate total pages\n- Better UX (show \"Page 3 of 153\")\n\n**Cons:**\n- COUNT query is expensive\n- Slows down response\n- Inaccurate for large/changing datasets\n\n### Omit Total Count\n\n```json\n{\n  \"data\": [...],\n  \"pagination\": {\n    \"has_more\": true,\n    \"limit\": 10\n  }\n}\n```\n\n**Use when:**\n- Large datasets (COUNT is too slow)\n- Real-time data (count changes constantly)\n- Cursor pagination\n- Infinite scroll UI\n\n### Optional Total Count\n\nLet client request total count:\n\n```http\nGET /users?limit=10&include_total=true\n```\n\n## Edge Cases\n\n### Empty Results\n\n```json\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 0,\n    \"limit\": 10,\n    \"total\": 0,\n    \"has_more\": false\n  }\n}\n```\n\n### Last Page\n\n```json\n{\n  \"data\": [{\"id\": 150, \"name\": \"Last User\"}],\n  \"pagination\": {\n    \"offset\": 140,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  },\n  \"links\": {\n    \"first\": \"/users?offset=0&limit=10\",\n    \"prev\": \"/users?offset=130&limit=10\",\n    \"next\": null\n  }\n}\n```\n\n### Out of Range\n\n```http\nGET /users?offset=10000&limit=10\n\nResponse: 200 OK (empty results)\n{\n  \"data\": [],\n  \"pagination\": {\n    \"offset\": 10000,\n    \"limit\": 10,\n    \"total\": 150,\n    \"has_more\": false\n  }\n}\n```\n\nOr return 404 for pages that don't exist:\n```http\nGET /users?page=1000&per_page=10\n\nResponse: 404 Not Found\n{\n  \"error\": {\n    \"code\": \"PAGE_NOT_FOUND\",\n    \"message\": \"Page 1000 does not exist. Total pages: 15\"\n  }\n}\n```\n\n## Best Practices\n\n1. **Always paginate collections** - Never return unbounded lists\n2. **Set reasonable defaults** - Default limit of 20-50 items\n3. **Enforce maximum limits** - Prevent excessive loads (max 100-1000)\n4. **Include has_more flag** - Tell clients if more results exist\n5. **Provide navigation links** - Make it easy to get next/prev pages\n6. **Document pagination** - Explain cursor format, limits, defaults\n7. **Be consistent** - Use same pagination pattern across all endpoints\n8. **Consider performance** - Choose strategy based on data size/type\n9. **Support sorting** - Let clients control result order\n10. **Handle edge cases** - Empty results, last page, invalid cursors\n\n## Comparison Matrix\n\n| Feature | Offset | Page | Cursor | Keyset |\n|---------|--------|------|--------|--------|\n| Performance | Poor for large offsets | Poor | Excellent | Excellent |\n| Random access | Yes | Yes | No | No |\n| Total count | Yes | Yes | No | Optional |\n| Consistency | Poor | Poor | Excellent | Excellent |\n| Complexity | Simple | Simple | Medium | Medium |\n| Real-time data | Poor | Poor | Excellent | Excellent |\n| Database load | High | High | Low | Low |\n| Use case | Small datasets | Web UIs | Feeds/streams | Large datasets |\n\n<!-- File: skills/api-designer/references/rest-patterns.md (Tokens: ~2081 | Category: anthropic-skill) -->\n# REST Design Patterns\n\n## Resource-Oriented Architecture\n\nREST APIs are built around resources, not actions. Resources are the nouns of your API.\n\n### Resource Identification\n\n**Good Resource URIs:**\n```\nGET    /users                  # Collection\nGET    /users/{id}             # Individual resource\nGET    /users/{id}/orders      # Nested collection\nPOST   /users                  # Create resource\nPUT    /users/{id}             # Replace resource\nPATCH  /users/{id}             # Update resource\nDELETE /users/{id}             # Delete resource\n```\n\n**Bad Resource URIs:**\n```\nPOST   /getUser                # Verb in URI\nPOST   /createUser             # Verb in URI\nGET    /user?action=delete     # Action as query param\n```\n\n### Resource Naming Conventions\n\n- Use plural nouns for collections: `/users`, `/orders`, `/products`\n- Use lowercase and hyphens for readability: `/shipping-addresses`\n- Avoid deep nesting (max 2-3 levels): `/users/{id}/orders/{orderId}`\n- Use query parameters for filtering: `/users?status=active&role=admin`\n\n## HTTP Method Semantics\n\n### Safe and Idempotent Methods\n\n| Method | Safe | Idempotent | Use Case |\n|--------|------|------------|----------|\n| GET | Yes | Yes | Retrieve resource(s) |\n| POST | No | No | Create resource, non-idempotent operations |\n| PUT | No | Yes | Replace entire resource |\n| PATCH | No | No | Partial update |\n| DELETE | No | Yes | Remove resource |\n| HEAD | Yes | Yes | Get metadata only |\n| OPTIONS | Yes | Yes | Get allowed methods |\n\n### Method Usage\n\n**GET - Retrieve Resources**\n```http\nGET /users/123\nAccept: application/json\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"created_at\": \"2024-01-15T10:30:00Z\"\n}\n```\n\n**POST - Create Resources**\n```http\nPOST /users\nContent-Type: application/json\n\n{\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\"\n}\n\nResponse: 201 Created\nLocation: /users/124\n{\n  \"id\": 124,\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\",\n  \"created_at\": \"2024-01-16T14:20:00Z\"\n}\n```\n\n**PUT - Replace Resource**\n```http\nPUT /users/123\nContent-Type: application/json\n\n{\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe Updated\",\n  \"email\": \"john.new@example.com\",\n  \"updated_at\": \"2024-01-17T09:15:00Z\"\n}\n```\n\n**PATCH - Partial Update**\n```http\nPATCH /users/123\nContent-Type: application/json\n\n{\n  \"email\": \"john.updated@example.com\"\n}\n\nResponse: 200 OK\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john.updated@example.com\",\n  \"updated_at\": \"2024-01-17T10:00:00Z\"\n}\n```\n\n**DELETE - Remove Resource**\n```http\nDELETE /users/123\n\nResponse: 204 No Content\n```\n\n## HTTP Status Codes\n\n### Success Codes (2xx)\n\n- **200 OK** - Request succeeded (GET, PUT, PATCH)\n- **201 Created** - Resource created (POST), include Location header\n- **202 Accepted** - Request accepted for async processing\n- **204 No Content** - Success with no response body (DELETE)\n\n### Redirection (3xx)\n\n- **301 Moved Permanently** - Resource permanently moved\n- **302 Found** - Temporary redirect\n- **304 Not Modified** - Cached version is still valid\n\n### Client Errors (4xx)\n\n- **400 Bad Request** - Invalid request syntax or validation error\n- **401 Unauthorized** - Authentication required or failed\n- **403 Forbidden** - Authenticated but not authorized\n- **404 Not Found** - Resource doesn't exist\n- **405 Method Not Allowed** - HTTP method not supported for resource\n- **409 Conflict** - Request conflicts with current state (e.g., duplicate)\n- **422 Unprocessable Entity** - Valid syntax but semantic errors\n- **429 Too Many Requests** - Rate limit exceeded\n\n### Server Errors (5xx)\n\n- **500 Internal Server Error** - Unexpected server error\n- **502 Bad Gateway** - Invalid response from upstream server\n- **503 Service Unavailable** - Server temporarily unavailable\n- **504 Gateway Timeout** - Upstream server timeout\n\n## HATEOAS (Hypermedia)\n\n### Hypermedia-Driven APIs\n\nInclude links to related resources and available actions:\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" },\n    \"orders\": { \"href\": \"/users/123/orders\" },\n    \"update\": { \"href\": \"/users/123\", \"method\": \"PATCH\" },\n    \"delete\": { \"href\": \"/users/123\", \"method\": \"DELETE\" }\n  }\n}\n```\n\n### HAL (Hypertext Application Language)\n\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"_links\": {\n    \"self\": { \"href\": \"/users/123\" }\n  },\n  \"_embedded\": {\n    \"orders\": [\n      {\n        \"id\": 456,\n        \"total\": 99.99,\n        \"_links\": {\n          \"self\": { \"href\": \"/orders/456\" }\n        }\n      }\n    ]\n  }\n}\n```\n\n## Content Negotiation\n\n### Accept Headers\n\n```http\nGET /users/123\nAccept: application/json\n\nGET /users/123\nAccept: application/xml\n\nGET /users/123\nAccept: application/hal+json\n```\n\n### Response Content-Type\n\n```http\nContent-Type: application/json; charset=utf-8\nContent-Type: application/problem+json\nContent-Type: application/hal+json\n```\n\n## Idempotency\n\n### Idempotent Operations\n\n**PUT - Always idempotent:**\nMultiple identical PUT requests produce the same result as a single request.\n\n**DELETE - Idempotent:**\nFirst DELETE returns 204, subsequent DELETEs return 404 (same end state).\n\n**POST - Not idempotent by default:**\nUse `Idempotency-Key` header for idempotent POST:\n\n```http\nPOST /payments\nIdempotency-Key: 550e8400-e29b-41d4-a716-446655440000\nContent-Type: application/json\n\n{\n  \"amount\": 100.00,\n  \"currency\": \"USD\"\n}\n```\n\nServer stores idempotency key and returns same response for duplicate requests.\n\n## Cache Control\n\n### Cache Headers\n\n```http\nCache-Control: public, max-age=3600\nCache-Control: private, no-cache\nCache-Control: no-store\nETag: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nLast-Modified: Wed, 15 Jan 2024 10:30:00 GMT\n```\n\n### Conditional Requests\n\n```http\nGET /users/123\nIf-None-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\n\nResponse: 304 Not Modified\n```\n\n```http\nPUT /users/123\nIf-Match: \"33a64df551425fcc55e4d42a148795d9f25f89d4\"\nContent-Type: application/json\n\n{\n  \"name\": \"Updated Name\"\n}\n\nResponse: 412 Precondition Failed (if ETag doesn't match)\n```\n\n## URI Patterns\n\n### Consistent URI Structure\n\n```\n/{version}/{resource}\n/{version}/{resource}/{id}\n/{version}/{resource}/{id}/{sub-resource}\n/{version}/{resource}/{id}/{sub-resource}/{sub-id}\n```\n\n### Query Parameters\n\n**Filtering:**\n```\nGET /users?status=active&role=admin\nGET /products?category=electronics&price_min=100&price_max=500\n```\n\n**Sorting:**\n```\nGET /users?sort=created_at\nGET /users?sort=-created_at          # Descending\nGET /users?sort=name,created_at      # Multiple fields\n```\n\n**Field Selection:**\n```\nGET /users?fields=id,name,email\nGET /users?exclude=password,social_security_number\n```\n\n**Search:**\n```\nGET /users?q=john\nGET /products?search=laptop\n```\n\n## Best Practices\n\n1. **Use nouns, not verbs** - Resources are nouns, methods are verbs\n2. **Plural collections** - Use `/users` not `/user`\n3. **Consistent naming** - Choose snake_case or camelCase and stick to it\n4. **Proper status codes** - Use appropriate HTTP status codes\n5. **Include metadata** - Pagination, filtering, sorting info in responses\n6. **Version your API** - Plan for evolution from day one\n7. **Document everything** - OpenAPI specs, examples, error codes\n8. **Security by default** - HTTPS, authentication, rate limiting\n9. **Support filtering** - Enable clients to get exactly what they need\n10. **Implement HATEOAS** - Make APIs self-documenting and discoverable\n\n<!-- File: skills/api-designer/references/versioning.md (Tokens: ~2080 | Category: anthropic-skill) -->\n# API Versioning Strategies\n\n## Why Version APIs?\n\nAPI versioning allows you to evolve your API while maintaining backward compatibility for existing clients. Breaking changes require a new version.\n\n### Breaking Changes\n\nChanges that require a new version:\n- Removing or renaming fields\n- Changing field types (string to integer)\n- Adding required fields to requests\n- Changing response structure\n- Removing endpoints\n- Changing HTTP status codes for same scenario\n- Changing authentication mechanisms\n\n### Non-Breaking Changes\n\nSafe changes that don't require a new version:\n- Adding new endpoints\n- Adding optional request fields\n- Adding new fields to responses (clients should ignore unknown fields)\n- Fixing bugs\n- Performance improvements\n- Adding new HTTP methods to existing resources\n\n## Versioning Strategies\n\n### 1. URI Versioning\n\nMost common and visible approach. Version is part of the URL path.\n\n```http\nGET /v1/users/123\nGET /v2/users/123\n```\n\n**Advantages:**\n- Clear and visible in URLs\n- Easy to understand and implement\n- Simple routing and caching\n- Can run multiple versions simultaneously\n\n**Disadvantages:**\n- Violates REST principle (same resource, different URIs)\n- Requires updating client code to change version\n- Can lead to URI proliferation\n\n**Implementation:**\n```\n/v1/users\n/v1/products\n/v2/users      # New version with breaking changes\n/v2/products\n```\n\n### 2. Header Versioning\n\nVersion specified in HTTP headers (Accept header or custom header).\n\n**Accept Header:**\n```http\nGET /users/123\nAccept: application/vnd.myapi.v1+json\n\nGET /users/123\nAccept: application/vnd.myapi.v2+json\n```\n\n**Custom Header:**\n```http\nGET /users/123\nAPI-Version: 1\n\nGET /users/123\nAPI-Version: 2\n```\n\n**Advantages:**\n- URIs remain stable\n- More RESTful (same resource, same URI)\n- Separates versioning from resource identification\n\n**Disadvantages:**\n- Less visible (harder to debug)\n- More complex routing\n- Difficult to test in browser\n- Cache complexity\n\n### 3. Query Parameter Versioning\n\nVersion specified as query parameter.\n\n```http\nGET /users/123?version=1\nGET /users/123?version=2\n\n# or\nGET /users/123?api-version=1\nGET /users/123?api-version=2\n```\n\n**Advantages:**\n- Simple to implement\n- Easy to test\n- Visible in URLs\n\n**Disadvantages:**\n- Pollutes query string\n- Not semantic (version not a filter)\n- Can interfere with other query params\n\n### 4. Content Negotiation\n\nClient specifies desired version through content negotiation.\n\n```http\nGET /users/123\nAccept: application/vnd.myapi+json; version=1\n\nGET /users/123\nAccept: application/vnd.myapi+json; version=2\n```\n\n**Advantages:**\n- Very RESTful\n- Flexible content type negotiation\n- Stable URIs\n\n**Disadvantages:**\n- Complex implementation\n- Less intuitive for developers\n- Harder to test\n\n## Recommended Approach\n\n**URI versioning is recommended for most APIs** because:\n- It's the most explicit and discoverable\n- Easy to understand and debug\n- Simple to implement and maintain\n- Clear separation between versions\n\n```\n/v1/users\n/v2/users\n/v3/users\n```\n\n## Version Format\n\n### Major Versions Only\n\nUse simple major versions (v1, v2, v3) for public APIs:\n```\n/v1/users\n/v2/users\n```\n\n**Advantages:**\n- Simple and clear\n- Easy to communicate\n- Forces thoughtful breaking changes\n\n### Date-Based Versions\n\nSome APIs use dates for versions:\n```\n/2024-01-01/users\n/2024-06-15/users\n```\n\n**Used by:** Stripe, GitHub API\n\n**Advantages:**\n- Clear when version was released\n- Easy to understand timeline\n- No confusion about major/minor\n\n**Disadvantages:**\n- Less intuitive for clients\n- Harder to understand what changed\n\n## Version Lifecycle\n\n### 1. Introduction Phase\n\nNew version is released alongside existing version:\n```\n/v1/users  # Still supported\n/v2/users  # New version available\n```\n\nAnnounce new version:\n- Blog post explaining changes\n- Migration guide\n- Breaking changes list\n- Timeline for v1 deprecation\n\n### 2. Deprecation Phase\n\nMark old version as deprecated but keep it running:\n\n```http\nGET /v1/users/123\n\nResponse:\nDeprecation: true\nSunset: Wed, 15 Jan 2025 00:00:00 GMT\nLink: </v2/users/123>; rel=\"successor-version\"\n\n{\n  \"id\": 123,\n  \"name\": \"John Doe\"\n}\n```\n\n**Deprecation Headers:**\n- `Deprecation: true` - Indicates version is deprecated\n- `Sunset: <date>` - When version will be removed (RFC 8594)\n- `Link: <url>; rel=\"successor-version\"` - Points to new version\n\n### 3. Sunset Phase\n\nOld version is shut down on announced date.\n\nReturn 410 Gone for deprecated endpoints:\n```http\nGET /v1/users/123\n\nResponse: 410 Gone\n{\n  \"error\": {\n    \"code\": \"VERSION_SUNSET\",\n    \"message\": \"API v1 was sunset on 2025-01-15. Please use v2.\",\n    \"documentation_url\": \"https://api.example.com/docs/migration-v1-to-v2\"\n  }\n}\n```\n\n## Deprecation Policy\n\n### Recommended Timeline\n\n1. **Announce deprecation** - At least 6 months before sunset\n2. **Support period** - Run both versions for 6-12 months\n3. **Sunset date** - Clear date communicated in advance\n4. **Grace period** - 30 days of 410 Gone responses before complete shutdown\n\n### Communication Channels\n\n- API response headers\n- Email to registered developers\n- Blog posts and changelog\n- Dashboard notifications\n- Documentation updates\n- Status page announcements\n\n## Migration Strategy\n\n### Provide Migration Guide\n\n```markdown\n# Migrating from v1 to v2\n\n## Breaking Changes\n\n### User Resource Changes\n\n**v1:**\n```json\n{\n  \"id\": 123,\n  \"name\": \"John Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**v2:**\n```json\n{\n  \"id\": 123,\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"email\": \"john@example.com\"\n}\n```\n\n**Migration:**\n- Split `name` field into `first_name` and `last_name`\n- Update client code to use new fields\n```\n\n### Offer Tools\n\n- Migration scripts\n- SDK updates\n- API diff viewer\n- Compatibility layer (temporary)\n\n## Version Discovery\n\n### Root Endpoint\n\n```http\nGET /\n\nResponse:\n{\n  \"versions\": {\n    \"v1\": {\n      \"status\": \"deprecated\",\n      \"sunset_date\": \"2025-01-15\",\n      \"documentation_url\": \"https://api.example.com/docs/v1\"\n    },\n    \"v2\": {\n      \"status\": \"current\",\n      \"documentation_url\": \"https://api.example.com/docs/v2\"\n    },\n    \"v3\": {\n      \"status\": \"beta\",\n      \"documentation_url\": \"https://api.example.com/docs/v3\"\n    }\n  }\n}\n```\n\n### Version Info Endpoint\n\n```http\nGET /v2/version\n\nResponse:\n{\n  \"version\": \"v2\",\n  \"released\": \"2024-01-15\",\n  \"status\": \"stable\",\n  \"sunset_date\": null\n}\n```\n\n## OpenAPI Versioning\n\n### Separate Specs per Version\n\n```\nopenapi-v1.yaml\nopenapi-v2.yaml\nopenapi-v3.yaml\n```\n\nEach spec is complete and independent.\n\n### Single Spec with Servers\n\n```yaml\nopenapi: 3.1.0\ninfo:\n  title: My API\n  version: 2.0.0\nservers:\n  - url: https://api.example.com/v1\n    description: Version 1 (deprecated)\n  - url: https://api.example.com/v2\n    description: Version 2 (current)\n```\n\n## Best Practices\n\n1. **Version from day one** - Start with /v1, not /api\n2. **Major versions only** - Use v1, v2, v3 (not v1.1, v1.2)\n3. **Long deprecation periods** - Give clients time to migrate (6-12 months)\n4. **Clear communication** - Use headers, docs, emails\n5. **Maintain old versions** - Support at least 2 versions simultaneously\n6. **Document changes** - Provide detailed migration guides\n7. **Use semantic versioning** - For internal/SDK versioning\n8. **Never break without warning** - Always announce breaking changes\n9. **Provide tools** - Migration scripts, updated SDKs\n10. **Monitor usage** - Track which versions are being used\n\n## Anti-Patterns\n\nAvoid these mistakes:\n\n- **Breaking changes without version bump** - Breaks existing clients\n- **Too many versions** - Maintenance nightmare (max 2-3 active versions)\n- **Short deprecation periods** - Frustrates developers\n- **No migration path** - Makes upgrades painful\n- **Surprise sunsets** - Breaks production apps without warning\n- **Inconsistent versioning** - Different strategies for different endpoints\n- **Versioning individual endpoints** - Use consistent version across API\n\n<!-- File: skills/api-designer/SKILL.md (Tokens: ~1882 | Category: anthropic-skill) -->\n---\nname: api-designer\ndescription: Use when designing REST or GraphQL APIs, creating OpenAPI specifications, or planning API architecture. Invoke for resource modeling, versioning strategies, pagination patterns, error handling standards.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.0\"\n  domain: api-architecture\n  triggers: API design, REST API, OpenAPI, API specification, API architecture, resource modeling, API versioning, GraphQL schema, API documentation\n  role: architect\n  scope: design\n  output-format: specification\n  related-skills: graphql-architect, fastapi-expert, nestjs-expert, spring-boot-engineer, security-reviewer\n---\n\n# API Designer\n\nSenior API architect specializing in REST and GraphQL APIs with comprehensive OpenAPI 3.1 specifications.\n\n## Core Workflow\n\n1. **Analyze domain** — Understand business requirements, data models, and client needs\n2. **Model resources** — Identify resources, relationships, and operations; sketch entity diagram before writing any spec\n3. **Design endpoints** — Define URI patterns, HTTP methods, request/response schemas\n4. **Specify contract** — Create OpenAPI 3.1 spec; validate before proceeding: `npx @redocly/cli lint openapi.yaml`\n5. **Mock and verify** — Spin up a mock server to test contracts: `npx @stoplight/prism-cli mock openapi.yaml`\n6. **Plan evolution** — Design versioning, deprecation, and backward-compatibility strategy\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| REST Patterns | `references/rest-patterns.md` | Resource design, HTTP methods, HATEOAS |\n| Versioning | `references/versioning.md` | API versions, deprecation, breaking changes |\n| Pagination | `references/pagination.md` | Cursor, offset, keyset pagination |\n| Error Handling | `references/error-handling.md` | Error responses, RFC 7807, status codes |\n| OpenAPI | `references/openapi.md` | OpenAPI 3.1, documentation, code generation |\n\n## Constraints\n\n### MUST DO\n- Follow REST principles (resource-oriented, proper HTTP methods)\n- Use consistent naming conventions (snake_case or camelCase — pick one, apply everywhere)\n- Include comprehensive OpenAPI 3.1 specification\n- Design proper error responses with actionable messages (RFC 7807)\n- Implement pagination for all collection endpoints\n- Version APIs with clear deprecation policies\n- Document authentication and authorization\n- Provide request/response examples\n\n### MUST NOT DO\n- Use verbs in resource URIs (use `/users/{id}`, not `/getUser/{id}`)\n- Return inconsistent response structures\n- Skip error code documentation\n- Ignore HTTP status code semantics\n- Design APIs without a versioning strategy\n- Expose implementation details in the API surface\n- Create breaking changes without a migration path\n- Omit rate limiting considerations\n\n## Templates\n\n### OpenAPI 3.1 Resource Endpoint (copy-paste starter)\n\n```yaml\nopenapi: \"3.1.0\"\ninfo:\n  title: Example API\n  version: \"1.1.0\"\npaths:\n  /users:\n    get:\n      summary: List users\n      operationId: listUsers\n      tags: [Users]\n      parameters:\n        - name: cursor\n          in: query\n          schema: { type: string }\n          description: Opaque cursor for pagination\n        - name: limit\n          in: query\n          schema: { type: integer, default: 20, maximum: 100 }\n      responses:\n        \"200\":\n          description: Paginated list of users\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [data, pagination]\n                properties:\n                  data:\n                    type: array\n                    items: { $ref: \"#/components/schemas/User\" }\n                  pagination:\n                    $ref: \"#/components/schemas/CursorPage\"\n        \"400\": { $ref: \"#/components/responses/BadRequest\" }\n        \"401\": { $ref: \"#/components/responses/Unauthorized\" }\n        \"429\": { $ref: \"#/components/responses/TooManyRequests\" }\n  /users/{id}:\n    get:\n      summary: Get a user\n      operationId: getUser\n      tags: [Users]\n      parameters:\n        - name: id\n          in: path\n          required: true\n          schema: { type: string, format: uuid }\n      responses:\n        \"200\":\n          description: User found\n          content:\n            application/json:\n              schema: { $ref: \"#/components/schemas/User\" }\n        \"404\": { $ref: \"#/components/responses/NotFound\" }\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      required: [id, email, created_at]\n      properties:\n        id:    { type: string, format: uuid, readOnly: true }\n        email: { type: string, format: email }\n        name:  { type: string }\n        created_at: { type: string, format: date-time, readOnly: true }\n\n    CursorPage:\n      type: object\n      required: [next_cursor, has_more]\n      properties:\n        next_cursor: { type: string, nullable: true }\n        has_more:    { type: boolean }\n\n    Problem:                       # RFC 7807 Problem Details\n      type: object\n      required: [type, title, status]\n      properties:\n        type:     { type: string, format: uri, example: \"https://api.example.com/errors/validation-error\" }\n        title:    { type: string, example: \"Validation Error\" }\n        status:   { type: integer, example: 400 }\n        detail:   { type: string, example: \"The 'email' field must be a valid email address.\" }\n        instance: { type: string, format: uri, example: \"/users/req-abc123\" }\n\n  responses:\n    BadRequest:\n      description: Invalid request parameters\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    Unauthorized:\n      description: Missing or invalid authentication\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    NotFound:\n      description: Resource not found\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n    TooManyRequests:\n      description: Rate limit exceeded\n      headers:\n        Retry-After: { schema: { type: integer } }\n      content:\n        application/problem+json:\n          schema: { $ref: \"#/components/schemas/Problem\" }\n\n  securitySchemes:\n    BearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\nsecurity:\n  - BearerAuth: []\n```\n\n### RFC 7807 Error Response (copy-paste)\n\n```json\n{\n  \"type\": \"https://api.example.com/errors/validation-error\",\n  \"title\": \"Validation Error\",\n  \"status\": 422,\n  \"detail\": \"The 'email' field must be a valid email address.\",\n  \"instance\": \"/users/req-abc123\",\n  \"errors\": [\n    { \"field\": \"email\", \"message\": \"Must be a valid email address.\" }\n  ]\n}\n```\n\n- Always use `Content-Type: application/problem+json` for error responses.\n- `type` must be a stable, documented URI — never a generic string.\n- `detail` must be human-readable and actionable.\n- Extend with `errors[]` for field-level validation failures.\n\n## Output Checklist\n\nWhen delivering an API design, provide:\n1. Resource model and relationships (diagram or table)\n2. Endpoint specifications with URIs and HTTP methods\n3. OpenAPI 3.1 specification (YAML)\n4. Authentication and authorization flows\n5. Error response catalog (all 4xx/5xx with `type` URIs)\n6. Pagination and filtering patterns\n7. Versioning and deprecation strategy\n8. Validation result: `npx @redocly/cli lint openapi.yaml` passes with no errors\n\n## Knowledge Reference\n\nREST architecture, OpenAPI 3.1, GraphQL, HTTP semantics, JSON:API, HATEOAS, OAuth 2.0, JWT, RFC 7807 Problem Details, API versioning patterns, pagination strategies, rate limiting, webhook design, SDK generation\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/api-designer/)\n\n<!-- File: skills/architecture-designer/references/adr-template.md (Tokens: ~637 | Category: anthropic-skill) -->\n# ADR Template\n\n## ADR Format\n\n```markdown\n# ADR-{number}: {Title}\n\n## Status\n[Proposed | Accepted | Deprecated | Superseded by ADR-XXX]\n\n## Context\n[Describe the situation and forces at play. What is the problem?\nWhat constraints exist? What are we trying to achieve?]\n\n## Decision\n[State the decision clearly. What are we going to do?]\n\n## Consequences\n\n### Positive\n- [Benefit 1]\n- [Benefit 2]\n\n### Negative\n- [Drawback 1]\n- [Drawback 2]\n\n### Neutral\n- [Side effect that is neither good nor bad]\n\n## Alternatives Considered\n[What other options were evaluated and why were they rejected?]\n\n## References\n- [Link to relevant documentation]\n- [Link to discussion/RFC]\n```\n\n## Example: Database Selection\n\n```markdown\n# ADR-001: Use PostgreSQL for primary database\n\n## Status\nAccepted\n\n## Context\nWe need a relational database for our e-commerce platform that:\n- Handles complex transactions with strong consistency\n- Supports JSON for flexible product attributes\n- Scales to millions of products and orders\n- Works well with our existing Python/Node stack\n\nTeam has experience with PostgreSQL and MySQL.\nBudget allows for managed database service.\n\n## Decision\nUse PostgreSQL as the primary database, hosted on AWS RDS.\n\n## Consequences\n\n### Positive\n- ACID compliance for financial transactions\n- Rich feature set (JSON, full-text search, CTEs)\n- Strong community and tooling\n- Excellent performance with proper indexing\n- Free and open source\n\n### Negative\n- Vertical scaling has limits (addressed with read replicas)\n- Requires DBA expertise for optimization\n- AWS RDS costs for high availability\n\n### Neutral\n- Team will need to learn PostgreSQL-specific features\n- Migration from current SQLite dev database needed\n\n## Alternatives Considered\n\n**MySQL**\n- Rejected: Less feature-rich for JSON operations\n- Considered: Similar cost, familiar to team\n\n**MongoDB**\n- Rejected: Relational data model needed for orders/inventory\n- Considered: Great for product catalog flexibility\n\n**CockroachDB**\n- Rejected: Higher cost, team unfamiliar\n- Considered: Better horizontal scaling\n\n## References\n- https://www.postgresql.org/docs/current/\n- Internal RFC: Database Selection for E-commerce Platform\n```\n\n## ADR Naming Convention\n\n```\ndocs/\n└── adr/\n    ├── 0001-use-postgresql-database.md\n    ├── 0002-adopt-microservices.md\n    ├── 0003-implement-event-sourcing.md\n    └── README.md\n```\n\n## Quick Reference\n\n| Section | Purpose | Key Question |\n|---------|---------|--------------|\n| Status | Current state | Is this active? |\n| Context | Background | Why are we deciding? |\n| Decision | The choice | What did we choose? |\n| Consequences | Impact | What happens now? |\n| Alternatives | Options | What else was considered? |\n\n<!-- File: skills/architecture-designer/references/architecture-patterns.md (Tokens: ~930 | Category: anthropic-skill) -->\n# Architecture Patterns\n\n## Pattern Comparison\n\n| Pattern | Best For | Team Size | Trade-offs |\n|---------|----------|-----------|------------|\n| **Monolith** | Simple domain, small team | 1-10 | Simple deploy; hard to scale parts |\n| **Modular Monolith** | Growing complexity | 5-20 | Module boundaries; still single deploy |\n| **Microservices** | Complex domain, large org | 20+ | Independent scale; operational complexity |\n| **Serverless** | Variable load, event-driven | Any | Auto-scale; cold starts, vendor lock |\n| **Event-Driven** | Async processing | 10+ | Loose coupling; debugging complexity |\n\n## Monolith\n\n```\n┌─────────────────────────────────────┐\n│            Application              │\n│  ┌─────┐  ┌─────┐  ┌─────┐         │\n│  │Users│  │Orders│ │Products│       │\n│  └─────┘  └─────┘  └─────┘         │\n│  └──────────┬──────────────┘        │\n│          Database                    │\n└─────────────────────────────────────┘\n```\n\n**When to Use**:\n- Starting a new project\n- Small team (< 10 developers)\n- Simple domain\n- Rapid iteration needed\n\n**Pros**: Simple deployment, easy debugging, no network latency\n**Cons**: Hard to scale independently, technology locked, deployment risk\n\n## Microservices\n\n```\n┌──────────┐  ┌──────────┐  ┌──────────┐\n│  Users   │  │  Orders  │  │ Products │\n│ Service  │  │ Service  │  │ Service  │\n└────┬─────┘  └────┬─────┘  └────┬─────┘\n     │             │             │\n┌────▼────┐  ┌────▼────┐  ┌────▼────┐\n│ User DB │  │Order DB │  │ Prod DB │\n└─────────┘  └─────────┘  └─────────┘\n```\n\n**When to Use**:\n- Large team (20+ developers)\n- Complex domain with clear boundaries\n- Different scaling requirements per service\n- Polyglot technology needs\n\n**Pros**: Independent scaling, team autonomy, fault isolation\n**Cons**: Distributed system complexity, eventual consistency, operational overhead\n\n## Event-Driven\n\n```\n┌──────────┐     ┌─────────────┐     ┌──────────┐\n│ Producer │────▶│ Message Bus │────▶│ Consumer │\n└──────────┘     │  (Kafka)    │     └──────────┘\n                 └─────────────┘\n                       │\n                       ▼\n                 ┌──────────┐\n                 │ Consumer │\n                 └──────────┘\n```\n\n**When to Use**:\n- Async processing required\n- Loose coupling between services\n- Event sourcing needs\n- High throughput messaging\n\n**Pros**: Decoupled services, scalable, audit trail\n**Cons**: Eventual consistency, debugging complexity, message ordering\n\n## CQRS (Command Query Responsibility Segregation)\n\n```\n┌─────────┐         ┌─────────────┐\n│ Commands│────────▶│ Write Model │──┐\n└─────────┘         └─────────────┘  │\n                                     ▼\n                              ┌──────────┐\n                              │  Events  │\n                              └──────────┘\n                                     │\n┌─────────┐         ┌─────────────┐  │\n│ Queries │◀────────│ Read Model  │◀─┘\n└─────────┘         └─────────────┘\n```\n\n**When to Use**:\n- Read/write ratio heavily skewed\n- Complex read queries\n- Event sourcing architecture\n- Different optimization needs\n\n## Quick Reference\n\n| Requirement | Recommended Pattern |\n|-------------|---------------------|\n| Simple CRUD app | Monolith |\n| Growing startup | Modular Monolith |\n| Enterprise scale | Microservices |\n| Variable load | Serverless |\n| Async processing | Event-Driven |\n| Read-heavy | CQRS |\n\n<!-- File: skills/architecture-designer/references/database-selection.md (Tokens: ~597 | Category: anthropic-skill) -->\n# Database Selection\n\n## Database Types\n\n| Type | Examples | Best For |\n|------|----------|----------|\n| **Relational** | PostgreSQL, MySQL | Transactions, complex queries, relationships |\n| **Document** | MongoDB, Firestore | Flexible schemas, rapid iteration |\n| **Key-Value** | Redis, DynamoDB | Caching, sessions, high throughput |\n| **Time-Series** | TimescaleDB, InfluxDB | Metrics, IoT, analytics |\n| **Graph** | Neo4j, Neptune | Relationships, social networks |\n| **Search** | Elasticsearch, Meilisearch | Full-text search, logs |\n\n## Relational (PostgreSQL, MySQL)\n\n```\nBest For:\n- Financial transactions (ACID compliance)\n- Complex queries with joins\n- Data integrity requirements\n- Structured, predictable schemas\n\nWhen to Avoid:\n- Highly variable schemas\n- Massive horizontal scaling needs\n- Simple key-value access patterns\n```\n\n| Feature | PostgreSQL | MySQL |\n|---------|------------|-------|\n| JSON support | Excellent (JSONB) | Good (JSON) |\n| Full-text search | Built-in | Basic |\n| Extensions | Rich ecosystem | Limited |\n| Replication | Streaming, logical | Statement, row-based |\n\n## Document (MongoDB, Firestore)\n\n```\nBest For:\n- Flexible, evolving schemas\n- Hierarchical data (nested documents)\n- Rapid prototyping\n- Content management\n\nWhen to Avoid:\n- Complex transactions across documents\n- Heavy relational queries\n- Strict schema requirements\n```\n\n## Key-Value (Redis, DynamoDB)\n\n```\nBest For:\n- Session storage\n- Caching layer\n- Real-time leaderboards\n- Rate limiting counters\n\nWhen to Avoid:\n- Complex queries\n- Relational data\n- Large value sizes (>1MB)\n```\n\n## Time-Series (TimescaleDB, InfluxDB)\n\n```\nBest For:\n- Metrics and monitoring\n- IoT sensor data\n- Financial tick data\n- Event logging with timestamps\n\nWhen to Avoid:\n- Frequent updates to existing records\n- Complex relational queries\n- Non-time-based access patterns\n```\n\n## Decision Matrix\n\n| Requirement | Recommended |\n|-------------|-------------|\n| ACID transactions | PostgreSQL, MySQL |\n| Flexible schema | MongoDB, Firestore |\n| High-speed caching | Redis |\n| Time-series data | TimescaleDB, InfluxDB |\n| Social relationships | Neo4j |\n| Full-text search | Elasticsearch |\n| Serverless scale | DynamoDB, Firestore |\n\n## Quick Reference\n\n| Question | If Yes → |\n|----------|----------|\n| Need ACID transactions? | Relational (PostgreSQL) |\n| Schema changes frequently? | Document (MongoDB) |\n| Sub-millisecond reads? | Key-Value (Redis) |\n| Time-based queries? | Time-Series |\n| Traversing relationships? | Graph (Neo4j) |\n| Full-text search primary? | Elasticsearch |\n\n<!-- File: skills/architecture-designer/references/nfr-checklist.md (Tokens: ~860 | Category: anthropic-skill) -->\n# Non-Functional Requirements Checklist\n\n## NFR Categories\n\n### Scalability\n\n| Question | Common Targets |\n|----------|----------------|\n| Expected concurrent users? | 100 / 1K / 10K / 100K |\n| Requests per second? | 10 / 100 / 1000 / 10000 |\n| Data volume? | GB / TB / PB |\n| Growth rate? | 10% / 50% / 100% per year |\n| Peak vs average load? | 2x / 5x / 10x |\n\n### Performance\n\n| Question | Common Targets |\n|----------|----------------|\n| API response time? | < 100ms / 200ms / 500ms p95 |\n| Page load time? | < 1s / 2s / 3s |\n| Database query time? | < 10ms / 50ms / 100ms |\n| Batch processing throughput? | 1K / 10K / 100K records/hour |\n\n### Availability\n\n| Target | Downtime/Year | Use Case |\n|--------|---------------|----------|\n| 99% | 3.65 days | Internal tools |\n| 99.9% | 8.76 hours | Business apps |\n| 99.95% | 4.38 hours | E-commerce |\n| 99.99% | 52.6 minutes | Financial systems |\n| 99.999% | 5.26 minutes | Life-critical |\n\n### Security\n\n| Question | Considerations |\n|----------|----------------|\n| Authentication required? | JWT, OAuth, SAML, MFA |\n| Authorization model? | RBAC, ABAC, ACL |\n| Data sensitivity? | Public, internal, confidential, PII |\n| Compliance requirements? | GDPR, HIPAA, PCI DSS, SOC 2 |\n| Encryption needs? | At rest, in transit, end-to-end |\n\n### Reliability\n\n| Question | Considerations |\n|----------|----------------|\n| Acceptable data loss? | RPO: 0 / 1hr / 24hr |\n| Recovery time target? | RTO: 1hr / 4hr / 24hr |\n| Backup frequency? | Real-time / hourly / daily |\n| Disaster recovery? | Single region / multi-region |\n\n### Maintainability\n\n| Question | Considerations |\n|----------|----------------|\n| Deployment frequency? | Daily / weekly / monthly |\n| Deployment strategy? | Blue-green, canary, rolling |\n| Monitoring requirements? | Logs, metrics, traces, alerts |\n| On-call requirements? | 24/7, business hours |\n\n### Cost\n\n| Question | Considerations |\n|----------|----------------|\n| Infrastructure budget? | $/month, $/user, $/request |\n| Operational budget? | FTE for maintenance |\n| Cost optimization? | Reserved instances, spot instances |\n| Cost alerts? | Thresholds for notification |\n\n## Template\n\n```markdown\n## Non-Functional Requirements\n\n### Performance\n- API response time: < 200ms p95\n- Page load time: < 2s\n- Database query time: < 50ms\n\n### Scalability\n- Concurrent users: 10,000\n- Requests per second: 1,000\n- Data volume: 1TB\n\n### Availability\n- Target: 99.9% (8.76 hours/year downtime)\n- RPO: 1 hour\n- RTO: 4 hours\n\n### Security\n- Authentication: JWT with refresh tokens\n- Authorization: Role-based (admin, user, guest)\n- Compliance: GDPR, SOC 2\n\n### Observability\n- Logging: Structured JSON to ELK\n- Metrics: Prometheus + Grafana\n- Tracing: OpenTelemetry\n- Alerts: PagerDuty integration\n```\n\n## Quick Reference\n\n| Category | Key Metric |\n|----------|------------|\n| Performance | Response time (p95) |\n| Scalability | Concurrent users, RPS |\n| Availability | Uptime percentage |\n| Reliability | RPO, RTO |\n| Security | Compliance requirements |\n| Cost | $/month budget |\n\n<!-- File: skills/architecture-designer/references/system-design.md (Tokens: ~701 | Category: anthropic-skill) -->\n# System Design Template\n\n## Design Template\n\n```markdown\n# System: {System Name}\n\n## Requirements\n\n### Functional\n- [What the system must do]\n- [Core features and capabilities]\n\n### Non-Functional\n- **Performance**: Response time < 200ms p95\n- **Availability**: 99.9% uptime (8.76 hours downtime/year)\n- **Scalability**: Support 10,000 concurrent users\n- **Security**: PCI DSS compliance required\n\n### Constraints\n- Budget: $X/month for infrastructure\n- Timeline: MVP in 3 months\n- Team: 5 backend, 3 frontend engineers\n\n## High-Level Architecture\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│   Client    │────▶│  API Gateway │────▶│  Service    │\n│   (Web)     │     │   (Kong)    │     │  (Node.js)  │\n└─────────────┘     └─────────────┘     └─────────────┘\n                           │                   │\n                           ▼                   ▼\n                    ┌─────────────┐     ┌─────────────┐\n                    │    Auth     │     │  Database   │\n                    │  (Auth0)    │     │ (PostgreSQL)│\n                    └─────────────┘     └─────────────┘\n```\n\n## Component Details\n\n### API Layer\n- Technology: Node.js with Express/NestJS\n- Responsibilities: Request routing, validation, auth\n- Scaling: Horizontal via load balancer\n\n### Data Layer\n- Primary: PostgreSQL (transactions, relationships)\n- Cache: Redis (sessions, hot data)\n- Storage: S3 (files, images)\n\n### External Services\n- Auth: Auth0 (SSO, MFA)\n- Email: SendGrid (transactional)\n- Monitoring: Datadog (APM, logs)\n\n## Key Decisions\n\n| Decision | Rationale |\n|----------|-----------|\n| PostgreSQL over MongoDB | Relational data, ACID needed |\n| Redis for caching | Sub-ms latency required |\n| Auth0 over custom | Reduce security risk |\n\n## Scaling Strategy\n\n### Current (MVP)\n- Single region deployment\n- 2 API instances behind ALB\n- Single RDS instance\n\n### Future (10x growth)\n- Multi-region with CDN\n- Auto-scaling API (2-10 instances)\n- RDS read replicas\n\n## Security Considerations\n- All traffic over TLS 1.3\n- JWT tokens with 15-min expiry\n- Rate limiting: 100 req/min per user\n- WAF for common attacks\n\n## Failure Modes\n\n| Failure | Impact | Mitigation |\n|---------|--------|------------|\n| DB down | Full outage | Multi-AZ failover |\n| Cache down | Degraded perf | Fallback to DB |\n| Auth down | No new logins | Cache valid tokens |\n```\n\n## Quick Reference\n\n| Section | Key Questions |\n|---------|---------------|\n| Requirements | What must it do? How well? |\n| Architecture | What components? How connected? |\n| Decisions | Why these choices? |\n| Scaling | How to grow? |\n| Failures | What can break? How to recover? |\n\n<!-- File: skills/architecture-designer/SKILL.md (Tokens: ~999 | Category: anthropic-skill) -->\n---\nname: architecture-designer\ndescription: Use when designing new high-level system architecture, reviewing existing designs, or making architectural decisions. Invoke to create architecture diagrams, write Architecture Decision Records (ADRs), evaluate technology trade-offs, design component interactions, and plan for scalability. Use for system design, architecture review, microservices structuring, ADR authoring, scalability planning, and infrastructure pattern selection — distinct from code-level design patterns or database-only design tasks.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.1.1\"\n  domain: api-architecture\n  triggers: architecture, system design, design pattern, microservices, scalability, ADR, technical design, infrastructure\n  role: expert\n  scope: design\n  output-format: document\n  related-skills: fullstack-guardian, devops-engineer, secure-code-guardian, microservices-architect, code-reviewer\n---\n\n# Architecture Designer\n\nSenior software architect specializing in system design, design patterns, and architectural decision-making.\n\n## Role Definition\n\nYou are a principal architect with 15+ years of experience designing scalable, distributed systems. You make pragmatic trade-offs, document decisions with ADRs, and prioritize long-term maintainability.\n\n## When to Use This Skill\n\n- Designing new system architecture\n- Choosing between architectural patterns\n- Reviewing existing architecture\n- Creating Architecture Decision Records (ADRs)\n- Planning for scalability\n- Evaluating technology choices\n\n## Core Workflow\n\n1. **Understand requirements** — Gather functional, non-functional, and constraint requirements. _Verify full requirements coverage before proceeding._\n2. **Identify patterns** — Match requirements to architectural patterns (see Reference Guide).\n3. **Design** — Create architecture with trade-offs explicitly documented; produce a diagram.\n4. **Document** — Write ADRs for all key decisions.\n5. **Review** — Validate with stakeholders. _If review fails, return to step 3 with recorded feedback._\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Architecture Patterns | `references/architecture-patterns.md` | Choosing monolith vs microservices |\n| ADR Template | `references/adr-template.md` | Documenting decisions |\n| System Design | `references/system-design.md` | Full system design template |\n| Database Selection | `references/database-selection.md` | Choosing database technology |\n| NFR Checklist | `references/nfr-checklist.md` | Gathering non-functional requirements |\n\n## Constraints\n\n### MUST DO\n- Document all significant decisions with ADRs\n- Consider non-functional requirements explicitly\n- Evaluate trade-offs, not just benefits\n- Plan for failure modes\n- Consider operational complexity\n- Review with stakeholders before finalizing\n\n### MUST NOT DO\n- Over-engineer for hypothetical scale\n- Choose technology without evaluating alternatives\n- Ignore operational costs\n- Design without understanding requirements\n- Skip security considerations\n\n## Output Templates\n\nWhen designing architecture, provide:\n1. Requirements summary (functional + non-functional)\n2. High-level architecture diagram (Mermaid preferred — see example below)\n3. Key decisions with trade-offs (ADR format — see example below)\n4. Technology recommendations with rationale\n5. Risks and mitigation strategies\n\n### Architecture Diagram (Mermaid)\n\n```mermaid\ngraph TD\n    Client[\"Client (Web/Mobile)\"] --> Gateway[\"API Gateway\"]\n    Gateway --> AuthSvc[\"Auth Service\"]\n    Gateway --> OrderSvc[\"Order Service\"]\n    OrderSvc --> DB[(\"Orders DB\\n(PostgreSQL)\")]\n    OrderSvc --> Queue[\"Message Queue\\n(RabbitMQ)\"]\n    Queue --> NotifySvc[\"Notification Service\"]\n```\n\n### ADR Example\n\n```markdown\n# ADR-001: Use PostgreSQL for Order Storage\n\n## Status\nAccepted\n\n## Context\nThe Order Service requires ACID-compliant transactions and complex relational queries\nacross orders, line items, and customers.\n\n## Decision\nUse PostgreSQL as the primary datastore for the Order Service.\n\n## Alternatives Considered\n- **MongoDB** — flexible schema, but lacks strong ACID guarantees across documents.\n- **DynamoDB** — excellent scalability, but complex query patterns require denormalization.\n\n## Consequences\n- Positive: Strong consistency, mature tooling, complex query support.\n- Negative: Vertical scaling limits; horizontal sharding adds operational complexity.\n\n## Trade-offs\nConsistency and query flexibility are prioritised over unlimited horizontal write scalability.\n```\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/api-architecture/architecture-designer/)\n\n<!-- File: skills/atlassian-mcp/references/authentication-patterns.md (Tokens: ~3115 | Category: anthropic-skill) -->\n# Authentication Patterns\n\n---\n\n## Authentication Methods Overview\n\n| Method | Platform | Use Case | Security Level |\n|--------|----------|----------|----------------|\n| OAuth 2.1 | Cloud | User-facing apps, integrations | Highest |\n| API Token | Cloud | Personal automation, scripts | Medium |\n| PAT | Server/DC | Server integrations | Medium |\n| Basic Auth | Legacy | Deprecated, avoid | Low |\n\n## OAuth 2.1 (Atlassian Cloud)\n\n### Authorization Code Flow\n\nFor applications that act on behalf of users.\n\n**Step 1: Register Your App**\n\n1. Go to [developer.atlassian.com](https://developer.atlassian.com/console/myapps/)\n2. Create new app\n3. Configure OAuth 2.0 (3LO)\n4. Add callback URL\n5. Request necessary scopes\n\n**Step 2: Authorization Request**\n\n```typescript\nconst authUrl = new URL('https://auth.atlassian.com/authorize');\nauthUrl.searchParams.set('audience', 'api.atlassian.com');\nauthUrl.searchParams.set('client_id', CLIENT_ID);\nauthUrl.searchParams.set('scope', 'read:jira-work write:jira-work read:confluence-content.all write:confluence-content');\nauthUrl.searchParams.set('redirect_uri', REDIRECT_URI);\nauthUrl.searchParams.set('state', generateState());\nauthUrl.searchParams.set('response_type', 'code');\nauthUrl.searchParams.set('prompt', 'consent');\n\n// Redirect user to authUrl.toString()\n```\n\n**Step 3: Exchange Code for Token**\n\n```typescript\nasync function exchangeCodeForToken(code: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'authorization_code',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      code,\n      redirect_uri: REDIRECT_URI,\n    }),\n  });\n\n  if (!response.ok) {\n    throw new Error(`Token exchange failed: ${response.statusText}`);\n  }\n\n  return response.json();\n}\n\ninterface TokenResponse {\n  access_token: string;\n  refresh_token: string;\n  expires_in: number;\n  scope: string;\n  token_type: 'Bearer';\n}\n```\n\n**Step 4: Refresh Token**\n\n```typescript\nasync function refreshAccessToken(refreshToken: string): Promise<TokenResponse> {\n  const response = await fetch('https://auth.atlassian.com/oauth/token', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      grant_type: 'refresh_token',\n      client_id: CLIENT_ID,\n      client_secret: CLIENT_SECRET,\n      refresh_token: refreshToken,\n    }),\n  });\n\n  return response.json();\n}\n```\n\n**Step 5: Get Accessible Resources**\n\n```typescript\nasync function getAccessibleResources(accessToken: string): Promise<Resource[]> {\n  const response = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    {\n      headers: {\n        Authorization: `Bearer ${accessToken}`,\n        Accept: 'application/json',\n      },\n    }\n  );\n\n  return response.json();\n}\n\ninterface Resource {\n  id: string;        // Cloud ID\n  name: string;      // Site name\n  url: string;       // https://your-site.atlassian.net\n  scopes: string[];\n  avatarUrl: string;\n}\n```\n\n### OAuth Scopes Reference\n\n**Jira Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:jira-work` | Read issues, projects, boards |\n| `write:jira-work` | Create/update issues |\n| `manage:jira-project` | Manage project settings |\n| `manage:jira-configuration` | Manage global settings |\n| `read:jira-user` | Read user profiles |\n| `manage:jira-data-provider` | Data provider integrations |\n\n**Confluence Scopes:**\n\n| Scope | Description |\n|-------|-------------|\n| `read:confluence-content.all` | Read all content |\n| `write:confluence-content` | Create/update content |\n| `read:confluence-content.summary` | Read content summaries |\n| `read:confluence-space.summary` | Read space summaries |\n| `write:confluence-space` | Create/manage spaces |\n| `read:confluence-user` | Read user profiles |\n\n**Granular Scopes (v2):**\n\n```\nread:issue-details:jira\nwrite:issue:jira\nread:sprint:jira-software\nwrite:sprint:jira-software\nread:page:confluence\nwrite:page:confluence\nread:comment:confluence\nwrite:comment:confluence\n```\n\n## API Tokens (Atlassian Cloud)\n\n### Creating API Token\n\n1. Go to [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens)\n2. Click \"Create API token\"\n3. Give it a descriptive label\n4. Copy token immediately (shown only once)\n\n### Using API Token\n\n```typescript\n// Basic authentication with API token\nconst credentials = Buffer.from(`${email}:${apiToken}`).toString('base64');\n\nconst response = await fetch('https://your-site.atlassian.net/rest/api/3/myself', {\n  headers: {\n    Authorization: `Basic ${credentials}`,\n    Accept: 'application/json',\n  },\n});\n\n// For MCP server configuration\nconst config = {\n  JIRA_URL: 'https://your-site.atlassian.net',\n  JIRA_USERNAME: 'your-email@company.com',\n  JIRA_API_TOKEN: '<jira-api-token>',\n};\n```\n\n### Token Security Best Practices\n\n```typescript\n// Store tokens securely\nimport { SecretManagerServiceClient } from '@google-cloud/secret-manager';\nimport { SecretsManager } from '@aws-sdk/client-secrets-manager';\n\n// Option 1: Environment variables (development only)\nconst token = process.env.ATLASSIAN_API_TOKEN;\n\n// Option 2: GCP Secret Manager\nasync function getTokenFromGCP(secretName: string): Promise<string> {\n  const client = new SecretManagerServiceClient();\n  const [version] = await client.accessSecretVersion({\n    name: `projects/my-project/secrets/${secretName}/versions/latest`,\n  });\n  return version.payload?.data?.toString() || '';\n}\n\n// Option 3: AWS Secrets Manager\nasync function getTokenFromAWS(secretName: string): Promise<string> {\n  const client = new SecretsManager({ region: 'us-east-1' });\n  const response = await client.getSecretValue({ SecretId: secretName });\n  return response.SecretString || '';\n}\n\n// Option 4: HashiCorp Vault\nasync function getTokenFromVault(path: string): Promise<string> {\n  const response = await fetch(`${VAULT_ADDR}/v1/${path}`, {\n    headers: { 'X-Vault-Token': VAULT_TOKEN },\n  });\n  const data = await response.json();\n  return data.data.data.token;\n}\n```\n\n## Personal Access Tokens (Server/Data Center)\n\n### Creating PAT\n\n**Jira Server/DC:**\n1. Profile > Personal Access Tokens\n2. Create token\n3. Set expiration date\n4. Select permissions\n\n**Confluence Server/DC:**\n1. Profile > Settings > Personal Access Tokens\n2. Create token\n3. Configure permissions\n\n### Using PAT\n\n```typescript\n// Bearer token authentication\nconst response = await fetch('https://jira.internal.company.com/rest/api/2/myself', {\n  headers: {\n    Authorization: `Bearer ${personalAccessToken}`,\n    Accept: 'application/json',\n  },\n});\n\n// MCP server configuration\nconst config = {\n  JIRA_URL: 'https://jira.internal.company.com',\n  JIRA_PERSONAL_TOKEN: '<jira-personal-access-token>',\n};\n```\n\n### PAT Permissions\n\n| Permission | Jira | Confluence |\n|------------|------|------------|\n| Read | Browse projects, view issues | View pages |\n| Write | Create/edit issues | Create/edit pages |\n| Admin | Project administration | Space administration |\n\n## Token Management\n\n### Token Lifecycle Manager\n\n```typescript\ninterface TokenInfo {\n  accessToken: string;\n  refreshToken?: string;\n  expiresAt: Date;\n  scopes: string[];\n}\n\nclass TokenManager {\n  private tokenInfo: TokenInfo | null = null;\n  private refreshThreshold = 5 * 60 * 1000; // 5 minutes\n\n  async getValidToken(): Promise<string> {\n    if (!this.tokenInfo) {\n      throw new Error('Not authenticated');\n    }\n\n    // Check if token needs refresh\n    const timeUntilExpiry = this.tokenInfo.expiresAt.getTime() - Date.now();\n\n    if (timeUntilExpiry < this.refreshThreshold) {\n      await this.refreshToken();\n    }\n\n    return this.tokenInfo.accessToken;\n  }\n\n  private async refreshToken(): Promise<void> {\n    if (!this.tokenInfo?.refreshToken) {\n      throw new Error('No refresh token available');\n    }\n\n    const response = await refreshAccessToken(this.tokenInfo.refreshToken);\n\n    this.tokenInfo = {\n      accessToken: response.access_token,\n      refreshToken: response.refresh_token || this.tokenInfo.refreshToken,\n      expiresAt: new Date(Date.now() + response.expires_in * 1000),\n      scopes: response.scope.split(' '),\n    };\n  }\n\n  isAuthenticated(): boolean {\n    return this.tokenInfo !== null && this.tokenInfo.expiresAt > new Date();\n  }\n\n  getScopes(): string[] {\n    return this.tokenInfo?.scopes || [];\n  }\n\n  hasScope(scope: string): boolean {\n    return this.getScopes().includes(scope);\n  }\n}\n```\n\n### Token Rotation Strategy\n\n```typescript\nclass TokenRotationManager {\n  private rotationInterval = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n  async checkAndRotate(tokenCreatedAt: Date): Promise<boolean> {\n    const age = Date.now() - tokenCreatedAt.getTime();\n\n    if (age > this.rotationInterval) {\n      console.warn('API token is due for rotation');\n      return true;\n    }\n\n    return false;\n  }\n\n  async sendRotationReminder(email: string, tokenLabel: string): Promise<void> {\n    // Integrate with your notification system\n    await sendEmail({\n      to: email,\n      subject: 'Atlassian API Token Rotation Reminder',\n      body: `Your API token \"${tokenLabel}\" is due for rotation.\n             Please create a new token and update your integrations.`,\n    });\n  }\n}\n```\n\n## Permission Verification\n\n### Check Current Permissions\n\n```typescript\nasync function verifyPermissions(\n  client: MCPClient,\n  requiredOperations: string[]\n): Promise<PermissionReport> {\n  const report: PermissionReport = {\n    hasAllPermissions: true,\n    details: [],\n  };\n\n  for (const operation of requiredOperations) {\n    try {\n      switch (operation) {\n        case 'read:jira':\n          await client.callTool({\n            name: 'jira_get_issue',\n            arguments: { issue_key: 'TEST-1' },\n          });\n          break;\n        case 'write:jira':\n          // Create and immediately delete a test issue\n          const created = await client.callTool({\n            name: 'jira_create_issue',\n            arguments: {\n              project_key: 'TEST',\n              issue_type: 'Task',\n              summary: '[Permission Test] Delete me',\n            },\n          });\n          // Clean up\n          await client.callTool({\n            name: 'jira_delete_issue',\n            arguments: { issue_key: JSON.parse(created.content[0].text).key },\n          });\n          break;\n        case 'read:confluence':\n          await client.callTool({\n            name: 'confluence_search',\n            arguments: { cql: 'type = page', limit: 1 },\n          });\n          break;\n      }\n\n      report.details.push({ operation, status: 'granted' });\n    } catch (error: any) {\n      report.hasAllPermissions = false;\n      report.details.push({\n        operation,\n        status: 'denied',\n        error: error.message,\n      });\n    }\n  }\n\n  return report;\n}\n```\n\n## Security Checklist\n\n### Do:\n- Use OAuth 2.1 for user-facing applications\n- Store secrets in dedicated secrets management systems\n- Implement token rotation policies\n- Use minimal required scopes\n- Log authentication events (without secrets)\n- Implement rate limiting at application level\n- Validate tokens before use\n\n### Don't:\n- Hardcode tokens in source code\n- Log tokens or secrets\n- Share tokens between environments\n- Use Basic Auth (deprecated)\n- Request more scopes than needed\n- Store tokens in browser localStorage\n- Commit `.env` files with real credentials\n\n### Environment Configuration Template\n\n```bash\n# .env.example (commit this)\nATLASSIAN_SITE_URL=https://your-site.atlassian.net\nATLASSIAN_AUTH_TYPE=oauth  # or 'api_token' or 'pat'\n\n# OAuth settings (if using OAuth)\nATLASSIAN_CLIENT_ID=\nATLASSIAN_CLIENT_SECRET=\n\n# API Token settings (if using API token)\nATLASSIAN_USERNAME=\nATLASSIAN_API_TOKEN=\n\n# PAT settings (if using Server/DC)\nATLASSIAN_PERSONAL_TOKEN=\n```\n\n```bash\n# .gitignore\n.env\n.env.local\n.env.*.local\ncredentials.json\n**/secrets/**\n```\n\n## Troubleshooting\n\n### Common Authentication Errors\n\n**401 Unauthorized:**\n- Invalid or expired token\n- Wrong authentication method\n- Missing Authorization header\n\n**403 Forbidden:**\n- Token valid but lacks required scope\n- Resource-level permission denied\n- IP allowlist blocking request\n\n**Token refresh fails:**\n- Refresh token expired (after 90 days of inactivity)\n- Client secret changed\n- App permissions revoked\n\n### Debug Authentication\n\n```typescript\nasync function debugAuth(token: string): Promise<void> {\n  // Check token validity\n  const meResponse = await fetch(\n    'https://api.atlassian.com/me',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  console.log('Token status:', meResponse.status);\n\n  if (meResponse.ok) {\n    const me = await meResponse.json();\n    console.log('Authenticated as:', me.email);\n  }\n\n  // Check accessible resources\n  const resourcesResponse = await fetch(\n    'https://api.atlassian.com/oauth/token/accessible-resources',\n    { headers: { Authorization: `Bearer ${token}` } }\n  );\n\n  if (resourcesResponse.ok) {\n    const resources = await resourcesResponse.json();\n    console.log('Accessible sites:', resources.map((r: any) => r.name));\n  }\n}\n```\n\n## Related References\n\n- `mcp-server-setup.md` - Server configuration with credentials\n- `jira-queries.md` - Operations that require authentication\n- `confluence-operations.md` - Content operations with auth\n\n<!-- File: skills/atlassian-mcp/references/common-workflows.md (Tokens: ~4983 | Category: anthropic-skill) -->\n# Common Workflows\n\n---\n\n## Issue Triage Workflow\n\nAutomatically categorize, prioritize, and route incoming issues.\n\n### Triage Bot Implementation\n\n```typescript\ninterface TriageResult {\n  issueKey: string;\n  category: string;\n  priority: string;\n  assignee: string | null;\n  labels: string[];\n  actions: string[];\n}\n\nasync function triageNewIssue(\n  client: MCPClient,\n  issueKey: string\n): Promise<TriageResult> {\n  // Get issue details\n  const issueResult = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: {\n      issue_key: issueKey,\n      expand: [\"changelog\"]\n    }\n  });\n\n  const issue = JSON.parse(issueResult.content[0].text);\n  const result: TriageResult = {\n    issueKey,\n    category: \"uncategorized\",\n    priority: issue.fields.priority?.name || \"Medium\",\n    assignee: null,\n    labels: [],\n    actions: []\n  };\n\n  // Categorize based on content\n  const summary = issue.fields.summary.toLowerCase();\n  const description = (issue.fields.description?.content?.[0]?.content?.[0]?.text || \"\").toLowerCase();\n  const text = `${summary} ${description}`;\n\n  // Category detection\n  if (text.match(/\\b(crash|down|outage|critical|emergency)\\b/)) {\n    result.category = \"incident\";\n    result.priority = \"Highest\";\n    result.labels.push(\"incident\", \"urgent\");\n  } else if (text.match(/\\b(bug|error|fail|broken|doesn't work)\\b/)) {\n    result.category = \"bug\";\n    result.labels.push(\"bug\", \"needs-investigation\");\n  } else if (text.match(/\\b(feature|enhancement|request|add|improve)\\b/)) {\n    result.category = \"feature\";\n    result.labels.push(\"feature-request\");\n  } else if (text.match(/\\b(docs|documentation|readme|guide)\\b/)) {\n    result.category = \"documentation\";\n    result.labels.push(\"documentation\");\n  }\n\n  // Component detection for routing\n  const componentMap: Record<string, { component: string; team: string }> = {\n    \"api|rest|endpoint\": { component: \"API\", team: \"backend-team\" },\n    \"ui|button|page|screen|css\": { component: \"Frontend\", team: \"frontend-team\" },\n    \"database|sql|query|migration\": { component: \"Database\", team: \"data-team\" },\n    \"auth|login|password|sso\": { component: \"Authentication\", team: \"security-team\" },\n    \"deploy|ci|cd|pipeline\": { component: \"DevOps\", team: \"platform-team\" }\n  };\n\n  for (const [pattern, { component, team }] of Object.entries(componentMap)) {\n    if (text.match(new RegExp(`\\\\b(${pattern})\\\\b`))) {\n      result.labels.push(component.toLowerCase());\n      result.actions.push(`Route to ${team}`);\n      break;\n    }\n  }\n\n  // Apply triage results\n  await client.callTool({\n    name: \"jira_update_issue\",\n    arguments: {\n      issue_key: issueKey,\n      fields: {\n        priority: { name: result.priority },\n        labels: [...new Set([...issue.fields.labels || [], ...result.labels])]\n      }\n    }\n  });\n\n  // Add triage comment\n  await client.callTool({\n    name: \"jira_add_comment\",\n    arguments: {\n      issue_key: issueKey,\n      body: `*Automated Triage Results*\n\nCategory: ${result.category}\nPriority: ${result.priority}\nLabels added: ${result.labels.join(\", \")}\n\n${result.actions.length > 0 ? `Recommended actions:\\n${result.actions.map(a => `- ${a}`).join(\"\\n\")}` : \"\"}`\n    }\n  });\n\n  return result;\n}\n```\n\n### Bulk Triage New Issues\n\n```typescript\nasync function triageBacklog(client: MCPClient, projectKey: string): Promise<void> {\n  // Find untriaged issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND labels IS EMPTY AND created >= -7d AND resolution IS EMPTY`,\n      max_results: 50,\n      fields: [\"summary\", \"description\", \"priority\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  console.log(`Found ${response.total} issues to triage`);\n\n  for (const issue of response.issues) {\n    const result = await triageNewIssue(client, issue.key);\n    console.log(`Triaged ${issue.key}: ${result.category} (${result.priority})`);\n\n    // Rate limiting\n    await delay(500);\n  }\n}\n```\n\n## Documentation Sync Workflow\n\nKeep Confluence documentation in sync with Jira issues.\n\n### Generate Release Notes\n\n```typescript\nasync function generateReleaseNotes(\n  client: MCPClient,\n  projectKey: string,\n  version: string,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get all issues in the release\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `project = ${projectKey} AND fixVersion = \"${version}\" AND resolution = Done ORDER BY issuetype, priority DESC`,\n      max_results: 200,\n      fields: [\"summary\", \"issuetype\", \"priority\", \"assignee\", \"labels\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n\n  // Group by issue type\n  const grouped: Record<string, any[]> = {};\n  for (const issue of response.issues) {\n    const type = issue.fields.issuetype.name;\n    if (!grouped[type]) grouped[type] = [];\n    grouped[type].push(issue);\n  }\n\n  // Build Confluence page content\n  let content = `\n<h2>Release ${version}</h2>\n<p>Released on ${new Date().toISOString().split('T')[0]}</p>\n\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">2</ac:parameter>\n</ac:structured-macro>\n\n<h2>Summary</h2>\n<p>This release includes ${response.total} changes.</p>\n<table>\n  <tr><th>Type</th><th>Count</th></tr>\n  ${Object.entries(grouped).map(([type, issues]) =>\n    `<tr><td>${type}</td><td>${issues.length}</td></tr>`\n  ).join('')}\n</table>\n`;\n\n  // Add sections for each issue type\n  const typeOrder = [\"New Feature\", \"Improvement\", \"Bug\", \"Task\"];\n  const orderedTypes = [...new Set([...typeOrder, ...Object.keys(grouped)])];\n\n  for (const type of orderedTypes) {\n    if (!grouped[type]) continue;\n\n    content += `\n<h2>${type}s</h2>\n<table>\n  <tr><th>Key</th><th>Summary</th><th>Assignee</th></tr>\n  ${grouped[type].map(issue => `\n    <tr>\n      <td><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issue.key}</ac:parameter></ac:structured-macro></td>\n      <td>${escapeHtml(issue.fields.summary)}</td>\n      <td>${issue.fields.assignee?.displayName || 'Unassigned'}</td>\n    </tr>\n  `).join('')}\n</table>\n`;\n  }\n\n  // Create or update Confluence page\n  const pageTitle = `Release Notes - ${version}`;\n\n  // Check if page exists\n  const existingSearch = await client.callTool({\n    name: \"confluence_search\",\n    arguments: {\n      cql: `space = \"${confluenceSpaceKey}\" AND title = \"${pageTitle}\"`,\n      limit: 1\n    }\n  });\n\n  const existingResults = JSON.parse(existingSearch.content[0].text);\n\n  if (existingResults.results.length > 0) {\n    // Update existing page\n    const pageId = existingResults.results[0].id;\n    const currentVersion = existingResults.results[0].version.number;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: pageId,\n        title: pageTitle,\n        body: content,\n        version_number: currentVersion + 1,\n        version_message: `Updated release notes for ${version}`\n      }\n    });\n\n    return pageId;\n  } else {\n    // Create new page\n    const newPage = await client.callTool({\n      name: \"confluence_create_page\",\n      arguments: {\n        space_key: confluenceSpaceKey,\n        title: pageTitle,\n        body: content,\n        labels: [\"release-notes\", `version-${version.replace(/\\./g, '-')}`]\n      }\n    });\n\n    return JSON.parse(newPage.content[0].text).id;\n  }\n}\n```\n\n### Sync Meeting Notes to Jira\n\n```typescript\ninterface ActionItem {\n  description: string;\n  assignee: string;\n  dueDate?: string;\n}\n\nasync function syncMeetingNotes(\n  client: MCPClient,\n  confluencePageId: string,\n  jiraProjectKey: string\n): Promise<string[]> {\n  // Get meeting notes content\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: confluencePageId,\n      expand: [\"body.storage\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n  const content = page.body.storage.value;\n\n  // Extract action items (look for checkbox patterns)\n  const actionItems = extractActionItems(content);\n  const createdIssues: string[] = [];\n\n  for (const item of actionItems) {\n    // Create Jira task\n    const newIssue = await client.callTool({\n      name: \"jira_create_issue\",\n      arguments: {\n        project_key: jiraProjectKey,\n        issue_type: \"Task\",\n        summary: item.description,\n        description: {\n          type: \"doc\",\n          version: 1,\n          content: [{\n            type: \"paragraph\",\n            content: [{\n              type: \"text\",\n              text: `Action item from meeting: ${page.title}\\n\\nSource: ${page._links.webui}`\n            }]\n          }]\n        },\n        assignee: item.assignee,\n        due_date: item.dueDate,\n        labels: [\"meeting-action\", \"auto-created\"]\n      }\n    });\n\n    const issueKey = JSON.parse(newIssue.content[0].text).key;\n    createdIssues.push(issueKey);\n  }\n\n  // Update Confluence page with Jira links\n  if (createdIssues.length > 0) {\n    const jiraLinksSection = `\n<h2>Linked Jira Issues</h2>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">key IN (${createdIssues.join(',')})</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n`;\n\n    await client.callTool({\n      name: \"confluence_update_page\",\n      arguments: {\n        page_id: confluencePageId,\n        title: page.title,\n        body: content + jiraLinksSection,\n        version_number: page.version.number + 1\n      }\n    });\n  }\n\n  return createdIssues;\n}\n\nfunction extractActionItems(html: string): ActionItem[] {\n  // Parse action items from common patterns\n  const items: ActionItem[] = [];\n\n  // Pattern: [x] or [ ] followed by @mention and task description\n  const taskPattern = /\\[[ x]\\]\\s*@([a-zA-Z.]+)\\s*[-:]\\s*(.+?)(?=<|$)/gi;\n  let match;\n\n  while ((match = taskPattern.exec(html)) !== null) {\n    items.push({\n      assignee: match[1],\n      description: match[2].trim()\n    });\n  }\n\n  return items;\n}\n```\n\n## Sprint Automation Workflow\n\nAutomate sprint ceremonies and reporting.\n\n### Sprint Planning Assistant\n\n```typescript\ninterface SprintPlanningReport {\n  totalPoints: number;\n  byAssignee: Record<string, number>;\n  byComponent: Record<string, number>;\n  riskyItems: string[];\n  recommendations: string[];\n}\n\nasync function analyzeSprintPlanning(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number\n): Promise<SprintPlanningReport> {\n  // Get sprint issues\n  const searchResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"assignee\", \"customfield_10001\", \"components\", \"labels\", \"priority\"]\n    }\n  });\n\n  const response = JSON.parse(searchResult.content[0].text);\n  const issues = response.issues;\n\n  const report: SprintPlanningReport = {\n    totalPoints: 0,\n    byAssignee: {},\n    byComponent: {},\n    riskyItems: [],\n    recommendations: []\n  };\n\n  for (const issue of issues) {\n    const points = issue.fields.customfield_10001 || 0; // Story points\n    const assignee = issue.fields.assignee?.displayName || \"Unassigned\";\n    const components = issue.fields.components?.map((c: any) => c.name) || [\"No Component\"];\n\n    report.totalPoints += points;\n    report.byAssignee[assignee] = (report.byAssignee[assignee] || 0) + points;\n\n    for (const component of components) {\n      report.byComponent[component] = (report.byComponent[component] || 0) + points;\n    }\n\n    // Identify risks\n    if (points === 0) {\n      report.riskyItems.push(`${issue.key}: No story points estimated`);\n    }\n    if (points >= 8) {\n      report.riskyItems.push(`${issue.key}: Large story (${points} points) - consider breaking down`);\n    }\n    if (assignee === \"Unassigned\") {\n      report.riskyItems.push(`${issue.key}: No assignee`);\n    }\n  }\n\n  // Generate recommendations\n  const avgPointsPerPerson = report.totalPoints / Object.keys(report.byAssignee).length;\n\n  for (const [assignee, points] of Object.entries(report.byAssignee)) {\n    if (points > avgPointsPerPerson * 1.5) {\n      report.recommendations.push(`${assignee} has ${points} points (${Math.round((points / avgPointsPerPerson - 1) * 100)}% above average) - consider rebalancing`);\n    }\n  }\n\n  return report;\n}\n```\n\n### Sprint Retrospective Generator\n\n```typescript\nasync function generateRetroBoard(\n  client: MCPClient,\n  boardId: number,\n  sprintId: number,\n  confluenceSpaceKey: string\n): Promise<string> {\n  // Get completed sprint data\n  const sprintResult = await client.callTool({\n    name: \"jira_get_sprint_report\",\n    arguments: {\n      board_id: boardId,\n      sprint_id: sprintId\n    }\n  });\n\n  const sprintReport = JSON.parse(sprintResult.content[0].text);\n\n  // Get sprint issues with details\n  const issuesResult = await client.callTool({\n    name: \"jira_search\",\n    arguments: {\n      jql: `sprint = ${sprintId}`,\n      max_results: 100,\n      fields: [\"summary\", \"status\", \"resolution\", \"customfield_10001\", \"assignee\"]\n    }\n  });\n\n  const issues = JSON.parse(issuesResult.content[0].text).issues;\n\n  // Calculate metrics\n  const completed = issues.filter((i: any) => i.fields.resolution);\n  const incomplete = issues.filter((i: any) => !i.fields.resolution);\n  const completedPoints = completed.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n  const plannedPoints = issues.reduce((sum: number, i: any) => sum + (i.fields.customfield_10001 || 0), 0);\n\n  // Create retrospective page\n  const content = `\n<h2>Sprint Retrospective: ${sprintReport.sprint.name}</h2>\n<p><em>Generated on ${new Date().toISOString().split('T')[0]}</em></p>\n\n<h3>Sprint Metrics</h3>\n<table>\n  <tr><th>Metric</th><th>Value</th></tr>\n  <tr><td>Start Date</td><td>${sprintReport.sprint.startDate}</td></tr>\n  <tr><td>End Date</td><td>${sprintReport.sprint.endDate}</td></tr>\n  <tr><td>Issues Planned</td><td>${issues.length}</td></tr>\n  <tr><td>Issues Completed</td><td>${completed.length}</td></tr>\n  <tr><td>Completion Rate</td><td>${Math.round(completed.length / issues.length * 100)}%</td></tr>\n  <tr><td>Points Planned</td><td>${plannedPoints}</td></tr>\n  <tr><td>Points Completed</td><td>${completedPoints}</td></tr>\n  <tr><td>Velocity</td><td>${completedPoints} points</td></tr>\n</table>\n\n<h3>Completed Items</h3>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS NOT EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee</ac:parameter>\n</ac:structured-macro>\n\n${incomplete.length > 0 ? `\n<h3>Incomplete Items (Spillover)</h3>\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>${incomplete.length} items were not completed and will spill over to the next sprint.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">sprint = ${sprintId} AND resolution IS EMPTY</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,assignee,status</ac:parameter>\n</ac:structured-macro>\n` : ''}\n\n<h3>Discussion Points</h3>\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What went well?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">What could be improved?</ac:parameter>\n  <ac:rich-text-body>\n    <ul>\n      <li><em>Add team input here...</em></li>\n    </ul>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Action Items</ac:parameter>\n  <ac:rich-text-body>\n    <ac:task-list>\n      <ac:task><ac:task-body><em>Add action items here...</em></ac:task-body></ac:task>\n    </ac:task-list>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  const newPage = await client.callTool({\n    name: \"confluence_create_page\",\n    arguments: {\n      space_key: confluenceSpaceKey,\n      title: `Sprint Retro - ${sprintReport.sprint.name}`,\n      body: content,\n      labels: [\"sprint-retro\", \"team-meeting\"]\n    }\n  });\n\n  return JSON.parse(newPage.content[0].text).id;\n}\n```\n\n## Issue-Documentation Link Workflow\n\nBidirectional linking between code changes and documentation.\n\n### Link PR to Documentation\n\n```typescript\nasync function linkPRToDocumentation(\n  client: MCPClient,\n  issueKey: string,\n  docPageId: string\n): Promise<void> {\n  // Add remote link to Jira issue\n  await client.callTool({\n    name: \"jira_add_remote_link\",\n    arguments: {\n      issue_key: issueKey,\n      url: `https://your-site.atlassian.net/wiki/spaces/DEV/pages/${docPageId}`,\n      title: \"Related Documentation\",\n      icon_url: \"https://your-site.atlassian.net/wiki/favicon.ico\"\n    }\n  });\n\n  // Add Jira macro to Confluence page\n  const pageResult = await client.callTool({\n    name: \"confluence_get_page\",\n    arguments: {\n      page_id: docPageId,\n      expand: [\"body.storage\", \"version\"]\n    }\n  });\n\n  const page = JSON.parse(pageResult.content[0].text);\n\n  // Check if link already exists\n  if (page.body.storage.value.includes(issueKey)) {\n    return; // Already linked\n  }\n\n  const jiraLink = `\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Related Issue</ac:parameter>\n  <ac:rich-text-body>\n    <p><ac:structured-macro ac:name=\"jira\"><ac:parameter ac:name=\"key\">${issueKey}</ac:parameter></ac:structured-macro></p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n`;\n\n  await client.callTool({\n    name: \"confluence_update_page\",\n    arguments: {\n      page_id: docPageId,\n      title: page.title,\n      body: jiraLink + page.body.storage.value,\n      version_number: page.version.number + 1\n    }\n  });\n}\n```\n\n## Utility Functions\n\n```typescript\nfunction escapeHtml(text: string): string {\n  return text\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#039;');\n}\n\nfunction delay(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function withRetry<T>(\n  operation: () => Promise<T>,\n  maxRetries = 3,\n  baseDelay = 1000\n): Promise<T> {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      if (attempt === maxRetries) throw error;\n\n      const delay = baseDelay * Math.pow(2, attempt - 1);\n      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n## Related References\n\n- `jira-queries.md` - JQL syntax for workflow queries\n- `confluence-operations.md` - Page creation and formatting\n- `authentication-patterns.md` - Secure API access for automated workflows\n\n<!-- File: skills/atlassian-mcp/references/confluence-operations.md (Tokens: ~3341 | Category: anthropic-skill) -->\n# Confluence Operations\n\n---\n\n## CQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `space = \"DEV\"` |\n| `!=` | Not equal | `type != attachment` |\n| `~` | Contains | `title ~ \"API\"` |\n| `!~` | Does not contain | `text !~ \"deprecated\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `lastModified >= \"2024-01-01\"` |\n| `IN` | Multiple values | `space IN (\"DEV\", \"OPS\")` |\n| `NOT IN` | Exclude | `creator NOT IN (\"bot\")` |\n\n### Field Reference\n\n**Content Fields:**\n```cql\ntype = page                        -- Pages only\ntype = blogpost                    -- Blog posts\ntype = attachment                  -- Attachments\ntype = comment                     -- Comments\nspace = \"DEVDOCS\"                  -- Specific space\nspace.type = global                -- Global spaces\nspace.type = personal              -- Personal spaces\n```\n\n**Search Fields:**\n```cql\ntitle ~ \"architecture\"             -- Title contains\ntext ~ \"kubernetes\"                -- Full text search\ncontent ~ \"deployment\"             -- Content body\nlabel = \"official\"                 -- Has label\nlabel IN (\"api\", \"reference\")      -- Multiple labels\n```\n\n**Date Fields:**\n```cql\ncreated >= \"2024-01-01\"            -- Created after date\nlastModified >= now(\"-30d\")        -- Modified in last 30 days\ncreated >= startOfYear()           -- Created this year\nlastModified >= startOfMonth()     -- Modified this month\n```\n\n**User Fields:**\n```cql\ncreator = currentUser()            -- Created by me\ncontributor = \"john.doe\"           -- Edited by user\nmention = currentUser()            -- Mentions me\nwatcher = currentUser()            -- Pages I watch\nfavourite = currentUser()          -- My favorites\n```\n\n## Essential CQL Patterns\n\n### Documentation Search\n\n```cql\n-- API documentation in dev space\nspace = \"DEV\" AND label = \"api-docs\" AND type = page\n\n-- Recently updated architecture docs\nlabel = \"architecture\" AND lastModified >= now(\"-7d\")\n\n-- Search for code examples\ntext ~ \"```\" AND label = \"tutorial\"\n\n-- Find outdated documentation\nlabel = \"needs-review\" OR lastModified <= now(\"-180d\")\n\n-- Meeting notes from this month\nlabel = \"meeting-notes\" AND created >= startOfMonth()\n```\n\n### Space Management\n\n```cql\n-- All pages in multiple spaces\nspace IN (\"DEV\", \"OPS\", \"PRODUCT\") AND type = page\n\n-- Personal space content\nspace.type = personal AND creator = currentUser()\n\n-- Archived content\nlabel = \"archived\" AND space = \"LEGACY\"\n\n-- Templates\ntype = page AND label = \"template\"\n```\n\n### Content Discovery\n\n```cql\n-- Popular pages (frequently viewed)\ntype = page AND space = \"DOCS\" ORDER BY lastModified DESC\n\n-- Draft pages\nlabel = \"draft\" AND type = page\n\n-- Pages without labels\ntype = page AND space = \"DEV\" AND label IS NULL\n\n-- Orphan pages (no parent)\ntype = page AND ancestor IS NULL AND space = \"DOCS\"\n```\n\n## MCP Tool Calls\n\n### Searching Content\n\n```typescript\n// Basic CQL search\nconst searchResult = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'space = \"DEV\" AND label = \"api-docs\"',\n    limit: 25,\n    expand: [\"body.storage\", \"version\", \"ancestors\"]\n  }\n});\n\n// Parse results\nconst results = JSON.parse(searchResult.content[0].text);\nfor (const page of results.results) {\n  console.log(`${page.title} - ${page._links.webui}`);\n}\n\n// Search with content preview\nconst searchWithContent = await client.callTool({\n  name: \"confluence_search\",\n  arguments: {\n    cql: 'text ~ \"deployment\" AND space = \"OPS\"',\n    limit: 10,\n    excerpt: true\n  }\n});\n```\n\n### Getting Page Content\n\n```typescript\n// Get page by ID\nconst page = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"body.view\", \"version\", \"ancestors\", \"children.page\"]\n  }\n});\n\n// Get page by space and title\nconst pageByTitle = await client.callTool({\n  name: \"confluence_get_page_by_title\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Reference\"\n  }\n});\n\n// Get page children\nconst children = await client.callTool({\n  name: \"confluence_get_children\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"page\"]\n  }\n});\n```\n\n### Creating Pages\n\n```typescript\n// Create page with storage format (XHTML)\nconst newPage = await client.callTool({\n  name: \"confluence_create_page\",\n  arguments: {\n    space_key: \"DEV\",\n    title: \"API Authentication Guide\",\n    parent_id: \"123456\",  // Optional parent page\n    body: `\n      <h2>Overview</h2>\n      <p>This guide covers authentication methods for our API.</p>\n\n      <h2>OAuth 2.0</h2>\n      <p>We support OAuth 2.0 with the following grant types:</p>\n      <ul>\n        <li>Authorization Code</li>\n        <li>Client Credentials</li>\n      </ul>\n\n      <ac:structured-macro ac:name=\"code\">\n        <ac:parameter ac:name=\"language\">bash</ac:parameter>\n        <ac:plain-text-body><![CDATA[curl -X POST https://api.example.com/oauth/token \\\\\n  -d \"grant_type=client_credentials\" \\\\\n  -d \"client_id=<client-id>\" \\\\\n  -d \"client_secret=<client-secret>\"]]></ac:plain-text-body>\n      </ac:structured-macro>\n\n      <h2>API Tokens</h2>\n      <p>For simple integrations, use API tokens:</p>\n      <ac:structured-macro ac:name=\"info\">\n        <ac:rich-text-body>\n          <p>API tokens are tied to your user account and have the same permissions.</p>\n        </ac:rich-text-body>\n      </ac:structured-macro>\n    `,\n    labels: [\"api-docs\", \"authentication\", \"official\"]\n  }\n});\n\nconsole.log(`Created page: ${newPage.content[0].text}`);\n```\n\n### Updating Pages\n\n```typescript\n// Update page content\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: \"API Authentication Guide (Updated)\",\n    body: \"<h2>Updated Content</h2><p>New documentation here...</p>\",\n    version_number: 5,  // Current version + 1\n    version_message: \"Added OAuth 2.1 section\"\n  }\n});\n\n// Append to existing page\nconst currentPage = await client.callTool({\n  name: \"confluence_get_page\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\nconst pageData = JSON.parse(currentPage.content[0].text);\nconst currentBody = pageData.body.storage.value;\nconst newSection = `\n  <h2>New Section</h2>\n  <p>Additional content appended to the page.</p>\n`;\n\nawait client.callTool({\n  name: \"confluence_update_page\",\n  arguments: {\n    page_id: \"123456\",\n    title: pageData.title,\n    body: currentBody + newSection,\n    version_number: pageData.version.number + 1\n  }\n});\n```\n\n### Working with Comments\n\n```typescript\n// Add comment to page\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    body: \"<p>This section needs to be updated for v2.0 changes.</p>\"\n  }\n});\n\n// Get page comments\nconst comments = await client.callTool({\n  name: \"confluence_get_comments\",\n  arguments: {\n    page_id: \"123456\",\n    expand: [\"body.storage\", \"version\"]\n  }\n});\n\n// Reply to comment\nawait client.callTool({\n  name: \"confluence_add_comment\",\n  arguments: {\n    page_id: \"123456\",\n    parent_comment_id: \"789012\",\n    body: \"<p>Good catch! I'll update this section.</p>\"\n  }\n});\n```\n\n### Managing Labels\n\n```typescript\n// Add labels to page\nawait client.callTool({\n  name: \"confluence_add_labels\",\n  arguments: {\n    page_id: \"123456\",\n    labels: [\"reviewed\", \"q1-2024\", \"api-v2\"]\n  }\n});\n\n// Remove label\nawait client.callTool({\n  name: \"confluence_remove_label\",\n  arguments: {\n    page_id: \"123456\",\n    label: \"draft\"\n  }\n});\n\n// Get page labels\nconst labels = await client.callTool({\n  name: \"confluence_get_labels\",\n  arguments: {\n    page_id: \"123456\"\n  }\n});\n```\n\n### Space Operations\n\n```typescript\n// Get space information\nconst space = await client.callTool({\n  name: \"confluence_get_space\",\n  arguments: {\n    space_key: \"DEV\",\n    expand: [\"description\", \"homepage\"]\n  }\n});\n\n// List all spaces\nconst spaces = await client.callTool({\n  name: \"confluence_list_spaces\",\n  arguments: {\n    type: \"global\",\n    limit: 100\n  }\n});\n\n// Get space content\nconst spaceContent = await client.callTool({\n  name: \"confluence_get_space_content\",\n  arguments: {\n    space_key: \"DEV\",\n    depth: \"root\",  // or \"all\"\n    expand: [\"children.page\"]\n  }\n});\n```\n\n## Storage Format Reference\n\n### Common Macros\n\n```xml\n<!-- Code block -->\n<ac:structured-macro ac:name=\"code\">\n  <ac:parameter ac:name=\"language\">python</ac:parameter>\n  <ac:parameter ac:name=\"title\">Example</ac:parameter>\n  <ac:plain-text-body><![CDATA[print(\"Hello, World!\")]]></ac:plain-text-body>\n</ac:structured-macro>\n\n<!-- Info panel -->\n<ac:structured-macro ac:name=\"info\">\n  <ac:parameter ac:name=\"title\">Note</ac:parameter>\n  <ac:rich-text-body>\n    <p>Important information here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Warning panel -->\n<ac:structured-macro ac:name=\"warning\">\n  <ac:rich-text-body>\n    <p>Be careful with this operation!</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Table of contents -->\n<ac:structured-macro ac:name=\"toc\">\n  <ac:parameter ac:name=\"maxLevel\">3</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issue link -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"key\">PROJ-123</ac:parameter>\n</ac:structured-macro>\n\n<!-- Jira issues table -->\n<ac:structured-macro ac:name=\"jira\">\n  <ac:parameter ac:name=\"jqlQuery\">project = PROJ AND sprint in openSprints()</ac:parameter>\n  <ac:parameter ac:name=\"columns\">key,summary,status,assignee</ac:parameter>\n</ac:structured-macro>\n\n<!-- Expand section -->\n<ac:structured-macro ac:name=\"expand\">\n  <ac:parameter ac:name=\"title\">Click to expand</ac:parameter>\n  <ac:rich-text-body>\n    <p>Hidden content here.</p>\n  </ac:rich-text-body>\n</ac:structured-macro>\n\n<!-- Include page -->\n<ac:structured-macro ac:name=\"include\">\n  <ac:parameter ac:name=\"\"><ri:page ri:content-title=\"Shared Footer\" /></ac:parameter>\n</ac:structured-macro>\n```\n\n### Formatting Elements\n\n```xml\n<!-- Status badge -->\n<ac:structured-macro ac:name=\"status\">\n  <ac:parameter ac:name=\"colour\">Green</ac:parameter>\n  <ac:parameter ac:name=\"title\">APPROVED</ac:parameter>\n</ac:structured-macro>\n\n<!-- User mention -->\n<ac:link><ri:user ri:account-id=\"557058:f3c7...\" /></ac:link>\n\n<!-- Page link -->\n<ac:link><ri:page ri:content-title=\"Target Page\" ri:space-key=\"DEV\" /></ac:link>\n\n<!-- Attachment -->\n<ac:link><ri:attachment ri:filename=\"diagram.png\" /></ac:link>\n\n<!-- Image from attachment -->\n<ac:image><ri:attachment ri:filename=\"screenshot.png\" /></ac:image>\n\n<!-- External image -->\n<ac:image><ri:url ri:value=\"https://example.com/image.png\" /></ac:image>\n```\n\n## Pagination Handling\n\n```typescript\nasync function getAllPages(cql: string): Promise<Page[]> {\n  const allPages: Page[] = [];\n  let start = 0;\n  const limit = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"confluence_search\",\n      arguments: {\n        cql,\n        start,\n        limit,\n        expand: [\"body.storage\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allPages.push(...response.results);\n\n    if (response.results.length < limit || !response._links.next) {\n      break;\n    }\n\n    start += limit;\n  }\n\n  return allPages;\n}\n```\n\n## Error Handling\n\n```typescript\nasync function safeConfluenceCall<T>(operation: () => Promise<T>): Promise<T> {\n  try {\n    return await operation();\n  } catch (error: any) {\n    const status = error.response?.status;\n\n    switch (status) {\n      case 404:\n        throw new Error(`Page or space not found: ${error.message}`);\n      case 403:\n        throw new Error(`Permission denied. Check space permissions.`);\n      case 409:\n        throw new Error(`Version conflict. Page was modified. Refresh and retry.`);\n      case 429:\n        const retryAfter = error.response?.headers?.['retry-after'] || 60;\n        throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);\n      default:\n        throw error;\n    }\n  }\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```cql\n-- Too broad (slow)\ntext ~ \"the\"\n\n-- Missing quotes\nspace = DEV DOCS  -- WRONG\nspace = \"DEV DOCS\"  -- CORRECT\n\n-- Invalid date format\ncreated >= 2024-01-01  -- WRONG\ncreated >= \"2024-01-01\"  -- CORRECT\n```\n\n**Best Practices:**\n- Always specify space when possible for faster queries\n- Use labels for categorization and filtering\n- Combine text search with specific fields\n- Cache frequently accessed page content\n- Handle version conflicts gracefully\n\n## Related References\n\n- `common-workflows.md` - Documentation sync and automation\n- `jira-queries.md` - Linking Confluence pages to Jira issues\n- `authentication-patterns.md` - API access configuration\n\n<!-- File: skills/atlassian-mcp/references/jira-queries.md (Tokens: ~3309 | Category: anthropic-skill) -->\n# Jira Queries and Operations\n\n---\n\n## JQL Fundamentals\n\n### Basic Query Structure\n\n```\nfield OPERATOR value [AND|OR field OPERATOR value]\n```\n\n### Common Operators\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `=` | Exact match | `project = \"PROJ\"` |\n| `!=` | Not equal | `status != Done` |\n| `~` | Contains (text search) | `summary ~ \"login bug\"` |\n| `!~` | Does not contain | `description !~ \"test\"` |\n| `>`, `<`, `>=`, `<=` | Comparison | `created >= -7d` |\n| `IN` | Multiple values | `status IN (Open, \"In Progress\")` |\n| `NOT IN` | Exclude values | `assignee NOT IN (john, jane)` |\n| `IS` | Null check | `assignee IS EMPTY` |\n| `IS NOT` | Not null | `resolution IS NOT EMPTY` |\n| `WAS` | Historical state | `status WAS \"In Progress\"` |\n| `CHANGED` | Field changed | `status CHANGED FROM Open` |\n\n### Field Reference\n\n**Standard Fields:**\n```jql\nproject = PROJ\nissuetype = Bug\nstatus = \"In Progress\"\npriority = High\nassignee = currentUser()\nreporter = \"john.doe\"\nresolution = Unresolved\nlabels = backend\ncomponent = \"API\"\nfixVersion = \"2.0\"\naffectsVersion = \"1.5\"\n```\n\n**Date Fields:**\n```jql\ncreated >= -30d                    -- Last 30 days\nupdated >= \"2024-01-01\"           -- Since specific date\ndue <= endOfWeek()                 -- Due this week\nresolved >= startOfMonth()         -- Resolved this month\n```\n\n**Text Search:**\n```jql\nsummary ~ \"authentication\"         -- Summary contains\ndescription ~ \"error AND login\"    -- Description search\ntext ~ \"payment failed\"            -- All text fields\ncomment ~ \"blocked\"                -- Comment contains\n```\n\n## Essential JQL Patterns\n\n### Sprint and Backlog Queries\n\n```jql\n-- Current sprint issues\nsprint in openSprints() AND project = PROJ\n\n-- Backlog items\nsprint IS EMPTY AND resolution IS EMPTY AND project = PROJ\n\n-- Sprint completion\nsprint = \"Sprint 23\" AND status = Done\n\n-- Spillover from last sprint\nsprint in closedSprints() AND resolution IS EMPTY\n\n-- Ready for sprint planning\nstatus = \"Ready for Dev\" AND sprint IS EMPTY\n```\n\n### Bug Tracking\n\n```jql\n-- Open bugs by priority\nissuetype = Bug AND resolution IS EMPTY ORDER BY priority DESC\n\n-- Critical production bugs\nissuetype = Bug AND priority IN (Highest, High)\n  AND labels = production AND resolution IS EMPTY\n\n-- Bugs created this week\nissuetype = Bug AND created >= startOfWeek()\n\n-- Bugs without reproduction steps\nissuetype = Bug AND \"Reproduction Steps\" IS EMPTY\n  AND resolution IS EMPTY\n\n-- Regression bugs\nissuetype = Bug AND labels = regression AND fixVersion = \"2.0\"\n```\n\n### Team Workload\n\n```jql\n-- My open issues\nassignee = currentUser() AND resolution IS EMPTY\n\n-- Unassigned high priority\nassignee IS EMPTY AND priority IN (Highest, High)\n  AND resolution IS EMPTY\n\n-- Team member workload\nassignee = \"jane.smith\" AND sprint in openSprints()\n\n-- Blocked issues\nstatus = Blocked OR labels = blocked\n\n-- Stale issues (no update in 14 days)\nupdated <= -14d AND resolution IS EMPTY\n```\n\n### Release Management\n\n```jql\n-- Release candidates\nfixVersion = \"2.0\" AND status = \"Ready for Release\"\n\n-- Missing fix version\nresolution = Done AND fixVersion IS EMPTY AND updated >= -30d\n\n-- Release blockers\nfixVersion = \"2.0\" AND priority = Blocker AND resolution IS EMPTY\n\n-- Changelog items\nfixVersion = \"2.0\" AND resolution = Done ORDER BY issuetype\n```\n\n## MCP Tool Calls\n\n### Searching Issues\n\n```typescript\n// Basic JQL search\nconst searchResult = await client.callTool({\n  name: \"jira_search\",\n  arguments: {\n    jql: \"project = PROJ AND sprint in openSprints()\",\n    max_results: 50,\n    fields: [\"summary\", \"status\", \"assignee\", \"priority\"]\n  }\n});\n\n// Parse response\nconst issues = JSON.parse(searchResult.content[0].text);\nfor (const issue of issues.issues) {\n  console.log(`${issue.key}: ${issue.fields.summary}`);\n}\n```\n\n### Getting Issue Details\n\n```typescript\n// Get single issue with all fields\nconst issue = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    expand: [\"changelog\", \"comments\", \"transitions\"]\n  }\n});\n\n// Get issue with specific fields\nconst issuePartial = await client.callTool({\n  name: \"jira_get_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: [\"summary\", \"description\", \"customfield_10001\"]\n  }\n});\n```\n\n### Creating Issues\n\n```typescript\n// Create a bug\nconst newBug = await client.callTool({\n  name: \"jira_create_issue\",\n  arguments: {\n    project_key: \"PROJ\",\n    issue_type: \"Bug\",\n    summary: \"Login fails with SSO enabled\",\n    description: {\n      type: \"doc\",\n      version: 1,\n      content: [\n        {\n          type: \"paragraph\",\n          content: [{ type: \"text\", text: \"Users cannot log in when SSO is enabled.\" }]\n        },\n        {\n          type: \"heading\",\n          attrs: { level: 3 },\n          content: [{ type: \"text\", text: \"Steps to Reproduce\" }]\n        },\n        {\n          type: \"orderedList\",\n          content: [\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Enable SSO in settings\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Log out\" }] }] },\n            { type: \"listItem\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Attempt to log in via SSO\" }] }] }\n          ]\n        }\n      ]\n    },\n    priority: \"High\",\n    labels: [\"sso\", \"authentication\", \"production\"],\n    components: [\"Authentication\"],\n    assignee: \"jane.smith\"\n  }\n});\n\nconsole.log(`Created: ${newBug.content[0].text}`); // PROJ-456\n```\n\n### Updating Issues\n\n```typescript\n// Update issue fields\nawait client.callTool({\n  name: \"jira_update_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    fields: {\n      summary: \"Updated summary\",\n      priority: { name: \"Highest\" },\n      labels: [\"urgent\", \"production\"]\n    }\n  }\n});\n\n// Add comment\nawait client.callTool({\n  name: \"jira_add_comment\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    body: \"Investigating this issue. Initial analysis suggests a race condition.\"\n  }\n});\n\n// Transition issue\nawait client.callTool({\n  name: \"jira_transition_issue\",\n  arguments: {\n    issue_key: \"PROJ-123\",\n    transition: \"In Progress\"\n  }\n});\n```\n\n### Sprint Operations\n\n```typescript\n// Get active sprints\nconst sprints = await client.callTool({\n  name: \"jira_get_sprints\",\n  arguments: {\n    board_id: 42,\n    state: \"active\"\n  }\n});\n\n// Move issue to sprint\nawait client.callTool({\n  name: \"jira_move_to_sprint\",\n  arguments: {\n    sprint_id: 123,\n    issue_keys: [\"PROJ-100\", \"PROJ-101\", \"PROJ-102\"]\n  }\n});\n\n// Get sprint report\nconst report = await client.callTool({\n  name: \"jira_get_sprint_report\",\n  arguments: {\n    board_id: 42,\n    sprint_id: 123\n  }\n});\n```\n\n### Issue Linking\n\nUse `jira_create_issue_link` to create dependency relationships between issues.\n\n> **The parameter names are counterintuitive.** The naming reflects Jira's internal \"inward/outward\" link direction, not natural English. Verify every link call against the table below.\n\n#### Parameter Semantics for \"Blocks\" Links\n\n| Parameter | Role | Meaning |\n|-----------|------|---------|\n| `inward_issue_key` | **Blocker** | This issue blocks the other |\n| `outward_issue_key` | **Blocked** | This issue is blocked by the other |\n\n**Memory aid:** `inward_issue_key` = the issue receiving the inward description (\"is blocked by\") — but it is the *blocker*. Think: \"the inward key is where the arrow points FROM.\"\n\n#### Single Blocks Link\n\n```typescript\n// Make AUTH-1 block AUTH-2\n// AUTH-1 will show: \"blocks AUTH-2\"\n// AUTH-2 will show: \"is blocked by AUTH-1\"\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-1\",   // blocker\n    outward_issue_key: \"AUTH-2\"   // blocked\n  }\n});\n```\n\n#### Linking a Dependency Chain\n\nWhen creating a chain A → B → C (A blocks B, B blocks C):\n\n```typescript\nconst chain = [\n  { blocker: \"AUTH-1\", blocked: \"AUTH-2\" },\n  { blocker: \"AUTH-2\", blocked: \"AUTH-3\" },\n  { blocker: \"AUTH-3\", blocked: \"AUTH-4\" }\n];\n\nfor (const dep of chain) {\n  await client.callTool({\n    name: \"jira_create_issue_link\",\n    arguments: {\n      link_type: \"Blocks\",\n      inward_issue_key: dep.blocker,\n      outward_issue_key: dep.blocked\n    }\n  });\n\n  // Respect rate limits between link operations\n  await delay(100);\n}\n```\n\n#### Other Link Types\n\nThe same `inward`/`outward` pattern applies to all link types:\n\n| Link Type | `inward_issue_key` shows | `outward_issue_key` shows |\n|-----------|--------------------------|---------------------------|\n| `Blocks` | \"blocks [outward]\" | \"is blocked by [inward]\" |\n| `Duplicate` | \"duplicates [outward]\" | \"is duplicated by [inward]\" |\n| `Relates` | \"relates to [outward]\" | \"relates to [inward]\" |\n\n```typescript\n// Mark PROJ-10 as a duplicate of PROJ-5\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Duplicate\",\n    inward_issue_key: \"PROJ-10\",   // the duplicate\n    outward_issue_key: \"PROJ-5\"    // the original\n  }\n});\n```\n\n#### Anti-Pattern: Reversed Parameters\n\n```typescript\n// WRONG — This makes AUTH-2 block AUTH-1 (backwards!)\nawait client.callTool({\n  name: \"jira_create_issue_link\",\n  arguments: {\n    link_type: \"Blocks\",\n    inward_issue_key: \"AUTH-2\",   // accidentally made AUTH-2 the blocker\n    outward_issue_key: \"AUTH-1\"   // accidentally made AUTH-1 the blocked\n  }\n});\n```\n\nAlways verify: after creating a link, the blocker (`inward_issue_key`) should display \"blocks [outward]\" in its Jira issue view.\n\n## Pagination Handling\n\n```typescript\nasync function getAllIssues(jql: string): Promise<Issue[]> {\n  const allIssues: Issue[] = [];\n  let startAt = 0;\n  const maxResults = 100;\n\n  while (true) {\n    const result = await client.callTool({\n      name: \"jira_search\",\n      arguments: {\n        jql,\n        start_at: startAt,\n        max_results: maxResults,\n        fields: [\"summary\", \"status\", \"assignee\"]\n      }\n    });\n\n    const response = JSON.parse(result.content[0].text);\n    allIssues.push(...response.issues);\n\n    if (startAt + response.issues.length >= response.total) {\n      break;\n    }\n\n    startAt += maxResults;\n  }\n\n  return allIssues;\n}\n```\n\n## Bulk Operations\n\n```typescript\n// Bulk update with JQL\nasync function bulkUpdateLabels(jql: string, addLabels: string[]) {\n  const issues = await getAllIssues(jql);\n\n  for (const issue of issues) {\n    const existingLabels = issue.fields.labels || [];\n    await client.callTool({\n      name: \"jira_update_issue\",\n      arguments: {\n        issue_key: issue.key,\n        fields: {\n          labels: [...new Set([...existingLabels, ...addLabels])]\n        }\n      }\n    });\n\n    // Respect rate limits\n    await delay(100);\n  }\n}\n\n// Usage\nawait bulkUpdateLabels(\n  'project = PROJ AND sprint in openSprints() AND labels = backend',\n  ['q4-priority', 'needs-review']\n);\n```\n\n## Error Handling\n\n```typescript\nasync function safeJiraCall<T>(\n  operation: () => Promise<T>,\n  retries = 3\n): Promise<T> {\n  for (let attempt = 1; attempt <= retries; attempt++) {\n    try {\n      return await operation();\n    } catch (error: any) {\n      const status = error.response?.status;\n\n      // Don't retry client errors (except rate limits)\n      if (status >= 400 && status < 500 && status !== 429) {\n        throw error;\n      }\n\n      // Rate limited - wait and retry\n      if (status === 429) {\n        const retryAfter = parseInt(error.response?.headers?.['retry-after'] || '60');\n        console.log(`Rate limited. Waiting ${retryAfter}s...`);\n        await delay(retryAfter * 1000);\n        continue;\n      }\n\n      // Server error - exponential backoff\n      if (attempt < retries) {\n        const backoff = Math.pow(2, attempt) * 1000;\n        console.log(`Attempt ${attempt} failed. Retrying in ${backoff}ms...`);\n        await delay(backoff);\n      } else {\n        throw error;\n      }\n    }\n  }\n  throw new Error('Unexpected end of retry loop');\n}\n```\n\n## Common Anti-Patterns\n\n**Avoid:**\n```jql\n-- Too broad (slow, may timeout)\nproject IS NOT EMPTY\n\n-- Missing quotes for multi-word values\nstatus = In Progress  -- WRONG\nstatus = \"In Progress\"  -- CORRECT\n\n-- Case sensitivity issues\nassignee = John  -- May fail\nassignee = \"john.doe@company.com\"  -- CORRECT\n\n-- Inefficient ordering\nORDER BY created  -- Missing direction\nORDER BY created DESC  -- CORRECT\n```\n\n## Related References\n\n- `common-workflows.md` - End-to-end workflow patterns\n- `authentication-patterns.md` - Credential setup for API calls\n- `confluence-operations.md` - Linking Jira issues to Confluence pages\n\n<!-- File: skills/atlassian-mcp/references/mcp-server-setup.md (Tokens: ~3298 | Category: anthropic-skill) -->\n# MCP Server Setup\n\n---\n\n## Server Options Overview\n\n### Atlassian Rovo MCP Server (official, recommended)\n\nAtlassian hosts the official MCP server. There is nothing to install: clients connect over Streamable HTTP to\n\n```\nhttps://mcp.atlassian.com/v2/mcp\n```\n\n**Capabilities:**\n- Jira, Confluence, Jira Service Management, Bitbucket Cloud, Compass, Loom, Goals, Projects, Teams, Focus, Talent and the Teamwork Graph\n- OAuth 2.1 browser sign-in, or API tokens for headless clients (admin-enabled)\n- Acts with the signed-in user's existing Atlassian permissions\n- Tools are grouped into permission groups (`read_jira`, `write_jira`, `search_jira`, `read_confluence`, ...) that org admins grant or revoke; `delete_jira` and `manage_jira` are off by default\n- Exposes a small set of primary tools and lets the client discover the rest on demand; append `?tools=all` for a flat list (useful behind MCP gateways)\n- Atlassian Cloud only\n\n**Endpoint versions:**\n\n| Endpoint | Status |\n|----------|--------|\n| `https://mcp.atlassian.com/v2/mcp` | Recommended for new setups |\n| `https://mcp.atlassian.com/v1/mcp`, `/v1/mcp/authv2` | Still supported; existing v1 connections start exposing v2 tools |\n| `https://mcp.atlassian.com/v1/sse` | Legacy SSE endpoint, unsupported after 30 June 2026; move to `/v2/mcp` |\n\nSources: [atlassian/atlassian-mcp-server](https://github.com/atlassian/atlassian-mcp-server), [Getting started with the Atlassian Rovo MCP Server](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/), [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/).\n\n### Community Servers\n\nUse these only when the official server does not fit, mainly for Jira or Confluence **Server/Data Center**.\n\n**mcp-atlassian (sooperset)** - Python, the most complete community option, and the only one here with Server/DC support. Published on PyPI as `mcp-atlassian` and as a Docker image:\n```bash\n# Run without installing (recommended)\nuvx mcp-atlassian --help\n\n# Or install\nuv tool install mcp-atlassian\npip install mcp-atlassian\n\n# Or Docker\ndocker pull ghcr.io/sooperset/mcp-atlassian:latest\n```\n\n**atlassian-mcp (xuanxt)** - TypeScript, Jira and Confluence Cloud only. Published on npm as `@xuandev/atlassian-mcp`:\n```bash\nnpx -y @xuandev/atlassian-mcp\n```\n\n### Comparison Matrix\n\n| Feature | Rovo MCP Server | sooperset | xuanxt |\n|---------|-----------------|-----------|--------|\n| Hosting | Atlassian-hosted | Self-run (stdio, Docker, HTTP) | Self-run |\n| Jira / Confluence Cloud | Yes | Yes | Yes |\n| Jira / Confluence Server/DC | No | Yes | No |\n| Other Atlassian products | JSM, Bitbucket, Compass, Loom, Goals, ... | Jira and Confluence only | Jira and Confluence only |\n| OAuth 2.1 | Yes | Yes (bring your own OAuth app) | No |\n| API token | Yes, if an admin enables it | Yes | Yes |\n| PAT (Server/DC) | No | Yes | No |\n| Admin controls | Permission groups, domain and IP allowlists, audit log | Env vars (`READ_ONLY_MODE`, filters) | Env vars |\n\n## Connecting to the Rovo MCP Server\n\n### Claude Code\n\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nThen run `/mcp` in a session and complete the OAuth sign-in in the browser.\n\n### Claude Desktop\n\nInstall it from **Settings → Extensions → Browse extensions → Plugins**, then search for **Atlassian**.\n\n### Other Clients\n\nAny client that speaks Streamable HTTP takes the server URL directly. Clients that only run local stdio servers can bridge with the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) proxy (Node.js 18+):\n\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-remote\", \"https://mcp.atlassian.com/v2/mcp\"]\n    }\n  }\n}\n```\n\n### Headless: API Token Authentication\n\nFor automations and other non-interactive clients. An org admin must first enable API token authentication (**Atlassian Administration → Rovo → Rovo MCP server → Authentication**). Jira Service Management tools require it.\n\n| Mechanism | Header |\n|-----------|--------|\n| Personal API token | `Authorization: Basic <base64(email:api_token)>` |\n| Service account API key | `Authorization: Bearer <api_key>` |\n\n```bash\n# Build the Basic credential from environment variables; never paste tokens inline\nexport ATLASSIAN_BASIC_AUTH=$(printf '%s:%s' \"$ATLASSIAN_EMAIL\" \"$ATLASSIAN_API_TOKEN\" | base64 -w0)\n\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp \\\n  --header \"Authorization: Basic ${ATLASSIAN_BASIC_AUTH}\"\n```\n\nCreate the token with only the scopes the tools need. See [Configuring authentication via API token](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/configuring-authentication-via-api-token/).\n\n### Admin Prerequisites\n\n- **Domain allowlist:** OAuth connections must come from a client domain allowed under Atlassian Administration → Rovo → Rovo MCP server. Atlassian-supported clients are allowed by default.\n- **IP allowlist:** if the org uses IP allowlisting, tool calls must come from an allowed IP. Hosted AI clients call from the provider's egress IPs, not the user's VPN.\n- **Audit:** every tool call is logged under Insights → Audit log (filter for *Rovo MCP User Actions*).\n\nSee [Control Atlassian Rovo MCP Server settings](https://support.atlassian.com/security-and-access-policies/docs/control-atlassian-rovo-mcp-server-settings/).\n\n## Configuring sooperset (Server/DC or Self-Hosted)\n\n**Cloud with API token:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"CONFLUENCE_URL\": \"https://your-company.atlassian.net/wiki\",\n        \"CONFLUENCE_USERNAME\": \"your-email@company.com\",\n        \"CONFLUENCE_API_TOKEN\": \"<confluence-api-token>\",\n        \"JIRA_URL\": \"https://your-company.atlassian.net\",\n        \"JIRA_USERNAME\": \"your-email@company.com\",\n        \"JIRA_API_TOKEN\": \"<jira-api-token>\"\n      }\n    }\n  }\n}\n```\n\n**Server/Data Center with PAT:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.company.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"<jira-personal-access-token>\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.company.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"<confluence-personal-access-token>\"\n      }\n    }\n  }\n}\n```\n\n### Environment Variables Reference\n\nFrom the project's [`.env.example`](https://github.com/sooperset/mcp-atlassian/blob/main/.env.example).\n\n**Jira:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `JIRA_URL` | Base URL of the Jira instance | Yes |\n| `JIRA_USERNAME` | Account email (Cloud) | Cloud only |\n| `JIRA_API_TOKEN` | API token (Cloud) | Cloud only |\n| `JIRA_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `JIRA_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Confluence:**\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `CONFLUENCE_URL` | Base URL, with `/wiki` for Cloud | Yes |\n| `CONFLUENCE_USERNAME` | Account email (Cloud) | Cloud only |\n| `CONFLUENCE_API_TOKEN` | API token (Cloud) | Cloud only |\n| `CONFLUENCE_PERSONAL_TOKEN` | Personal access token (Server/DC) | Server/DC only |\n| `CONFLUENCE_SSL_VERIFY` | Verify SSL certificates (default: true) | No |\n\n**Scope, safety and tuning:**\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `READ_ONLY_MODE` | Disable all create, update and delete tools | false |\n| `JIRA_PROJECTS_FILTER` | Comma-separated Jira project keys to limit operations to | All |\n| `CONFLUENCE_SPACES_FILTER` | Comma-separated Confluence space keys to limit operations to | All |\n| `TOOLSETS` | Tool groups to enable (`default`, `all`, or names such as `jira_agile`) | All |\n| `ENABLED_TOOLS` | Comma-separated tool names to enable | All in active toolsets |\n| `JIRA_TIMEOUT` / `CONFLUENCE_TIMEOUT` | HTTP timeout in seconds | 75 |\n| `ATLASSIAN_RETRY_TOTAL` | Retry attempts for read requests | Off |\n| `ATLASSIAN_REQUESTS_PER_SECOND` | Client-side request rate cap | Off |\n| `MCP_VERBOSE` / `MCP_VERY_VERBOSE` | INFO / DEBUG logging | WARNING |\n\n## Tool Names Differ by Server\n\nEach server names its tools differently. The examples in `jira-queries.md`, `confluence-operations.md` and `common-workflows.md` use sooperset's names (`jira_search`, `jira_get_issue`, ...). With the Rovo MCP Server, list the tools your client sees (or connect with `?tools=all`) and check [Supported tools](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/supported-tools/) before writing tool calls. Its tools take a `cloudId`; get it from `getAccessibleAtlassianResources`, or pin it in your agent instructions to skip that call.\n\n## Verification and Testing\n\n```bash\n# Rovo MCP Server: confirm the connection and auth status\nclaude mcp list\n\n# sooperset: confirm it starts and sees its environment\nuvx mcp-atlassian --help\nenv | grep -E \"^(JIRA|CONFLUENCE)_\" | sed 's/=.*/=***/'\n```\n\n### Test Connection (sooperset)\n\n```typescript\n// test-connection.ts\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nasync function testConnection(): Promise<void> {\n  const transport = new StdioClientTransport({\n    command: \"uvx\",\n    args: [\"mcp-atlassian\"],\n    env: process.env as Record<string, string>,\n  });\n\n  const client = new Client(\n    { name: \"test-client\", version: \"1.0.0\" },\n    { capabilities: {} }\n  );\n\n  await client.connect(transport);\n\n  // List available tools\n  const tools = await client.listTools();\n  console.log(\"Available tools:\", tools.tools.map(t => t.name));\n\n  // Test a simple read operation\n  const result = await client.callTool({\n    name: \"jira_get_issue\",\n    arguments: { issue_key: \"TEST-1\" }\n  });\n  console.log(\"Test result:\", result);\n\n  await client.close();\n}\n\ntestConnection().catch(console.error);\n```\n\n## When to Use Each Server\n\n**Choose the Rovo MCP Server when:**\n- Using Atlassian Cloud (the default choice)\n- Need products beyond Jira and Confluence\n- Want OAuth without running your own OAuth app\n- Need admin-managed permissions and audit logging\n\n**Choose sooperset when:**\n- Running Jira or Confluence Server/Data Center\n- Need PAT authentication\n- Must run the server inside your own network\n- Want local controls such as `READ_ONLY_MODE` or project and space filters\n\n**Choose xuanxt when:**\n- Want a small TypeScript server for Cloud that you can fork and extend\n\n## Troubleshooting\n\n**Rovo MCP Server: sign-in fails after moving from v1 to v2:**\nSome clients keep a cached client ID or `.well-known` credentials from v1. Clear them and authenticate again.\n\n**Rovo MCP Server: 403 or \"not allowed\":**\n- The client domain is not on the org's allowlist, or\n- IP allowlisting blocks the caller. Hosted clients call from the provider's IPs, so being on the VPN is not enough. Ask an org admin to review both.\n\n**Rovo MCP Server: a tool is missing:**\nIts permission group is not granted (`delete_jira` and `manage_jira` are off by default), or it needs the other auth method (JSM tools need an API token, Compass tools need OAuth).\n\n**\"Authentication failed\" with sooperset:**\n```bash\n# Verify API token is valid (Cloud)\ncurl -u \"<email>:<api-token>\" \\\n  \"https://your-company.atlassian.net/rest/api/3/myself\"\n\n# Verify PAT is valid (Server/DC)\ncurl -H \"Authorization: Bearer <personal-access-token>\" \\\n  \"https://jira.internal.company.com/rest/api/2/myself\"\n```\n\n**Rate limited (HTTP 429) with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"ATLASSIAN_REQUESTS_PER_SECOND\": \"5\",\n        \"ATLASSIAN_RETRY_TOTAL\": \"5\"\n      }\n    }\n  }\n}\n```\n\n**Debug logging with sooperset:**\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"env\": {\n        \"MCP_VERY_VERBOSE\": \"true\"\n      }\n    }\n  }\n}\n```\n\n## Security Best Practices\n\n1. **Prefer OAuth for people, scoped API tokens for automation** - Never share one user's token across a team\n2. **Never commit credentials** - Use environment variables or a secrets manager\n3. **Rotate API tokens regularly** - Set calendar reminders for 90-day rotation\n4. **Grant minimal permission groups** - Leave `delete_jira` and `manage_jira` off unless needed\n5. **Review the audit log** - Rovo MCP tool calls are recorded per user\n6. **Restrict network access** - Use domain and IP allowlists\n\n## Related References\n\n- `authentication-patterns.md` - OAuth 2.1 and API token setup details\n- `jira-queries.md` - JQL syntax after connection is established\n- `confluence-operations.md` - CQL and page operations\n\n<!-- File: skills/atlassian-mcp/SKILL.md (Tokens: ~1286 | Category: anthropic-skill) -->\n---\nname: atlassian-mcp\ndescription: Integrates with Atlassian products to manage project tracking and documentation via MCP protocol. Use when querying Jira issues with JQL filters, creating and updating tickets with custom fields, searching or editing Confluence pages with CQL, managing sprints and backlogs, setting up MCP server authentication, syncing documentation, or debugging Atlassian API integrations.\nlicense: MIT\nmetadata:\n  author: https://github.com/Jeffallan\n  company: https://synergetic.solutions\n  version: \"1.2.0\"\n  domain: platform\n  triggers: Jira, Confluence, Atlassian, MCP, tickets, issues, wiki, JQL, CQL, sprint, backlog, project management\n  role: expert\n  scope: implementation\n  output-format: code\n  related-skills: mcp-developer, api-designer, security-reviewer\n---\n\n# Atlassian MCP Expert\n\n## When to Use This Skill\n\n- Querying Jira issues with JQL filters\n- Searching or creating Confluence pages\n- Automating sprint workflows and backlog management\n- Setting up MCP server authentication (OAuth/API tokens)\n- Syncing meeting notes to Jira tickets\n- Generating documentation from issue data\n- Debugging Atlassian API integration issues\n- Choosing between official vs open-source MCP servers\n\n## Core Workflow\n\n1. **Select server** - Choose official cloud, open-source, or self-hosted MCP server\n2. **Authenticate** - Configure OAuth 2.1, API tokens, or PAT credentials\n3. **Design queries** - Write JQL for Jira, CQL for Confluence; validate with `maxResults=1` before full execution\n4. **Implement workflow** - Build tool calls, handle pagination, error recovery\n5. **Verify permissions** - Confirm required scopes with a read-only probe before any write or bulk operation\n6. **Deploy** - Configure IDE integration, test permissions, monitor rate limits\n\n## Reference Guide\n\nLoad detailed guidance based on context:\n\n| Topic | Reference | Load When |\n|-------|-----------|-----------|\n| Server Setup | `references/mcp-server-setup.md` | Installation, choosing servers, configuration |\n| Jira Operations | `references/jira-queries.md` | JQL syntax, issue CRUD, sprints, boards, issue linking |\n| Confluence Ops | `references/confluence-operations.md` | CQL search, page creation, spaces, comments |\n| Authentication | `references/authentication-patterns.md` | OAuth 2.0, API tokens, permission scopes |\n| Common Workflows | `references/common-workflows.md` | Issue triage, doc sync, sprint automation |\n\n## Quick-Start Examples\n\n### JQL Query Samples\n```\n# Open issues assigned to current user in a sprint\nproject = PROJ AND status = \"In Progress\" AND assignee = currentUser() ORDER BY priority DESC\n\n# Unresolved bugs created in the last 7 days\nproject = PROJ AND issuetype = Bug AND status != Done AND created >= -7d ORDER BY created DESC\n\n# Validate before bulk: test with maxResults=1 first\nproject = PROJ AND sprint in openSprints() AND status = Open ORDER BY created DESC\n```\n\n### CQL Query Samples\n```\n# Find pages updated in a specific space recently\nspace = \"ENG\" AND type = page AND lastModified >= \"2024-01-01\" ORDER BY lastModified DESC\n\n# Search page text for a keyword\nspace = \"ENG\" AND type = page AND text ~ \"deployment runbook\"\n```\n\n### Minimal MCP Server Configuration\nAtlassian Cloud: connect to the hosted Atlassian Rovo MCP Server, then run `/mcp` to sign in with OAuth.\n```bash\nclaude mcp add --transport http atlassian https://mcp.atlassian.com/v2/mcp\n```\n\nServer/Data Center: run the community `mcp-atlassian` server (sooperset) with personal access tokens.\n```json\n{\n  \"mcpServers\": {\n    \"atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-atlassian\"],\n      \"env\": {\n        \"JIRA_URL\": \"https://jira.internal.example.com\",\n        \"JIRA_PERSONAL_TOKEN\": \"${JIRA_PERSONAL_TOKEN}\",\n        \"CONFLUENCE_URL\": \"https://confluence.internal.example.com\",\n        \"CONFLUENCE_PERSONAL_TOKEN\": \"${CONFLUENCE_PERSONAL_TOKEN}\"\n      }\n    }\n  }\n}\n```\n> **Note:** Always load tokens from environment variables or a secrets manager; never hardcode credentials. Tool names differ between servers, see `references/mcp-server-setup.md`.\n\n## Constraints\n\n### MUST DO\n- Respect user permissions and workspace access controls\n- Validate JQL/CQL queries before execution (use `maxResults=1` probe first)\n- Handle rate limits with exponential backoff\n- Use pagination for large result sets (50-100 items per page)\n- Implement error recovery for network failures\n- Log API calls for debugging and audit trails\n- Test with read-only operations first\n- Document required permission scopes\n- Confirm before any write or bulk operation against production data\n\n### MUST NOT DO\n- Hardcode API tokens or OAuth secrets in code\n- Ignore rate limit headers from Atlassian APIs\n- Create issues without validating required fields\n- Skip input sanitization on user-provided query strings\n- Deploy without testing permission boundaries\n- Update production data without confirmation prompts\n- Mix different authentication methods in same session\n- Expose sensitive issue data in logs or error messages\n\n## Output Templates\n\nWhen implementing Atlassian MCP features, provide:\n1. MCP server configuration (JSON/environment vars)\n2. Query examples (JQL/CQL with explanations)\n3. Tool call implementation with error handling\n4. Authentication setup instructions\n5. Brief explanation of permission requirements\n\nMaintained by [@jeffallan](https://github.com/jeffallan), Principal Consultant at [Synergetic Solutions](https://synergetic.solutions)\n\n[Documentation](https://jeffallan.github.io/claude-skills/skills/platform/atlassian-mcp/)\n\n<!-- File: skills/chaos-engineer/references/chaos-tools.md (Tokens: ~3288 | Category: anthropic-skill) -->\n# Chaos Engineering Tools & Automation\n\n## Chaos Monkey (Netflix)\n\n```python\n# Chaos Monkey configuration for Spinnaker\n{\n  \"enabled\": true,\n  \"schedule\": {\n    \"enabled\": true,\n    \"frequency\": 1,  # Run once per day\n    \"frequencyUnit\": \"DAYS\",\n    \"start\": \"09:00\",\n    \"end\": \"15:00\",\n    \"timezone\": \"America/Los_Angeles\"\n  },\n  \"grouping\": \"cluster\",\n  \"regionsAreIndependent\": true,\n  \"exceptions\": [\n    {\n      \"type\": \"Opt-In\",\n      \"account\": \"production\",\n      \"stack\": \"*\",\n      \"detail\": \"*\"\n    }\n  ],\n  \"minTimeBetweenKillsInWorkDays\": 2,\n  \"maxAppsPerDay\": 5,\n  \"clusters\": [\n    {\n      \"app\": \"myapp\",\n      \"stack\": \"production\",\n      \"enabled\": true,\n      \"regions\": [\"us-east-1\", \"us-west-2\"],\n      \"meanTimeBetweenKillsInWorkDays\": 2,\n      \"minTimeBetweenKillsInWorkDays\": 1,\n      \"maxTerminationsPerDay\": 1\n    }\n  ]\n}\n```\n\n```bash\n#!/bin/bash\n# Simpl Chaos Monkey implementation\n\nINSTANCE_COUNT=5\nKILL_PERCENTAGE=20\n\n# Get running instances from ASG\nINSTANCES=$(aws autoscaling describe-auto-scaling-groups \\\n  --auto-scaling-group-names my-asg \\\n  --query 'AutoScalingGroups[0].Instances[?LifecycleState==`InService`].InstanceId' \\\n  --output text)\n\n# Calculate number to terminate\nTOTAL=$(echo \"$INSTANCES\" | wc -w)\nTO_KILL=$(( TOTAL * KILL_PERCENTAGE / 100 ))\n\nif [ $TO_KILL -eq 0 ]; then\n  TO_KILL=1\nfi\n\n# Randomly select and terminate instances\necho \"$INSTANCES\" | tr ' ' '\\n' | shuf | head -n $TO_KILL | while read instance; do\n  echo \"Terminating instance: $instance\"\n  aws ec2 terminate-instances --instance-ids \"$instance\"\n  sleep 30  # Wait between terminations\ndone\n```\n\n## Gremlin Integration\n\n```python\nimport requests\nfrom typing import Literal\n\nclass GremlinClient:\n    def __init__(self, api_key: str, team_id: str):\n        self.api_key = api_key\n        self.team_id = team_id\n        self.base_url = \"https://api.gremlin.com/v1\"\n        self.headers = {\n            \"Authorization\": f\"Key {api_key}\",\n            \"Content-Type\": \"application/json\"\n        }\n\n    def create_cpu_attack(\n        self,\n        targets: list[str],\n        length: int = 60,\n        cores: int = 1,\n        percent: int = 50\n    ):\n        \"\"\"Launch CPU resource attack.\"\"\"\n        payload = {\n            \"command\": {\n                \"type\": \"cpu\",\n                \"args\": [\n                    \"-l\", str(length),\n                    \"-c\", str(cores),\n                    \"-p\", str(percent)\n                ]\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def create_network_attack(\n        self,\n        targets: list[str],\n        attack_type: Literal[\"latency\", \"packet_loss\", \"blackhole\"],\n        length: int = 60,\n        **kwargs\n    ):\n        \"\"\"Launch network attack.\"\"\"\n        args = [\"-l\", str(length)]\n\n        if attack_type == \"latency\":\n            # kwargs: delay_ms, jitter_ms\n            args.extend([\"-m\", str(kwargs.get('delay_ms', 100))])\n            if 'jitter_ms' in kwargs:\n                args.extend([\"-j\", str(kwargs['jitter_ms'])])\n\n        elif attack_type == \"packet_loss\":\n            # kwargs: percent\n            args.extend([\"-p\", str(kwargs.get('percent', 10))])\n\n        elif attack_type == \"blackhole\":\n            # kwargs: port, protocol\n            if 'port' in kwargs:\n                args.extend([\"--port\", str(kwargs['port'])])\n            if 'protocol' in kwargs:\n                args.extend([\"--protocol\", kwargs['protocol']])\n\n        payload = {\n            \"command\": {\n                \"type\": attack_type,\n                \"args\": args\n            },\n            \"target\": {\n                \"type\": \"Exact\",\n                \"exact\": targets\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/attacks/new\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n    def halt_attack(self, attack_id: str):\n        \"\"\"Stop running attack.\"\"\"\n        response = requests.delete(\n            f\"{self.base_url}/attacks/{attack_id}\",\n            headers=self.headers\n        )\n        return response.status_code == 200\n\n    def create_scenario(self, name: str, attacks: list[dict]):\n        \"\"\"Create reusable attack scenario.\"\"\"\n        payload = {\n            \"name\": name,\n            \"description\": f\"Chaos scenario: {name}\",\n            \"graph\": {\n                \"nodes\": attacks\n            }\n        }\n\n        response = requests.post(\n            f\"{self.base_url}/scenarios\",\n            headers=self.headers,\n            json=payload\n        )\n        return response.json()\n\n# Example usage\ngremlin = GremlinClient(api_key=\"<gremlin-api-key>\", team_id=\"<gremlin-team-id>\")\n\n# CPU attack on specific containers\ngremlin.create_cpu_attack(\n    targets=[\"container-id-123\", \"container-id-456\"],\n    length=300,  # 5 minutes\n    cores=2,\n    percent=80\n)\n\n# Network latency attack\ngremlin.create_network_attack(\n    targets=[\"host-abc\"],\n    attack_type=\"latency\",\n    length=180,\n    delay_ms=500,\n    jitter_ms=100\n)\n```\n\n## CI/CD Integration\n\n```yaml\n# GitHub Actions workflow for chaos testing\nname: Chaos Engineering Tests\n\non:\n  schedule:\n    - cron: '0 10 * * 1-5'  # Weekdays at 10 AM\n  workflow_dispatch:  # Manual trigger\n\njobs:\n  chaos-tests:\n    runs-on: ubuntu-latest\n    environment: staging\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v3\n\n      - name: Setup kubectl\n        uses: azure/setup-kubectl@v3\n\n      - name: Configure AWS credentials\n        uses: aws-actions/configure-aws-credentials@v2\n        with:\n          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n          aws-region: us-east-1\n\n      - name: Update kubeconfig\n        run: |\n          aws eks update-kubeconfig --name staging-cluster --region us-east-1\n\n      - name: Install Litmus\n        run: |\n          kubectl apply -f https://litmuschaos.github.io/litmus/litmus-operator-v2.14.0.yaml\n          kubectl wait --for=condition=Ready pods -l app.kubernetes.io/component=operator --timeout=300s\n\n      - name: Run pod-delete chaos experiment\n        run: |\n          kubectl apply -f .github/chaos/pod-delete-experiment.yaml\n          kubectl wait --for=condition=Complete chaosengine/pod-delete-chaos --timeout=600s\n\n      - name: Verify system recovery\n        run: |\n          # Check all pods are running\n          kubectl wait --for=condition=Ready pods -l app=myapp --timeout=300s\n\n          # Verify no error rate spike\n          ERROR_RATE=$(curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\"5..\\\"}[5m])\" | jq -r '.data.result[0].value[1]')\n\n          if (( $(echo \"$ERROR_RATE > 0.01\" | bc -l) )); then\n            echo \"Error rate too high: $ERROR_RATE\"\n            exit 1\n          fi\n\n      - name: Cleanup chaos resources\n        if: always()\n        run: |\n          kubectl delete chaosengine --all\n          kubectl delete chaosexperiments --all\n\n      - name: Report results to Slack\n        if: failure()\n        uses: slackapi/slack-github-action@v1\n        with:\n          payload: |\n            {\n              \"text\": \"Chaos test failed in staging\",\n              \"blocks\": [\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"Chaos engineering test failed. System did not recover properly.\"\n                  }\n                }\n              ]\n            }\n        env:\n          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}\n```\n\n## Jenkins Pipeline\n\n```groovy\n// Jenkinsfile for chaos testing\npipeline {\n    agent any\n\n    parameters {\n        choice(\n            name: 'ENVIRONMENT',\n            choices: ['dev', 'staging'],\n            description: 'Target environment'\n        )\n        choice(\n            name: 'CHAOS_TYPE',\n            choices: ['pod-delete', 'network-latency', 'cpu-stress'],\n            description: 'Type of chaos experiment'\n        )\n        string(\n            name: 'DURATION',\n            defaultValue: '300',\n            description: 'Chaos duration in seconds'\n        )\n    }\n\n    stages {\n        stage('Pre-flight Check') {\n            steps {\n                script {\n                    // Verify steady state before chaos\n                    def errorRate = sh(\n                        script: '''\n                            curl -s \"http://prometheus/api/v1/query?query=rate(http_requests_total{status=~\\\\\"5..\\\\\"}[5m])\" | jq -r '.data.result[0].value[1]'\n                        ''',\n                        returnStdout: true\n                    ).trim()\n\n                    if (errorRate.toFloat() > 0.01) {\n                        error(\"System not in steady state. Error rate: ${errorRate}\")\n                    }\n                }\n            }\n        }\n\n        stage('Run Chaos Experiment') {\n            steps {\n                script {\n                    def chaosManifest = \"\"\"\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: jenkins-chaos-${env.BUILD_NUMBER}\n  namespace: ${params.ENVIRONMENT}\nspec:\n  appinfo:\n    appns: '${params.ENVIRONMENT}'\n    applabel: 'app=myapp'\n    appkind: 'deployment'\n  chaosServiceAccount: litmus-admin\n  experiments:\n    - name: ${params.CHAOS_TYPE}\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '${params.DURATION}'\n\"\"\"\n\n                    writeFile file: 'chaos-manifest.yaml', text: chaosManifest\n\n                    sh '''\n                        kubectl apply -f chaos-manifest.yaml\n                        kubectl wait --for=condition=Complete chaosengine/jenkins-chaos-${BUILD_NUMBER} --timeout=900s\n                    '''\n                }\n            }\n        }\n\n        stage('Verify Recovery') {\n            steps {\n                sh '''\n                    # Wait for system to stabilize\n                    sleep 60\n\n                    # Check pod status\n                    kubectl get pods -n ${ENVIRONMENT} -l app=myapp\n\n                    # Verify all pods running\n                    READY_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '[.items[] | select(.status.phase==\"Running\")] | length')\n                    TOTAL_PODS=$(kubectl get pods -n ${ENVIRONMENT} -l app=myapp -o json | jq '.items | length')\n\n                    if [ \"$READY_PODS\" -ne \"$TOTAL_PODS\" ]; then\n                        echo \"Not all pods recovered: $READY_PODS/$TOTAL_PODS ready\"\n                        exit 1\n                    fi\n                '''\n            }\n        }\n\n        stage('Extract Learnings') {\n            steps {\n                script {\n                    // Get chaos result\n                    def chaosResult = sh(\n                        script: \"kubectl get chaosresult -n ${params.ENVIRONMENT} -o json\",\n                        returnStdout: true\n                    )\n\n                    // Parse and store results\n                    writeFile file: \"chaos-result-${env.BUILD_NUMBER}.json\", text: chaosResult\n\n                    // Archive results\n                    archiveArtifacts artifacts: \"chaos-result-${env.BUILD_NUMBER}.json\"\n                }\n            }\n        }\n    }\n\n    post {\n        always {\n            // Cleanup\n            sh '''\n                kubectl delete chaosengine jenkins-chaos-${BUILD_NUMBER} -n ${ENVIRONMENT} || true\n            '''\n        }\n\n        failure {\n            // Notify team\n            slackSend(\n                color: 'danger',\n                message: \"Chaos test failed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}\"\n            )\n        }\n\n        success {\n            slackSend(\n                color: 'good',\n                message: \"Chaos test passed: ${params.CHAOS_TYPE} in ${params.ENVIRONMENT}. System recovered successfully.\"\n            )\n        }\n    }\n}\n```\n\n## Continuous Chaos Dashboard\n\n```python\n# Flask app for chaos monitoring dashboard\nfrom flask import Flask, render_template, jsonify\nimport requests\nfrom datetime import datetime, timedelta\n\napp = Flask(__name__)\n\nclass ChaosDashboard:\n    def __init__(self, prometheus_url: str):\n        self.prometheus = prometheus_url\n\n    def get_experiment_metrics(self, hours: int = 24):\n        \"\"\"Get chaos experiment results from last N hours.\"\"\"\n        end = datetime.now()\n        start = end - timedelta(hours=hours)\n\n        query = f'''\n            sum by (experiment, verdict) (\n                increase(litmuschaos_experiment_verdict[{hours}h])\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n    def get_mttr_trend(self):\n        \"\"\"Get MTTR trend over time.\"\"\"\n        query = '''\n            avg_over_time(\n                avg(\n                    time() - timestamp(\n                        kube_pod_status_phase{phase=\"Running\"} == 1\n                    )\n                )[7d:]\n            )\n        '''\n\n        response = requests.get(\n            f\"{self.prometheus}/api/v1/query\",\n            params={\"query\": query}\n        )\n\n        return response.json()\n\n@app.route('/api/chaos-summary')\ndef chaos_summary():\n    dashboard = ChaosDashboard(prometheus_url=\"http://prometheus:9090\")\n\n    return jsonify({\n        \"experiments\": dashboard.get_experiment_metrics(hours=24),\n        \"mttr_trend\": dashboard.get_mttr_trend(),\n        \"timestamp\": datetime.now().isoformat()\n    })\n\nif __name__ == '__main__':\n    app.run(host='0.0.0.0', port=5000)\n```\n\n## Quick Reference\n\n| Tool | Use Case | Integration |\n|------|----------|-------------|\n| Chaos Monkey | Random instance termination | Spinnaker/AWS ASG |\n| Gremlin | SaaS chaos platform | API/Web UI |\n| Litmus | Kubernetes chaos | Kubectl/Helm |\n| Chaos Mesh | Advanced K8s chaos | CRDs/Dashboard |\n| Toxiproxy | Network proxy chaos | Docker/API |\n| Pumba | Container chaos | Docker CLI |\n\n<!-- File: skills/chaos-engineer/references/experiment-design.md (Tokens: ~1522 | Category: anthropic-skill) -->\n# Chaos Experiment Design\n\n## Experiment Template\n\n```yaml\nname: \"Database Connection Pool Exhaustion\"\nhypothesis: \"When the database connection pool is exhausted, the application will gracefully degrade and return 503 errors without cascading failures\"\n\nsteady_state:\n  metrics:\n    - name: \"Error Rate\"\n      threshold: \"< 0.1%\"\n      source: \"prometheus\"\n      query: \"rate(http_requests_total{status=~'5..'}[5m])\"\n    - name: \"Latency P99\"\n      threshold: \"< 500ms\"\n      source: \"datadog\"\n    - name: \"Active Connections\"\n      threshold: \"> 10\"\n      query: \"pg_stat_activity_count\"\n\nblast_radius:\n  environment: \"staging\"\n  traffic_percentage: 10\n  duration_seconds: 300\n  max_error_rate: \"5%\"\n  auto_rollback: true\n\ninjection:\n  type: \"resource_exhaustion\"\n  target: \"database_connections\"\n  method: \"connection_leak\"\n  parameters:\n    leak_rate: 5  # connections per second\n    max_leaked: 50\n\nsafety:\n  rollback_triggers:\n    - \"error_rate > 5%\"\n    - \"manual_kill_switch\"\n    - \"duration_exceeded\"\n  rollback_time_limit_seconds: 30\n  alerts:\n    - slack: \"#chaos-engineering\"\n    - pagerduty: \"chaos-team\"\n\nsuccess_criteria:\n  - \"Circuit breakers activate within 10s\"\n  - \"503 errors returned (not 500)\"\n  - \"No cascading failures to other services\"\n  - \"System recovers within 60s of rollback\"\n```\n\n## Hypothesis Formulation\n\n```python\ndef create_hypothesis(component: str, failure: str, expected_behavior: str) -> dict:\n    \"\"\"\n    Create well-formed chaos hypothesis.\n\n    Format: \"Given [normal state], when [failure occurs],\n             then [expected behavior], measured by [metrics]\"\n    \"\"\"\n    return {\n        \"given\": f\"System is in steady state with {component} functioning normally\",\n        \"when\": f\"{failure} occurs\",\n        \"then\": expected_behavior,\n        \"measured_by\": [\n            \"Error rate remains below threshold\",\n            \"Latency stays within SLO\",\n            \"No data loss or corruption\",\n            \"Recovery time within RTO\"\n        ]\n    }\n\n# Example\nhypothesis = create_hypothesis(\n    component=\"payment service\",\n    failure=\"50% packet loss to payment gateway\",\n    expected_behavior=\"Requests timeout gracefully, retry queue activates, \"\n                     \"users see clear error messages\"\n)\n```\n\n## Blast Radius Control\n\n```python\nfrom dataclasses import dataclass\nfrom enum import Enum\n\nclass BlastRadiusLevel(Enum):\n    MINIMAL = \"single_instance_dev\"\n    LOW = \"single_instance_staging\"\n    MEDIUM = \"percentage_staging\"\n    HIGH = \"percentage_production\"\n    CRITICAL = \"full_production\"\n\n@dataclass\nclass BlastRadiusConfig:\n    level: BlastRadiusLevel\n    environment: str\n    target_percentage: float  # 0-100\n    canary_users: list[str]\n    feature_flag: str\n    auto_rollback: bool\n    max_duration_seconds: int\n\n    def validate(self):\n        \"\"\"Enforce safety rules.\"\"\"\n        if self.level == BlastRadiusLevel.CRITICAL:\n            raise ValueError(\"CRITICAL blast radius requires explicit approval\")\n\n        if self.environment == \"production\" and self.target_percentage > 10:\n            if not self.feature_flag or not self.auto_rollback:\n                raise ValueError(\"Production >10% requires feature flag AND auto-rollback\")\n\n        if self.max_duration_seconds > 600:\n            raise ValueError(\"Max duration cannot exceed 10 minutes without approval\")\n\n# Progressive blast radius expansion\ndef progressive_rollout() -> list[BlastRadiusConfig]:\n    return [\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MINIMAL,\n            environment=\"dev\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_dev\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.LOW,\n            environment=\"staging\",\n            target_percentage=100,\n            canary_users=[],\n            feature_flag=\"chaos_staging\",\n            auto_rollback=True,\n            max_duration_seconds=600\n        ),\n        BlastRadiusConfig(\n            level=BlastRadiusLevel.MEDIUM,\n            environment=\"production\",\n            target_percentage=1,\n            canary_users=[\"internal_team\"],\n            feature_flag=\"chaos_prod_canary\",\n            auto_rollback=True,\n            max_duration_seconds=300\n        )\n    ]\n```\n\n## Safety Mechanisms\n\n```python\nimport asyncio\nfrom typing import Callable\n\nclass ChaosExperimentSafety:\n    def __init__(self, config: dict):\n        self.config = config\n        self.kill_switch_active = False\n        self.metrics = {}\n\n    async def run_with_safety(self, chaos_fn: Callable):\n        \"\"\"Execute chaos with automatic safety checks.\"\"\"\n        # Pre-flight checks\n        if not await self.verify_steady_state():\n            raise Exception(\"System not in steady state - aborting\")\n\n        # Set up rollback trigger\n        rollback_task = asyncio.create_task(self.monitor_for_rollback())\n        chaos_task = asyncio.create_task(chaos_fn())\n\n        try:\n            # Wait for either chaos completion or rollback trigger\n            done, pending = await asyncio.wait(\n                [chaos_task, rollback_task],\n                return_when=asyncio.FIRST_COMPLETED\n            )\n\n            if rollback_task in done:\n                # Rollback triggered - cancel chaos\n                chaos_task.cancel()\n                await self.rollback()\n\n        finally:\n            await self.ensure_system_recovery()\n\n    async def verify_steady_state(self) -> bool:\n        \"\"\"Check all steady state metrics are within threshold.\"\"\"\n        for metric in self.config['steady_state']['metrics']:\n            value = await self.query_metric(metric['query'])\n            if not self.within_threshold(value, metric['threshold']):\n                return False\n        return True\n\n    async def monitor_for_rollback(self):\n        \"\"\"Continuously monitor for rollback triggers.\"\"\"\n        start_time = asyncio.get_event_loop().time()\n\n        while True:\n            # Check duration limit\n            if asyncio.get_event_loop().time() - start_time > \\\n               self.config['blast_radius']['duration_seconds']:\n                return \"duration_exceeded\"\n\n            # Check manual kill switch\n            if self.kill_switch_active:\n                return \"manual_kill_switch\"\n\n            # Check error rate\n            error_rate = await self.query_metric(\"error_rate\")\n            if error_rate > float(self.config['blast_radius']['max_error_rate'].strip('%')):\n                return \"error_rate_exceeded\"\n\n            await asyncio.sleep(5)  # Check every 5 seconds\n```\n\n## Quick Reference\n\n| Phase | Key Actions | Time Limit |\n|-------|-------------|------------|\n| Design | Hypothesis, metrics, blast radius | 1 hour |\n| Review | Team review, safety check | 30 min |\n| Prepare | Setup monitoring, rollback | 1 hour |\n| Execute | Run experiment, monitor | 5-10 min |\n| Rollback | Restore steady state | < 30 sec |\n| Learn | Document findings, plan fixes | 2 hours |\n\n<!-- File: skills/chaos-engineer/references/game-days.md (Tokens: ~3069 | Category: anthropic-skill) -->\n# Game Day Planning & Execution\n\n## Game Day Planning Template\n\n```yaml\ngame_day:\n  name: \"Database Failover Drill\"\n  date: \"2025-01-15\"\n  time: \"10:00-12:00 PST\"\n  environment: \"staging\"  # Start in staging\n\n  objectives:\n    - \"Verify RDS failover to standby in under 2 minutes\"\n    - \"Validate application auto-reconnect logic\"\n    - \"Test monitoring and alerting effectiveness\"\n    - \"Practice incident response procedures\"\n\n  participants:\n    facilitator: \"chaos-engineer@company.com\"\n    observers:\n      - \"sre-team@company.com\"\n      - \"dev-team@company.com\"\n    responders:\n      - \"on-call-engineer@company.com\"\n      - \"database-admin@company.com\"\n    stakeholders:\n      - \"engineering-manager@company.com\"\n\n  scenarios:\n    - name: \"Primary database instance failure\"\n      duration_minutes: 30\n      steps:\n        - action: \"Force RDS instance reboot with failover\"\n          expected: \"Failover to standby in <2 min\"\n          success_criteria:\n            - \"Downtime < 2 minutes\"\n            - \"No data loss\"\n            - \"Alerts fired correctly\"\n\n    - name: \"Network partition to database\"\n      duration_minutes: 20\n      steps:\n        - action: \"Block network traffic to RDS security group\"\n          expected: \"Application switches to read replica\"\n          success_criteria:\n            - \"Read-only mode activated\"\n            - \"User-facing error messages clear\"\n\n  communication_plan:\n    announcement_channel: \"#game-day-announcements\"\n    war_room: \"Zoom link: https://...\"\n    status_updates_every: \"5 minutes\"\n    escalation_contacts:\n      - name: \"VP Engineering\"\n        phone: \"+1-555-0100\"\n        threshold: \"downtime > 5 minutes\"\n\n  rollback_plan:\n    automatic_rollback_triggers:\n      - \"production traffic affected\"\n      - \"customer complaints received\"\n      - \"error_rate > 10%\"\n    manual_rollback_command: \"aws rds reboot-db-instance --db-instance-identifier primary --force-failover\"\n    rollback_time_limit_seconds: 60\n\n  success_metrics:\n    - metric: \"RTO (Recovery Time Objective)\"\n      target: \"< 2 minutes\"\n      measurement: \"time between failure and full recovery\"\n    - metric: \"Alert accuracy\"\n      target: \"100%\"\n      measurement: \"all expected alerts fired\"\n    - metric: \"Team response time\"\n      target: \"< 5 minutes\"\n      measurement: \"time to acknowledge incident\"\n\n  post_mortem:\n    scheduled_for: \"2025-01-16 14:00\"\n    template: \"game-day-retro.md\"\n    required_attendees: \"all participants\"\n```\n\n## Game Day Runbook\n\n```markdown\n# Database Failover Game Day Runbook\n\n**Date**: January 15, 2025\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Pre-Game Checklist (T-30 min)\n\n- [ ] Verify all participants joined war room\n- [ ] Confirm monitoring dashboards accessible\n- [ ] Test rollback procedures work\n- [ ] Announce game day start in #engineering\n- [ ] Verify staging environment healthy\n- [ ] Set up screen recording for timeline\n- [ ] Prepare incident timeline spreadsheet\n\n## Timeline\n\n### 10:00 - Introduction (10 min)\n- Facilitator explains objectives\n- Review scenarios and success criteria\n- Confirm roles and communication channels\n- Remind everyone: this is a learning exercise\n\n### 10:10 - Scenario 1: Primary DB Failure (30 min)\n\n**T+0 (10:10)** - Inject failure\n```bash\naws rds reboot-db-instance \\\n  --db-instance-identifier staging-primary \\\n  --force-failover\n```\n\n**Expected Timeline**:\n- T+0: Reboot initiated\n- T+30s: Primary becomes unavailable\n- T+60s: DNS updated to standby\n- T+90s: Application reconnects\n- T+120s: Full recovery\n\n**Observer Tasks**:\n- [ ] Record exact time of failure injection\n- [ ] Monitor application error logs\n- [ ] Track alert notifications\n- [ ] Document team response actions\n- [ ] Screenshot dashboard states\n\n**Questions to Answer**:\n- How long until first alert?\n- Did application auto-reconnect?\n- Were customers impacted?\n- What manual interventions needed?\n\n### 10:40 - Debrief Scenario 1 (10 min)\n- What went well?\n- What could improve?\n- Any surprises?\n- Action items identified\n\n### 10:50 - Scenario 2: Network Partition (20 min)\n\n**T+0 (10:50)** - Inject failure\n```bash\n# Block database security group ingress\naws ec2 revoke-security-group-ingress \\\n  --group-id sg-xxxxx \\\n  --protocol tcp \\\n  --port 5432 \\\n  --cidr 10.0.0.0/16\n```\n\n**Expected Behavior**:\n- Connection timeouts occur\n- Circuit breaker opens\n- Read-only mode activates\n- Clear error messages shown\n\n**Observer Tasks**:\n- [ ] Monitor circuit breaker state\n- [ ] Verify read-replica failover\n- [ ] Check user-facing error messages\n- [ ] Track degraded service duration\n\n### 11:10 - Debrief Scenario 2 (10 min)\n\n### 11:20 - Scenario 3: Surprise! (20 min)\n\n**Facilitator Note**: Don't announce this scenario details beforehand.\nTest true incident response capability.\n\n**Hidden Scenario**: Combination failure\n1. Database connection pool leak\n2. Simultaneous cache invalidation\n\n```python\n# Connection leak simulator\nimport psycopg2\nconnections = []\nfor i in range(100):\n    conn = psycopg2.connect(DATABASE_URL)\n    connections.append(conn)\n    # Intentionally don't close\n```\n\n**Observer Tasks**:\n- [ ] How long to identify root cause?\n- [ ] Communication effectiveness\n- [ ] Cross-team coordination\n- [ ] Escalation decisions\n\n### 11:40 - Final Debrief & Wrap-up (20 min)\n\n**Debrief Questions**:\n1. What worked well?\n2. What didn't work?\n3. What surprised us?\n4. What are our top 3 action items?\n5. When should we run this again?\n\n## Post-Game Checklist\n\n- [ ] Restore all services to normal state\n- [ ] Verify no lingering issues\n- [ ] Collect all observer notes\n- [ ] Export metrics and dashboards\n- [ ] Schedule post-mortem meeting\n- [ ] Send thank-you to participants\n- [ ] Create action item tickets\n- [ ] Update runbooks based on learnings\n```\n\n## Game Day Observation Template\n\n```python\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom typing import List\n\n@dataclass\nclass GameDayObservation:\n    timestamp: datetime\n    observer: str\n    scenario: str\n    observation: str\n    category: str  # technical, process, communication, surprise\n    severity: str  # info, concern, critical\n    photo_url: str = \"\"\n\n@dataclass\nclass GameDayMetrics:\n    scenario_name: str\n    start_time: datetime\n    end_time: datetime\n\n    # Technical metrics\n    time_to_detect_seconds: float\n    time_to_respond_seconds: float\n    time_to_recover_seconds: float\n    error_rate_peak: float\n    alerts_fired: List[str] = field(default_factory=list)\n    alerts_missed: List[str] = field(default_factory=list)\n\n    # Team metrics\n    responders_involved: int\n    escalations_needed: int\n    communication_gaps: List[str] = field(default_factory=list)\n\n    # Success criteria\n    met_rto: bool = False\n    met_rpo: bool = False\n    zero_customer_impact: bool = False\n\n    def calculate_mttr(self) -> float:\n        \"\"\"Mean Time To Recovery\"\"\"\n        return (self.end_time - self.start_time).total_seconds()\n\n    def success_rate(self) -> float:\n        \"\"\"Percentage of success criteria met\"\"\"\n        criteria = [\n            self.met_rto,\n            self.met_rpo,\n            self.zero_customer_impact,\n            len(self.alerts_missed) == 0\n        ]\n        return sum(criteria) / len(criteria) * 100\n\n# Example usage\nmetrics = GameDayMetrics(\n    scenario_name=\"Database Failover\",\n    start_time=datetime(2025, 1, 15, 10, 10, 0),\n    end_time=datetime(2025, 1, 15, 10, 12, 30),\n    time_to_detect_seconds=15.0,\n    time_to_respond_seconds=45.0,\n    time_to_recover_seconds=150.0,\n    error_rate_peak=0.05,\n    alerts_fired=[\"DatabaseConnectionError\", \"HighLatency\"],\n    alerts_missed=[\"FailoverInitiated\"],\n    responders_involved=3,\n    escalations_needed=0,\n    met_rto=True,\n    met_rpo=True,\n    zero_customer_impact=True\n)\n\nprint(f\"MTTR: {metrics.calculate_mttr()}s\")\nprint(f\"Success Rate: {metrics.success_rate()}%\")\n```\n\n## Surprise Scenarios Library\n\n```yaml\n# Keep these secret until game day!\nsurprise_scenarios:\n  - name: \"Cascading Failure\"\n    description: \"Primary failure triggers secondary issue\"\n    injection:\n      - \"Database failover (expected)\"\n      - \"Cache eviction due to new primary IP (surprise!)\"\n    learning_goals:\n      - \"Do we understand our dependencies?\"\n      - \"Can we handle multiple simultaneous issues?\"\n\n  - name: \"Monitoring Blind Spot\"\n    description: \"Failure that doesn't trigger alerts\"\n    injection:\n      - \"Gradual connection pool leak\"\n      - \"No immediate alerts fire\"\n    learning_goals:\n      - \"How do we discover issues without alerts?\"\n      - \"Do we have adequate monitoring coverage?\"\n\n  - name: \"Documentation Failure\"\n    description: \"Runbook is outdated or incorrect\"\n    setup:\n      - \"Modify runbook to have incorrect commands\"\n      - \"Or remove runbook entirely\"\n    learning_goals:\n      - \"Can team problem-solve without docs?\"\n      - \"How quickly can we update documentation?\"\n\n  - name: \"Key Person Unavailable\"\n    description: \"Subject matter expert is unreachable\"\n    setup:\n      - \"Ask SME to not respond for 15 minutes\"\n    learning_goals:\n      - \"Is knowledge properly distributed?\"\n      - \"Can team succeed without specific person?\"\n\n  - name: \"Partial Degradation\"\n    description: \"Service works but slowly\"\n    injection:\n      - \"Add 5 second latency instead of complete failure\"\n    learning_goals:\n      - \"Do we detect performance degradation?\"\n      - \"What are our latency SLOs?\"\n```\n\n## Post-Game Report Template\n\n```markdown\n# Game Day Report: Database Failover\n\n**Date**: January 15, 2025\n**Participants**: 12\n**Duration**: 2 hours\n**Environment**: Staging\n\n## Executive Summary\n\nConducted database failover game day to test RDS high availability and\napplication resilience. Successfully failed over database in 2.5 minutes\n(target: 2 min). Discovered 3 critical gaps in monitoring and 2 process\nimprovements needed.\n\n## Metrics\n\n| Metric | Target | Actual | Status |\n|--------|--------|--------|--------|\n| Time to Detect | < 30s | 15s | PASS |\n| Time to Respond | < 5min | 4min 20s | PASS |\n| Time to Recover | < 2min | 2min 30s | FAIL |\n| Alert Accuracy | 100% | 66% | FAIL |\n| Zero Customer Impact | Yes | Yes | PASS |\n\n## What Went Well\n\n1. Team responded quickly (4m 20s vs 5m target)\n2. Runbooks were accurate and helpful\n3. Communication was clear and frequent\n4. No customer impact during any scenario\n5. Application auto-reconnect worked perfectly\n\n## What Didn't Go Well\n\n1. Missing alert for failover initiation\n2. Took 30s longer than target to recover\n3. Connection pool exhaustion not detected\n4. Dashboard didn't show replica lag clearly\n5. Escalation contacts list was outdated\n\n## Surprises\n\n1. Cache invalidation cascaded from DB failover (unexpected)\n2. Read replica had 45s replication lag we didn't know about\n3. Application retried too aggressively during failover\n4. Team found a workaround we hadn't documented\n\n## Action Items\n\n| Action | Owner | Due Date | Priority |\n|--------|-------|----------|----------|\n| Add alert for RDS failover events | @sre-team | Jan 20 | P0 |\n| Update dashboard with replica lag | @platform | Jan 22 | P1 |\n| Document cache invalidation behavior | @dev-team | Jan 25 | P1 |\n| Add connection pool monitoring | @sre-team | Jan 27 | P0 |\n| Update escalation contact list | @manager | Jan 18 | P2 |\n| Tune application retry backoff | @dev-team | Feb 1 | P1 |\n\n## Lessons Learned\n\n1. **Monitoring Gaps**: We had blind spots in replica monitoring\n2. **Cascading Effects**: DB changes affect cache in non-obvious ways\n3. **Team Knowledge**: Cross-training is working well\n4. **Documentation**: Runbooks saved time, keep them updated\n\n## Next Game Day\n\n**Proposed Date**: March 15, 2025\n**Scenario**: Multi-region failover\n**Scope**: Production (with safeguards)\n\n## Appendix\n\n- Full timeline spreadsheet: [link]\n- Screen recordings: [link]\n- Metrics dashboard export: [link]\n- Raw observation notes: [link]\n```\n\n## Quick Reference\n\n| Phase | Duration | Key Activities |\n|-------|----------|----------------|\n| Planning | 2 weeks | Define scenarios, invite participants |\n| Pre-game | 30 min | Setup, verify environment, brief team |\n| Execution | 2 hours | Run scenarios, observe, document |\n| Debrief | 30 min | Immediate learnings, quick wins |\n| Post-mortem | 1 week later | Detailed analysis, action items |\n| Follow-up | 1 month | Verify improvements, plan next game day |\n\n<!-- File: skills/chaos-engineer/references/infrastructure-chaos.md (Tokens: ~2387 | Category: anthropic-skill) -->\n# Infrastructure Chaos Engineering\n\n## Network Latency Injection\n\n```python\n# Using toxiproxy for network chaos\nimport requests\nfrom typing import Literal\n\nclass ToxiproxyClient:\n    def __init__(self, host: str = \"localhost:8474\"):\n        self.base_url = f\"http://{host}\"\n\n    def create_proxy(self, name: str, listen: str, upstream: str):\n        \"\"\"Create proxy to inject failures.\"\"\"\n        response = requests.post(f\"{self.base_url}/proxies\", json={\n            \"name\": name,\n            \"listen\": listen,\n            \"upstream\": upstream,\n            \"enabled\": True\n        })\n        return response.json()\n\n    def add_latency(self, proxy: str, latency_ms: int, jitter_ms: int = 0):\n        \"\"\"Add latency toxic.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"latency\",\n                \"type\": \"latency\",\n                \"attributes\": {\n                    \"latency\": latency_ms,\n                    \"jitter\": jitter_ms\n                }\n            }\n        )\n\n    def add_bandwidth_limit(self, proxy: str, rate_kb: int):\n        \"\"\"Limit bandwidth.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"bandwidth\",\n                \"type\": \"bandwidth\",\n                \"attributes\": {\"rate\": rate_kb}\n            }\n        )\n\n    def add_timeout(self, proxy: str, timeout_ms: int):\n        \"\"\"Add connection timeout.\"\"\"\n        return requests.post(\n            f\"{self.base_url}/proxies/{proxy}/toxics\",\n            json={\n                \"name\": \"timeout\",\n                \"type\": \"timeout\",\n                \"attributes\": {\"timeout\": timeout_ms}\n            }\n        )\n\n# Example usage\ntoxiproxy = ToxiproxyClient()\n\n# Create proxy to database\ntoxiproxy.create_proxy(\n    name=\"postgres\",\n    listen=\"0.0.0.0:5433\",\n    upstream=\"postgres:5432\"\n)\n\n# Inject 200ms latency with 50ms jitter\ntoxiproxy.add_latency(\"postgres\", latency_ms=200, jitter_ms=50)\n```\n\n## AWS Zone Failure Simulation\n\n```python\nimport boto3\nfrom datetime import datetime, timedelta\n\nclass AWSChaosSimulator:\n    def __init__(self, region: str):\n        self.ec2 = boto3.client('ec2', region_name=region)\n        self.asg = boto3.client('autoscaling', region_name=region)\n        self.elb = boto3.client('elbv2', region_name=region)\n\n    def simulate_az_failure(\n        self,\n        availability_zone: str,\n        asg_name: str,\n        duration_minutes: int = 10\n    ):\n        \"\"\"\n        Simulate AZ failure by terminating instances in specific AZ.\n        Auto Scaling Group will launch replacements in other AZs.\n        \"\"\"\n        # Find instances in target AZ\n        instances = self.ec2.describe_instances(Filters=[\n            {'Name': 'tag:aws:autoscaling:groupName', 'Values': [asg_name]},\n            {'Name': 'availability-zone', 'Values': [availability_zone]},\n            {'Name': 'instance-state-name', 'Values': ['running']}\n        ])\n\n        instance_ids = [\n            i['InstanceId']\n            for r in instances['Reservations']\n            for i in r['Instances']\n        ]\n\n        if not instance_ids:\n            return {\"status\": \"no_instances\", \"instances\": []}\n\n        # Suspend AZ-specific scaling activities\n        self.asg.suspend_processes(\n            AutoScalingGroupName=asg_name,\n            ScalingProcesses=['AZRebalance']\n        )\n\n        # Terminate instances to simulate AZ failure\n        self.ec2.terminate_instances(InstanceIds=instance_ids)\n\n        return {\n            \"status\": \"simulated\",\n            \"availability_zone\": availability_zone,\n            \"terminated_instances\": instance_ids,\n            \"recovery_time\": datetime.now() + timedelta(minutes=duration_minutes)\n        }\n\n    def drain_az_from_load_balancer(\n        self,\n        target_group_arn: str,\n        availability_zone: str\n    ):\n        \"\"\"Remove AZ from load balancer to simulate zone failure.\"\"\"\n        # Get current target health\n        health = self.elb.describe_target_health(\n            TargetGroupArn=target_group_arn\n        )\n\n        # Find targets in AZ\n        targets_to_deregister = []\n        for target in health['TargetHealthDescriptions']:\n            # Get instance details\n            instance = self.ec2.describe_instances(\n                InstanceIds=[target['Target']['Id']]\n            )\n            if instance['Reservations'][0]['Instances'][0]['Placement']['AvailabilityZone'] == availability_zone:\n                targets_to_deregister.append(target['Target'])\n\n        # Deregister targets\n        if targets_to_deregister:\n            self.elb.deregister_targets(\n                TargetGroupArn=target_group_arn,\n                Targets=targets_to_deregister\n            )\n\n        return {\n            \"deregistered_targets\": len(targets_to_deregister),\n            \"availability_zone\": availability_zone\n        }\n```\n\n## Server Resource Exhaustion\n\n```bash\n#!/bin/bash\n# CPU stress test using stress-ng\n\n# Install stress-ng\nsudo apt-get install -y stress-ng\n\n# Stress CPU - use 80% of available cores for 5 minutes\nstress-ng --cpu $(nproc --all) --cpu-load 80 --timeout 5m\n\n# Memory stress - consume 70% of available memory\nTOTAL_MEM_MB=$(free -m | awk 'NR==2{print $2}')\nSTRESS_MEM_MB=$((TOTAL_MEM_MB * 70 / 100))\nstress-ng --vm 1 --vm-bytes ${STRESS_MEM_MB}M --timeout 5m\n\n# Disk I/O stress - 4 workers doing sequential writes\nstress-ng --hdd 4 --hdd-bytes 1G --timeout 5m\n\n# Network bandwidth saturation\n# Using iperf3 to saturate network\niperf3 -c target-server -t 300 -P 10  # 10 parallel streams for 5 minutes\n```\n\n## Docker Container Chaos with Pumba\n\n```bash\n#!/bin/bash\n# Pumba - chaos testing for Docker\n\n# Kill random container every 30 seconds\npumba --interval 30s kill --signal SIGKILL \"re2:^myapp\"\n\n# Pause container for 15 seconds, then resume\npumba pause --duration 15s myapp-container\n\n# Add network delay to container\npumba netem \\\n  --duration 5m \\\n  --interface eth0 \\\n  delay \\\n    --time 300 \\\n    --jitter 50 \\\n  myapp-container\n\n# Packet loss - drop 20% of packets\npumba netem \\\n  --duration 5m \\\n  loss \\\n    --percent 20 \\\n  myapp-container\n\n# Limit bandwidth to 1Mbps\npumba netem \\\n  --duration 5m \\\n  rate \\\n    --rate 1mbit \\\n  myapp-container\n\n# Stop all containers matching pattern for 2 minutes\npumba stop --duration 2m \"re2:^production-.*\"\n```\n\n## DNS Failure Simulation\n\n```python\n# Using dnsmasq or editing /etc/hosts for DNS chaos\n\nimport subprocess\nimport time\nfrom contextlib import contextmanager\n\nclass DNSChaos:\n    @staticmethod\n    @contextmanager\n    def block_domain(domain: str, duration_seconds: int = 60):\n        \"\"\"Block DNS resolution for domain by pointing to localhost.\"\"\"\n        try:\n            # Add entry to /etc/hosts\n            subprocess.run([\n                'sudo', 'sh', '-c',\n                f'echo \"127.0.0.1 {domain}\" >> /etc/hosts'\n            ], check=True)\n\n            print(f\"Blocked DNS for {domain}\")\n            yield\n\n        finally:\n            # Wait for duration\n            time.sleep(duration_seconds)\n\n            # Remove entry from /etc/hosts\n            subprocess.run([\n                'sudo', 'sed', '-i',\n                f'/127.0.0.1 {domain}/d',\n                '/etc/hosts'\n            ], check=True)\n\n            print(f\"Restored DNS for {domain}\")\n\n    @staticmethod\n    def add_dns_latency(domain: str, latency_ms: int):\n        \"\"\"Add latency to DNS queries using dnsmasq.\"\"\"\n        config = f\"\"\"\n        # Add to /etc/dnsmasq.conf\n        address=/{domain}/127.0.0.1\n        min-cache-ttl=0\n\n        # Restart dnsmasq with delay\n        \"\"\"\n        return config\n\n# Usage\nwith DNSChaos.block_domain('api.external-service.com', duration_seconds=120):\n    # Run tests while DNS is blocked\n    print(\"DNS blocked - testing fallback behavior\")\n```\n\n## Certificate Expiry Simulation\n\n```python\nfrom datetime import datetime, timedelta\nfrom cryptography import x509\nfrom cryptography.x509.oid import NameOID\nfrom cryptography.hazmat.primitives import hashes, serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\n\ndef create_expired_certificate(\n    common_name: str,\n    expired_days_ago: int = 1\n) -> tuple[bytes, bytes]:\n    \"\"\"\n    Create an expired TLS certificate for chaos testing.\n    Returns (certificate_pem, private_key_pem)\n    \"\"\"\n    # Generate private key\n    private_key = rsa.generate_private_key(\n        public_exponent=65537,\n        key_size=2048\n    )\n\n    # Certificate valid from 365 days ago to `expired_days_ago` ago\n    not_valid_before = datetime.utcnow() - timedelta(days=365)\n    not_valid_after = datetime.utcnow() - timedelta(days=expired_days_ago)\n\n    subject = issuer = x509.Name([\n        x509.NameAttribute(NameOID.COMMON_NAME, common_name)\n    ])\n\n    cert = x509.CertificateBuilder().subject_name(\n        subject\n    ).issuer_name(\n        issuer\n    ).public_key(\n        private_key.public_key()\n    ).serial_number(\n        x509.random_serial_number()\n    ).not_valid_before(\n        not_valid_before\n    ).not_valid_after(\n        not_valid_after\n    ).sign(private_key, hashes.SHA256())\n\n    # Serialize to PEM\n    cert_pem = cert.public_bytes(serialization.Encoding.PEM)\n    key_pem = private_key.private_bytes(\n        encoding=serialization.Encoding.PEM,\n        format=serialization.PrivateFormat.PKCS8,\n        encryption_algorithm=serialization.NoEncryption()\n    )\n\n    return cert_pem, key_pem\n```\n\n## Quick Reference\n\n| Failure Type | Tool | Command/Method |\n|--------------|------|----------------|\n| Network latency | toxiproxy | `add_latency(proxy, ms)` |\n| Packet loss | toxiproxy/pumba | `loss --percent 20` |\n| AZ failure | AWS API | `simulate_az_failure(az, asg)` |\n| CPU stress | stress-ng | `--cpu N --cpu-load 80` |\n| Memory exhaustion | stress-ng | `--vm 1 --vm-bytes XG` |\n| Container kill | pumba | `kill --signal SIGKILL` |\n| DNS failure | /etc/hosts | Block domain resolution |\n| Cert expiry | cryptography | Generate expired cert |\n\n<!-- File: skills/chaos-engineer/references/kubernetes-chaos.md (Tokens: ~2567 | Category: anthropic-skill) -->\n# Kubernetes Chaos Engineering\n\n## Litmus Chaos - ChaosEngine\n\n```yaml\napiVersion: litmuschaos.io/v1alpha1\nkind: ChaosEngine\nmetadata:\n  name: nginx-chaos\n  namespace: default\nspec:\n  # Application information\n  appinfo:\n    appns: 'default'\n    applabel: 'app=nginx'\n    appkind: 'deployment'\n\n  # Chaos service account\n  chaosServiceAccount: litmus-admin\n\n  # Experiments to run\n  experiments:\n    - name: pod-delete\n      spec:\n        components:\n          env:\n            # Total chaos duration\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n\n            # Chaos interval (delete pod every X seconds)\n            - name: CHAOS_INTERVAL\n              value: '10'\n\n            # Force delete pods\n            - name: FORCE\n              value: 'true'\n\n            # Number of pods to delete\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n    - name: pod-network-latency\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: NETWORK_LATENCY\n              value: '2000'  # 2 second latency\n            - name: JITTER\n              value: '200'   # 200ms jitter\n            - name: CONTAINER_RUNTIME\n              value: 'containerd'\n\n    - name: pod-cpu-hog\n      spec:\n        components:\n          env:\n            - name: TOTAL_CHAOS_DURATION\n              value: '60'\n            - name: CPU_CORES\n              value: '2'\n            - name: PODS_AFFECTED_PERC\n              value: '50'\n\n  # Monitor application during chaos\n  monitoring: true\n\n  # Job cleanup policy\n  jobCleanUpPolicy: 'delete'\n```\n\n## Chaos Mesh Experiments\n\n```yaml\n# Network partition between services\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: partition-frontend-backend\n  namespace: chaos-testing\nspec:\n  action: partition\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'frontend'\n  direction: to\n  target:\n    mode: all\n    selector:\n      namespaces:\n        - production\n      labelSelectors:\n        'app': 'backend'\n  duration: '5m'\n\n---\n# Pod failure - kill random pods\napiVersion: chaos-mesh.org/v1alpha1\nkind: PodChaos\nmetadata:\n  name: pod-failure\n  namespace: chaos-testing\nspec:\n  action: pod-failure\n  mode: one  # one, all, fixed, fixed-percent, random-max-percent\n  duration: '30s'\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'payment-service'\n  scheduler:\n    cron: '@every 10m'  # Run every 10 minutes\n\n---\n# Network bandwidth limitation\napiVersion: chaos-mesh.org/v1alpha1\nkind: NetworkChaos\nmetadata:\n  name: bandwidth-limit\nspec:\n  action: bandwidth\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'tier': 'backend'\n  bandwidth:\n    rate: '1mbps'\n    limit: 20000\n    buffer: 10000\n  duration: '5m'\n\n---\n# Disk I/O stress\napiVersion: chaos-mesh.org/v1alpha1\nkind: IOChaos\nmetadata:\n  name: io-latency\nspec:\n  action: latency\n  mode: one\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'database'\n  volumePath: /var/lib/postgresql/data\n  path: /var/lib/postgresql/data/**/*\n  delay: '100ms'\n  percent: 50  # 50% of I/O operations affected\n  duration: '5m'\n\n---\n# DNS chaos - random DNS errors\napiVersion: chaos-mesh.org/v1alpha1\nkind: DNSChaos\nmetadata:\n  name: dns-random-error\nspec:\n  action: random\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'api-gateway'\n  patterns:\n    - external-api.example.com\n    - *.third-party-service.com\n  duration: '3m'\n```\n\n## Node Drain Simulation\n\n```python\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport time\n\nclass K8sNodeChaos:\n    def __init__(self):\n        config.load_kube_config()\n        self.core_v1 = client.CoreV1Api()\n        self.apps_v1 = client.AppsV1Api()\n\n    def cordon_node(self, node_name: str):\n        \"\"\"Mark node as unschedulable.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": True\n            }\n        }\n        try:\n            self.core_v1.patch_node(node_name, body)\n            print(f\"Node {node_name} cordoned\")\n        except ApiException as e:\n            print(f\"Failed to cordon node: {e}\")\n\n    def drain_node(\n        self,\n        node_name: str,\n        grace_period_seconds: int = 30,\n        delete_local_data: bool = True\n    ):\n        \"\"\"\n        Drain node by evicting all pods.\n        Simulates node failure or maintenance.\n        \"\"\"\n        # First, cordon the node\n        self.cordon_node(node_name)\n\n        # Get all pods on the node\n        field_selector = f\"spec.nodeName={node_name}\"\n        pods = self.core_v1.list_pod_for_all_namespaces(\n            field_selector=field_selector\n        )\n\n        # Evict each pod\n        for pod in pods.items:\n            # Skip DaemonSet pods and mirror pods\n            if pod.metadata.owner_references:\n                for owner in pod.metadata.owner_references:\n                    if owner.kind in ['DaemonSet', 'Node']:\n                        continue\n\n            # Create eviction\n            eviction = client.V1Eviction(\n                metadata=client.V1ObjectMeta(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace\n                ),\n                delete_options=client.V1DeleteOptions(\n                    grace_period_seconds=grace_period_seconds\n                )\n            )\n\n            try:\n                self.core_v1.create_namespaced_pod_eviction(\n                    name=pod.metadata.name,\n                    namespace=pod.metadata.namespace,\n                    body=eviction\n                )\n                print(f\"Evicted pod {pod.metadata.name}\")\n            except ApiException as e:\n                if e.status == 429:  # Too many requests\n                    print(f\"Pod {pod.metadata.name} protected by PDB\")\n                else:\n                    print(f\"Failed to evict {pod.metadata.name}: {e}\")\n\n        return {\"node\": node_name, \"status\": \"drained\"}\n\n    def uncordon_node(self, node_name: str):\n        \"\"\"Mark node as schedulable again.\"\"\"\n        body = {\n            \"spec\": {\n                \"unschedulable\": False\n            }\n        }\n        self.core_v1.patch_node(node_name, body)\n        print(f\"Node {node_name} uncordoned\")\n\n    def simulate_node_failure(\n        self,\n        node_name: str,\n        duration_seconds: int = 300\n    ):\n        \"\"\"\n        Simulate complete node failure.\n        Drain node, wait, then restore.\n        \"\"\"\n        print(f\"Simulating failure of node {node_name}\")\n\n        # Drain the node\n        self.drain_node(node_name)\n\n        # Wait for duration\n        print(f\"Node failed for {duration_seconds} seconds\")\n        time.sleep(duration_seconds)\n\n        # Restore node\n        self.uncordon_node(node_name)\n        print(\"Node restored\")\n```\n\n## Pod Autoscaling Chaos\n\n```yaml\n# Test HPA behavior under load\napiVersion: chaos-mesh.org/v1alpha1\nkind: StressChaos\nmetadata:\n  name: stress-hpa-trigger\nspec:\n  mode: all\n  selector:\n    namespaces:\n      - production\n    labelSelectors:\n      'app': 'web-server'\n  stressors:\n    cpu:\n      workers: 2\n      load: 80  # 80% CPU load\n  duration: '10m'\n\n---\n# Verify HPA scaling response\napiVersion: v1\nkind: Pod\nmetadata:\n  name: chaos-verification\nspec:\n  containers:\n  - name: verifier\n    image: bitnami/kubectl:latest\n    command:\n      - /bin/bash\n      - -c\n      - |\n        # Monitor HPA scaling\n        while true; do\n          echo \"=== HPA Status ===\"\n          kubectl get hpa web-server -o json | \\\n            jq '.status | {current: .currentReplicas, desired: .desiredReplicas, cpu: .currentCPUUtilizationPercentage}'\n\n          echo \"=== Pod Count ===\"\n          kubectl get pods -l app=web-server --no-headers | wc -l\n\n          sleep 10\n        done\n```\n\n## Custom Resource Chaos\n\n```python\n# Python script to test custom CRD resilience\nfrom kubernetes import client, config\nfrom kubernetes.client.rest import ApiException\nimport random\nimport time\n\ndef chaos_delete_custom_resources(\n    group: str,\n    version: str,\n    plural: str,\n    namespace: str,\n    percentage: int = 30\n):\n    \"\"\"\n    Randomly delete custom resources to test operator resilience.\n\n    Args:\n        group: API group (e.g., 'app.example.com')\n        version: API version (e.g., 'v1')\n        plural: Resource plural name (e.g., 'myapps')\n        namespace: Namespace to target\n        percentage: Percentage of resources to delete (0-100)\n    \"\"\"\n    config.load_kube_config()\n    custom_api = client.CustomObjectsApi()\n\n    try:\n        # List all custom resources\n        resources = custom_api.list_namespaced_custom_object(\n            group=group,\n            version=version,\n            namespace=namespace,\n            plural=plural\n        )\n\n        items = resources.get('items', [])\n        if not items:\n            print(\"No resources found\")\n            return\n\n        # Calculate number to delete\n        count_to_delete = max(1, int(len(items) * percentage / 100))\n\n        # Randomly select resources\n        to_delete = random.sample(items, count_to_delete)\n\n        print(f\"Deleting {count_to_delete} of {len(items)} {plural}\")\n\n        # Delete selected resources\n        for resource in to_delete:\n            name = resource['metadata']['name']\n            try:\n                custom_api.delete_namespaced_custom_object(\n                    group=group,\n                    version=version,\n                    namespace=namespace,\n                    plural=plural,\n                    name=name,\n                    body=client.V1DeleteOptions()\n                )\n                print(f\"Deleted {plural}/{name}\")\n                time.sleep(1)  # Rate limit deletions\n            except ApiException as e:\n                print(f\"Failed to delete {name}: {e}\")\n\n    except ApiException as e:\n        print(f\"Error listing resources: {e}\")\n\n# Example: Delete 30% of MyApp custom resources\nchaos_delete_custom_resources(\n    group='app.example.com',\n    version='v1',\n    plural='myapps',\n    namespace='production',\n    percentage=30\n)\n```\n\n## Quick Reference\n\n| Chaos Type | Tool | YAML/Command |\n|------------|------|--------------|\n| Pod delete | Litmus | `pod-delete` experiment |\n| Network latency | Chaos Mesh | `NetworkChaos` with action: delay |\n| Node drain | kubectl/API | `kubectl drain <node>` |\n| CPU stress | Chaos Mesh | `StressChaos` with cpu stressor |\n| DNS failure | Chaos Mesh | `DNSChaos` random/error action |\n| I/O latency | Chaos Mesh | `IOChaos` with latency action |\n| Network partition | Chaos Mesh | `NetworkChaos` partition |\n| Pod failure | Chaos Mesh | `PodChaos` pod-failure |\n\n<!-- ... +411 more modular skills available via /api/skills/Jeffallan/claude-skills?path={path} -->\n</yakaai_skills>"}