@synapta/skills 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +11 -4
- package/package.json +3 -4
- package/skills/ATTRIBUTION.md +80 -0
- package/skills/accessibility-audit/SKILL.md +325 -0
- package/skills/accessibility-audit/reference/wcag-checklist.md +103 -0
- package/skills/apns-notifier/SKILL.md +86 -0
- package/skills/approval-policy-enforcer/SKILL.md +66 -0
- package/skills/apps-sdk-builder/LICENSE.txt +201 -0
- package/skills/apps-sdk-builder/SKILL.md +328 -0
- package/skills/apps-sdk-builder/agents/openai.yaml +13 -0
- package/skills/apps-sdk-builder/references/app-archetypes.md +132 -0
- package/skills/apps-sdk-builder/references/apps-sdk-docs-workflow.md +135 -0
- package/skills/apps-sdk-builder/references/interactive-state-sync-patterns.md +113 -0
- package/skills/apps-sdk-builder/references/repo-contract-and-validation.md +93 -0
- package/skills/apps-sdk-builder/references/search-fetch-standard.md +67 -0
- package/skills/apps-sdk-builder/references/upstream-example-workflow.md +79 -0
- package/skills/apps-sdk-builder/references/window-openai-patterns.md +79 -0
- package/skills/apps-sdk-builder/scripts/scaffold_node_ext_apps.mjs +606 -0
- package/skills/architecture-selector/SKILL.md +64 -0
- package/skills/backlog-planner/SKILL.md +68 -0
- package/skills/carplay-entitlement-checker/SKILL.md +82 -0
- package/skills/concept-discovery/SKILL.md +517 -0
- package/skills/concept-discovery/assets/sample-analysis.json +81 -0
- package/skills/concept-discovery/expected_outputs/sample-enum-dictionary.md +25 -0
- package/skills/concept-discovery/expected_outputs/sample-page-user-list.md +83 -0
- package/skills/concept-discovery/expected_outputs/sample-prd-readme.md +43 -0
- package/skills/concept-discovery/references/framework-patterns.md +228 -0
- package/skills/concept-discovery/references/prd-quality-checklist.md +65 -0
- package/skills/concept-discovery/scripts/codebase_analyzer.py +732 -0
- package/skills/concept-discovery/scripts/prd_scaffolder.py +435 -0
- package/skills/dast-zap/SKILL.md +453 -0
- package/skills/dast-zap/assets/.gitkeep +9 -0
- package/skills/dast-zap/assets/github_action.yml +207 -0
- package/skills/dast-zap/assets/gitlab_ci.yml +226 -0
- package/skills/dast-zap/assets/zap_automation.yaml +196 -0
- package/skills/dast-zap/assets/zap_context.xml +192 -0
- package/skills/dast-zap/references/EXAMPLE.md +40 -0
- package/skills/dast-zap/references/api_testing_guide.md +475 -0
- package/skills/dast-zap/references/authentication_guide.md +431 -0
- package/skills/dast-zap/references/false_positive_handling.md +427 -0
- package/skills/dast-zap/references/owasp_mapping.md +255 -0
- package/skills/dep-sbom-scan/SKILL.md +466 -0
- package/skills/deploy-cloudflare/SKILL.md +930 -0
- package/skills/deploy-docker/SKILL.md +55 -0
- package/skills/deploy-fly/SKILL.md +228 -0
- package/skills/deploy-k8s/SKILL.md +108 -0
- package/skills/deploy-k8s/assets/logo.png +0 -0
- package/skills/deploy-k8s/docs/README.md +29 -0
- package/skills/deploy-k8s/docs/SUMMARY.md +56 -0
- package/skills/deploy-k8s/docs/advanced/token-efficiency.md +61 -0
- package/skills/deploy-k8s/docs/architecture/multi-tenancy.md +96 -0
- package/skills/deploy-k8s/docs/architecture/storage-and-state.md +102 -0
- package/skills/deploy-k8s/docs/architecture/workload-patterns.md +87 -0
- package/skills/deploy-k8s/docs/book.json +16 -0
- package/skills/deploy-k8s/docs/community/changelog.md +34 -0
- package/skills/deploy-k8s/docs/community/contributing.md +67 -0
- package/skills/deploy-k8s/docs/core-concepts/failure-modes.md +153 -0
- package/skills/deploy-k8s/docs/core-concepts/philosophy.md +83 -0
- package/skills/deploy-k8s/docs/core-concepts/workflow.md +124 -0
- package/skills/deploy-k8s/docs/examples/bad-patterns.md +47 -0
- package/skills/deploy-k8s/docs/examples/do-dont-checklist.md +37 -0
- package/skills/deploy-k8s/docs/examples/good-patterns.md +49 -0
- package/skills/deploy-k8s/docs/failure-modes/api-drift.md +104 -0
- package/skills/deploy-k8s/docs/failure-modes/fragile-rollouts.md +99 -0
- package/skills/deploy-k8s/docs/failure-modes/insecure-workload-defaults.md +80 -0
- package/skills/deploy-k8s/docs/failure-modes/network-exposure.md +98 -0
- package/skills/deploy-k8s/docs/failure-modes/privilege-sprawl.md +91 -0
- package/skills/deploy-k8s/docs/failure-modes/resource-starvation.md +85 -0
- package/skills/deploy-k8s/docs/getting-started/installation.md +152 -0
- package/skills/deploy-k8s/docs/getting-started/quick-start.md +115 -0
- package/skills/deploy-k8s/docs/guides/helm-patterns.md +71 -0
- package/skills/deploy-k8s/docs/guides/kustomize-patterns.md +65 -0
- package/skills/deploy-k8s/docs/guides/observability.md +67 -0
- package/skills/deploy-k8s/docs/guides/security-hardening.md +59 -0
- package/skills/deploy-k8s/docs/guides/validation-and-policy.md +66 -0
- package/skills/deploy-k8s/docs/integrations/mcp-integration.md +52 -0
- package/skills/deploy-k8s/docs/package-lock.json +2892 -0
- package/skills/deploy-k8s/docs/package.json +13 -0
- package/skills/deploy-k8s/references/api-drift.md +298 -0
- package/skills/deploy-k8s/references/conditional/aks-patterns.md +70 -0
- package/skills/deploy-k8s/references/conditional/eks-patterns.md +79 -0
- package/skills/deploy-k8s/references/conditional/gitops-controllers.md +71 -0
- package/skills/deploy-k8s/references/conditional/gke-patterns.md +74 -0
- package/skills/deploy-k8s/references/conditional/observability-stacks.md +80 -0
- package/skills/deploy-k8s/references/conditional/openshift-patterns.md +67 -0
- package/skills/deploy-k8s/references/daemonset-operator-patterns.md +155 -0
- package/skills/deploy-k8s/references/deployment-patterns.md +146 -0
- package/skills/deploy-k8s/references/do-dont-patterns.md +87 -0
- package/skills/deploy-k8s/references/examples-bad.md +282 -0
- package/skills/deploy-k8s/references/examples-good.md +440 -0
- package/skills/deploy-k8s/references/fragile-rollouts.md +303 -0
- package/skills/deploy-k8s/references/helm-patterns.md +203 -0
- package/skills/deploy-k8s/references/insecure-workload-defaults.md +300 -0
- package/skills/deploy-k8s/references/job-patterns.md +120 -0
- package/skills/deploy-k8s/references/kustomize-patterns.md +239 -0
- package/skills/deploy-k8s/references/multi-tenancy.md +343 -0
- package/skills/deploy-k8s/references/network-exposure.md +481 -0
- package/skills/deploy-k8s/references/observability.md +302 -0
- package/skills/deploy-k8s/references/privilege-sprawl.md +273 -0
- package/skills/deploy-k8s/references/resource-starvation.md +374 -0
- package/skills/deploy-k8s/references/security-hardening.md +209 -0
- package/skills/deploy-k8s/references/stateful-patterns.md +130 -0
- package/skills/deploy-k8s/references/storage-and-state.md +330 -0
- package/skills/deploy-k8s/references/validation-and-policy.md +242 -0
- package/skills/deploy-railway/SKILL.md +235 -0
- package/skills/deploy-railway/references/analyze-db-mongo.md +84 -0
- package/skills/deploy-railway/references/analyze-db-mysql.md +254 -0
- package/skills/deploy-railway/references/analyze-db-postgres.md +479 -0
- package/skills/deploy-railway/references/analyze-db-redis.md +208 -0
- package/skills/deploy-railway/references/analyze-db.md +344 -0
- package/skills/deploy-railway/references/configure.md +309 -0
- package/skills/deploy-railway/references/deploy.md +195 -0
- package/skills/deploy-railway/references/operate.md +214 -0
- package/skills/deploy-railway/references/request.md +248 -0
- package/skills/deploy-railway/references/setup.md +312 -0
- package/skills/deploy-railway/scripts/analyze-mongo.py +1549 -0
- package/skills/deploy-railway/scripts/analyze-mysql.py +1195 -0
- package/skills/deploy-railway/scripts/analyze-postgres.py +3058 -0
- package/skills/deploy-railway/scripts/analyze-redis.py +1090 -0
- package/skills/deploy-railway/scripts/dal.py +671 -0
- package/skills/deploy-railway/scripts/enable-pg-stats.py +170 -0
- package/skills/deploy-railway/scripts/pg-extensions.py +370 -0
- package/skills/deploy-railway/scripts/railway-api.sh +52 -0
- package/skills/deploy-ssh/SKILL.md +91 -0
- package/skills/deploy-vercel/SKILL.md +304 -0
- package/skills/deploy-vercel/resources/deploy-codex.sh +301 -0
- package/skills/deploy-vercel/resources/deploy.sh +301 -0
- package/skills/docs-runbooks/SKILL.md +399 -0
- package/skills/drive-status-renderer/SKILL.md +62 -0
- package/skills/iac-scan/SKILL.md +680 -0
- package/skills/iac-scan/assets/.gitkeep +9 -0
- package/skills/iac-scan/assets/checkov_config.yaml +94 -0
- package/skills/iac-scan/assets/github_actions.yml +199 -0
- package/skills/iac-scan/assets/gitlab_ci.yml +218 -0
- package/skills/iac-scan/assets/pre_commit_config.yaml +92 -0
- package/skills/iac-scan/references/EXAMPLE.md +40 -0
- package/skills/iac-scan/references/compliance_mapping.md +237 -0
- package/skills/iac-scan/references/custom_policies.md +460 -0
- package/skills/iac-scan/references/suppression_guide.md +431 -0
- package/skills/incident-briefing/SKILL.md +66 -0
- package/skills/incident-triage/SKILL.md +481 -0
- package/{LICENSE → skills/mcp-builder/LICENSE.txt} +15 -14
- package/skills/mcp-builder/SKILL.md +244 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/mobile-pairing/SKILL.md +52 -0
- package/skills/ops-sre/SKILL.md +297 -0
- package/skills/playwright-qa/LICENSE.txt +201 -0
- package/skills/playwright-qa/NOTICE.txt +14 -0
- package/skills/playwright-qa/SKILL.md +156 -0
- package/skills/playwright-qa/agents/openai.yaml +6 -0
- package/skills/playwright-qa/assets/playwright-small.svg +3 -0
- package/skills/playwright-qa/assets/playwright.png +0 -0
- package/skills/playwright-qa/references/cli.md +116 -0
- package/skills/playwright-qa/references/workflows.md +95 -0
- package/skills/playwright-qa/scripts/playwright_cli.sh +25 -0
- package/skills/release-publish/SKILL.md +85 -0
- package/skills/repo-bootstrap/SKILL.md +92 -0
- package/skills/repo-bootstrap/assets/example-workflows/validate-agents.yml +89 -0
- package/skills/repo-bootstrap/assets/root-thin.md +141 -0
- package/skills/repo-bootstrap/assets/root-verbose.md +149 -0
- package/skills/repo-bootstrap/assets/scoped/backend-go.md +107 -0
- package/skills/repo-bootstrap/assets/scoped/backend-php.md +94 -0
- package/skills/repo-bootstrap/assets/scoped/backend-python.md +84 -0
- package/skills/repo-bootstrap/assets/scoped/backend-typescript.md +89 -0
- package/skills/repo-bootstrap/assets/scoped/claude-code-skill.md +101 -0
- package/skills/repo-bootstrap/assets/scoped/cli.md +83 -0
- package/skills/repo-bootstrap/assets/scoped/concourse.md +196 -0
- package/skills/repo-bootstrap/assets/scoped/ddev.md +68 -0
- package/skills/repo-bootstrap/assets/scoped/docker.md +160 -0
- package/skills/repo-bootstrap/assets/scoped/documentation.md +98 -0
- package/skills/repo-bootstrap/assets/scoped/examples.md +96 -0
- package/skills/repo-bootstrap/assets/scoped/frontend-typescript.md +88 -0
- package/skills/repo-bootstrap/assets/scoped/github-actions.md +174 -0
- package/skills/repo-bootstrap/assets/scoped/gitlab-ci.md +174 -0
- package/skills/repo-bootstrap/assets/scoped/oro-bundle.md +209 -0
- package/skills/repo-bootstrap/assets/scoped/oro-project.md +170 -0
- package/skills/repo-bootstrap/assets/scoped/python-modern.md +170 -0
- package/skills/repo-bootstrap/assets/scoped/resources.md +96 -0
- package/skills/repo-bootstrap/assets/scoped/skill-repo.md +139 -0
- package/skills/repo-bootstrap/assets/scoped/symfony.md +168 -0
- package/skills/repo-bootstrap/assets/scoped/testing.md +87 -0
- package/skills/repo-bootstrap/assets/scoped/typo3-docs.md +103 -0
- package/skills/repo-bootstrap/assets/scoped/typo3-extension.md +133 -0
- package/skills/repo-bootstrap/assets/scoped/typo3-project.md +137 -0
- package/skills/repo-bootstrap/assets/scoped/typo3-testing.md +80 -0
- package/skills/repo-bootstrap/checkpoints.yaml +279 -0
- package/skills/repo-bootstrap/evals/evals.json +385 -0
- package/skills/repo-bootstrap/references/ai-contribution-guidelines.md +63 -0
- package/skills/repo-bootstrap/references/ai-tool-compatibility.md +223 -0
- package/skills/repo-bootstrap/references/directory-coverage.md +82 -0
- package/skills/repo-bootstrap/references/examples/coding-agent-cli/AGENTS.md +70 -0
- package/skills/repo-bootstrap/references/examples/coding-agent-cli/go.mod +3 -0
- package/skills/repo-bootstrap/references/examples/coding-agent-cli/scripts-AGENTS.md +389 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/.env.example +13 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/AGENTS.md +91 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/package.json +33 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/pnpm-lock.yaml +3 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/AGENTS.md +91 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/config.ts +28 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/controllers/userController.ts +74 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/index.ts +26 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/middleware/errorHandler.ts +45 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/middleware/requestLogger.ts +18 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/routes/health.ts +18 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/routes/users.ts +13 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/utils/errors.ts +40 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/src/utils/logger.ts +14 -0
- package/skills/repo-bootstrap/references/examples/express-api-ts/tsconfig.json +24 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/.env.example +19 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/AGENTS.md +92 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/pyproject.toml +88 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/AGENTS.md +85 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/__init__.py +3 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/config.py +49 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/main.py +66 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/models/__init__.py +13 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/models/item.py +43 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/models/user.py +40 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/__init__.py +5 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/health.py +20 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/items.py +61 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/users.py +55 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/services/__init__.py +6 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/services/item_service.py +77 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/src/services/user_service.py +69 -0
- package/skills/repo-bootstrap/references/examples/fastapi-app/uv.lock +4 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/.scopes +3 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/AGENTS.md +86 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/admin/package.json +20 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/admin/src/App.tsx +5 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/cmd/api/main.go +7 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/go.mod +2 -0
- package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/main.go +7 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/.scopes +3 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/AGENTS.md +89 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/go.mod +2 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/AGENTS.md +90 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/package.json +17 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/App.tsx +1 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Button.tsx +1 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Footer.tsx +1 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Header.tsx +1 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Sidebar.tsx +1 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/main.go +7 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/package-lock.json +0 -0
- package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/package.json +12 -0
- package/skills/repo-bootstrap/references/examples/ldap-selfservice/AGENTS.md +70 -0
- package/skills/repo-bootstrap/references/examples/ldap-selfservice/go.mod +3 -0
- package/skills/repo-bootstrap/references/examples/ldap-selfservice/internal-AGENTS.md +371 -0
- package/skills/repo-bootstrap/references/examples/ldap-selfservice/internal-web-AGENTS.md +448 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/.scopes +3 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/AGENTS.md +91 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/composer.json +8 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/package.json +15 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/pnpm-lock.yaml +0 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/src/Controller.php +3 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/AGENTS.md +92 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/package.json +26 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/App.tsx +3 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/Button.tsx +10 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/Footer.tsx +9 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/Header.tsx +9 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/main.tsx +3 -0
- package/skills/repo-bootstrap/references/examples/php-with-frontend/web/tsconfig.json +13 -0
- package/skills/repo-bootstrap/references/examples/pnpm-workspace/AGENTS.md +75 -0
- package/skills/repo-bootstrap/references/examples/pnpm-workspace/package.json +7 -0
- package/skills/repo-bootstrap/references/examples/pnpm-workspace/packages/web/package.json +11 -0
- package/skills/repo-bootstrap/references/examples/pnpm-workspace/packages/web/src/index.ts +11 -0
- package/skills/repo-bootstrap/references/examples/pnpm-workspace/pnpm-lock.yaml +42 -0
- package/skills/repo-bootstrap/references/examples/pnpm-workspace/pnpm-workspace.yaml +2 -0
- package/skills/repo-bootstrap/references/examples/simple-ldap-go/AGENTS.md +70 -0
- package/skills/repo-bootstrap/references/examples/simple-ldap-go/examples-AGENTS.md +45 -0
- package/skills/repo-bootstrap/references/examples/simple-ldap-go/go.mod +3 -0
- package/skills/repo-bootstrap/references/examples/t3x-rte-ckeditor-image/AGENTS.md +70 -0
- package/skills/repo-bootstrap/references/examples/t3x-rte-ckeditor-image/Classes-AGENTS.md +392 -0
- package/skills/repo-bootstrap/references/examples/t3x-rte-ckeditor-image/composer.json +8 -0
- package/skills/repo-bootstrap/references/feedback-memory-schema.md +135 -0
- package/skills/repo-bootstrap/references/git-hooks-setup.md +79 -0
- package/skills/repo-bootstrap/references/output-structure.md +124 -0
- package/skills/repo-bootstrap/references/scripts-guide.md +175 -0
- package/skills/repo-bootstrap/references/verification-guide.md +137 -0
- package/skills/repo-bootstrap/scripts/analyze-git-history.sh +315 -0
- package/skills/repo-bootstrap/scripts/check-freshness.sh +230 -0
- package/skills/repo-bootstrap/scripts/detect-golden-samples.sh +161 -0
- package/skills/repo-bootstrap/scripts/detect-heuristics.sh +93 -0
- package/skills/repo-bootstrap/scripts/detect-project.sh +486 -0
- package/skills/repo-bootstrap/scripts/detect-scopes.sh +330 -0
- package/skills/repo-bootstrap/scripts/detect-utilities.sh +133 -0
- package/skills/repo-bootstrap/scripts/extract-adrs.sh +194 -0
- package/skills/repo-bootstrap/scripts/extract-agent-configs.sh +331 -0
- package/skills/repo-bootstrap/scripts/extract-architecture-rules.sh +522 -0
- package/skills/repo-bootstrap/scripts/extract-ci-commands.sh +385 -0
- package/skills/repo-bootstrap/scripts/extract-ci-rules.sh +384 -0
- package/skills/repo-bootstrap/scripts/extract-commands.sh +358 -0
- package/skills/repo-bootstrap/scripts/extract-documentation.sh +308 -0
- package/skills/repo-bootstrap/scripts/extract-github-rulesets.sh +96 -0
- package/skills/repo-bootstrap/scripts/extract-github-settings.sh +88 -0
- package/skills/repo-bootstrap/scripts/extract-ide-settings.sh +228 -0
- package/skills/repo-bootstrap/scripts/extract-platform-files.sh +290 -0
- package/skills/repo-bootstrap/scripts/extract-quality-configs.sh +442 -0
- package/skills/repo-bootstrap/scripts/generate-agents.sh +2424 -0
- package/skills/repo-bootstrap/scripts/generate-file-map.sh +153 -0
- package/skills/repo-bootstrap/scripts/lib/config-root.sh +211 -0
- package/skills/repo-bootstrap/scripts/lib/summary.sh +244 -0
- package/skills/repo-bootstrap/scripts/lib/template.sh +397 -0
- package/skills/repo-bootstrap/scripts/validate-structure.sh +324 -0
- package/skills/repo-bootstrap/scripts/verify-commands.sh +615 -0
- package/skills/repo-bootstrap/scripts/verify-content.sh +302 -0
- package/skills/schema-api-contracts/SKILL.md +56 -0
- package/skills/secret-hygiene/SKILL.md +511 -0
- package/skills/secret-hygiene/assets/.gitkeep +9 -0
- package/skills/secret-hygiene/assets/config-balanced.toml +81 -0
- package/skills/secret-hygiene/assets/config-custom.toml +178 -0
- package/skills/secret-hygiene/assets/config-strict.toml +48 -0
- package/skills/secret-hygiene/assets/github-action.yml +181 -0
- package/skills/secret-hygiene/assets/gitlab-ci.yml +257 -0
- package/skills/secret-hygiene/assets/precommit-config.yaml +70 -0
- package/skills/secret-hygiene/references/EXAMPLE.md +40 -0
- package/skills/secret-hygiene/references/compliance_mapping.md +538 -0
- package/skills/secret-hygiene/references/detection_rules.md +276 -0
- package/skills/secret-hygiene/references/false_positives.md +598 -0
- package/skills/secret-hygiene/references/remediation_guide.md +530 -0
- package/skills/stack-selector/SKILL.md +56 -0
- package/skills/telegram-control/SKILL.md +110 -0
- package/skills/telegram-control/references/architecture.md +184 -0
- package/skills/telegram-control/references/convex.md +173 -0
- package/skills/telegram-control/references/error_handling.md +212 -0
- package/skills/telegram-control/references/initial_setup.md +165 -0
- package/skills/telegram-control/references/telegram_api.md +156 -0
- package/skills/telegram-control/scripts/cancel_message.ts +53 -0
- package/skills/telegram-control/scripts/list_scheduled.ts +103 -0
- package/skills/telegram-control/scripts/logger.ts +121 -0
- package/skills/telegram-control/scripts/proxy-util.ts +11 -0
- package/skills/telegram-control/scripts/schedule_message.ts +216 -0
- package/skills/telegram-control/scripts/send_message.ts +115 -0
- package/skills/telegram-control/scripts/setup.ts +185 -0
- package/skills/telegram-control/scripts/types.ts +75 -0
- package/skills/telegram-control/scripts/view_history.ts +74 -0
- package/skills/test-strategy/SKILL.md +352 -0
- package/skills/threat-model/SKILL.md +303 -0
- package/skills/threat-model/examples/example-output.md +196 -0
- package/skills/threat-model/template.md +96 -0
- package/skills/ts-lint/SKILL.md +80 -0
- package/skills/ui-flow/SKILL.md +668 -0
- package/skills/voice-command-router/SKILL.md +51 -0
- package/skills/widget-live-activity-sync/SKILL.md +66 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Enable pg_stat_statements for query statistics tracking.
|
|
4
|
+
|
|
5
|
+
This script replicates the frontend's Metrics.tsx enable logic:
|
|
6
|
+
1. Check if pg_stat_statements is already in shared_preload_libraries
|
|
7
|
+
2. If yes, just install the extension
|
|
8
|
+
3. If no, install extension + ALTER SYSTEM + restart
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
enable-pg-stats.py --service <name>
|
|
12
|
+
|
|
13
|
+
Requires: railway CLI linked to the correct project/environment
|
|
14
|
+
|
|
15
|
+
IMPORTANT: This script requires interactive terminal confirmation.
|
|
16
|
+
It cannot be run with piped input - the user must confirm directly.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import sys
|
|
21
|
+
import re
|
|
22
|
+
from typing import List
|
|
23
|
+
|
|
24
|
+
from dal import run_psql_query, info, error, confirm_with_user
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def parse_preload_libraries(value: str) -> List[str]:
|
|
28
|
+
"""Parse shared_preload_libraries value into clean library names."""
|
|
29
|
+
if not value or not value.strip():
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
# Split by comma and clean up quotes
|
|
33
|
+
libs = []
|
|
34
|
+
for lib in value.split(","):
|
|
35
|
+
clean = lib.strip().replace('"', '').replace("'", '')
|
|
36
|
+
# Validate as PostgreSQL identifier
|
|
37
|
+
if clean and re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', clean):
|
|
38
|
+
libs.append(clean)
|
|
39
|
+
return libs
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main():
|
|
43
|
+
parser = argparse.ArgumentParser(
|
|
44
|
+
description="Enable pg_stat_statements for query performance tracking.",
|
|
45
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
46
|
+
epilog="""
|
|
47
|
+
What this does:
|
|
48
|
+
1. Checks if pg_stat_statements is already in shared_preload_libraries
|
|
49
|
+
2. Installs the extension: CREATE EXTENSION IF NOT EXISTS pg_stat_statements
|
|
50
|
+
3. If library wasn't preloaded: configures shared_preload_libraries and restarts
|
|
51
|
+
|
|
52
|
+
Note: If restart is needed, the database will have brief downtime.
|
|
53
|
+
|
|
54
|
+
IMPORTANT: This script requires interactive terminal confirmation and cannot
|
|
55
|
+
be automated. The user must confirm the action directly.
|
|
56
|
+
"""
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument("--service", required=True, help="Service name (requires linked project)")
|
|
60
|
+
|
|
61
|
+
args = parser.parse_args()
|
|
62
|
+
service = args.service
|
|
63
|
+
|
|
64
|
+
# Step 1: Check current shared_preload_libraries
|
|
65
|
+
info("Checking current shared_preload_libraries...")
|
|
66
|
+
code, output = run_psql_query(service, "SHOW shared_preload_libraries")
|
|
67
|
+
if code != 0:
|
|
68
|
+
error(f"Failed to query shared_preload_libraries: {output}")
|
|
69
|
+
|
|
70
|
+
current_libs = output
|
|
71
|
+
info(f"Current shared_preload_libraries: {current_libs or '<empty>'}")
|
|
72
|
+
|
|
73
|
+
# Check if pg_stat_statements is already loaded
|
|
74
|
+
existing_libs = parse_preload_libraries(current_libs)
|
|
75
|
+
library_already_loaded = "pg_stat_statements" in existing_libs
|
|
76
|
+
|
|
77
|
+
if library_already_loaded:
|
|
78
|
+
info("pg_stat_statements is already in shared_preload_libraries")
|
|
79
|
+
needs_restart = False
|
|
80
|
+
else:
|
|
81
|
+
info("pg_stat_statements is NOT in shared_preload_libraries (will need restart)")
|
|
82
|
+
needs_restart = True
|
|
83
|
+
|
|
84
|
+
# Step 2: Check if extension is already installed
|
|
85
|
+
info("Checking if pg_stat_statements extension is installed...")
|
|
86
|
+
code, output = run_psql_query(service, "SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'")
|
|
87
|
+
|
|
88
|
+
extension_exists = code == 0 and output.strip() == "1"
|
|
89
|
+
|
|
90
|
+
if extension_exists:
|
|
91
|
+
info("pg_stat_statements extension is already installed")
|
|
92
|
+
needs_install = False
|
|
93
|
+
else:
|
|
94
|
+
info("pg_stat_statements extension needs to be installed")
|
|
95
|
+
needs_install = True
|
|
96
|
+
|
|
97
|
+
# If everything is already configured, exit early
|
|
98
|
+
if not needs_restart and not needs_install:
|
|
99
|
+
info("pg_stat_statements is fully configured and ready!")
|
|
100
|
+
print("")
|
|
101
|
+
print("Query statistics are available. Run:")
|
|
102
|
+
print(" SELECT * FROM pg_stat_statements LIMIT 10;")
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
# Confirm before making changes - REQUIRES interactive terminal
|
|
106
|
+
print("")
|
|
107
|
+
print(f"About to make the following changes to '{service}':")
|
|
108
|
+
if needs_install:
|
|
109
|
+
print(" - Install pg_stat_statements extension")
|
|
110
|
+
if needs_restart:
|
|
111
|
+
print(" - Configure shared_preload_libraries")
|
|
112
|
+
print(" - Restart the database (brief downtime)")
|
|
113
|
+
print("")
|
|
114
|
+
|
|
115
|
+
if not confirm_with_user("Continue? [y/N]"):
|
|
116
|
+
print("Cancelled.")
|
|
117
|
+
return 1
|
|
118
|
+
|
|
119
|
+
# Step 3: Install extension
|
|
120
|
+
if needs_install:
|
|
121
|
+
info("Installing pg_stat_statements extension...")
|
|
122
|
+
code, output = run_psql_query(service, "CREATE EXTENSION IF NOT EXISTS pg_stat_statements")
|
|
123
|
+
if code != 0:
|
|
124
|
+
error(f"Failed to install extension: {output}")
|
|
125
|
+
info("Extension installed")
|
|
126
|
+
|
|
127
|
+
# Step 4: Configure shared_preload_libraries and restart if needed
|
|
128
|
+
if needs_restart:
|
|
129
|
+
info("Configuring shared_preload_libraries...")
|
|
130
|
+
|
|
131
|
+
# Add pg_stat_statements to existing libraries
|
|
132
|
+
if "pg_stat_statements" not in existing_libs:
|
|
133
|
+
existing_libs.append("pg_stat_statements")
|
|
134
|
+
|
|
135
|
+
# Build ALTER SYSTEM with individual quoted values
|
|
136
|
+
# PostgreSQL syntax: ALTER SYSTEM SET shared_preload_libraries TO 'lib1', 'lib2'
|
|
137
|
+
quoted_libs = ", ".join(f"'{lib}'" for lib in existing_libs)
|
|
138
|
+
alter_query = f"ALTER SYSTEM SET shared_preload_libraries TO {quoted_libs}"
|
|
139
|
+
|
|
140
|
+
info(f"Setting shared_preload_libraries to: {', '.join(existing_libs)}")
|
|
141
|
+
|
|
142
|
+
code, output = run_psql_query(service, alter_query)
|
|
143
|
+
if code != 0:
|
|
144
|
+
error(f"Failed to configure shared_preload_libraries: {output}")
|
|
145
|
+
info("shared_preload_libraries configured")
|
|
146
|
+
|
|
147
|
+
# Step 5: Restart the service
|
|
148
|
+
info("Restarting database service...")
|
|
149
|
+
code, stdout, stderr = run_railway_command(["restart", "--service", service, "--yes"])
|
|
150
|
+
if code != 0:
|
|
151
|
+
error(f"Failed to restart service: {stderr or stdout}")
|
|
152
|
+
|
|
153
|
+
print("")
|
|
154
|
+
info("Database is restarting. Query statistics will be available shortly.")
|
|
155
|
+
print("")
|
|
156
|
+
print("After restart completes, verify with:")
|
|
157
|
+
print(" SHOW shared_preload_libraries;")
|
|
158
|
+
print(" SELECT * FROM pg_stat_statements LIMIT 5;")
|
|
159
|
+
else:
|
|
160
|
+
print("")
|
|
161
|
+
info("pg_stat_statements is now ready!")
|
|
162
|
+
print("")
|
|
163
|
+
print("Query statistics are available. Run:")
|
|
164
|
+
print(" SELECT * FROM pg_stat_statements LIMIT 10;")
|
|
165
|
+
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == "__main__":
|
|
170
|
+
sys.exit(main())
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Manage PostgreSQL extensions for Railway database services.
|
|
4
|
+
|
|
5
|
+
This script replicates the frontend's Extensions.tsx logic:
|
|
6
|
+
- List available and installed extensions
|
|
7
|
+
- Install extensions with automatic dependency handling
|
|
8
|
+
- Uninstall extensions with dependency checking
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
pg-extensions.py --service <name> list
|
|
12
|
+
pg-extensions.py --service <name> install <extension> [--version <ver>]
|
|
13
|
+
pg-extensions.py --service <name> uninstall <extension>
|
|
14
|
+
pg-extensions.py --service <name> info <extension>
|
|
15
|
+
|
|
16
|
+
Requires: railway CLI linked to the correct project/environment
|
|
17
|
+
|
|
18
|
+
IMPORTANT: Install and uninstall require interactive terminal confirmation.
|
|
19
|
+
They cannot be run with piped input - the user must confirm directly.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import sys
|
|
24
|
+
import json
|
|
25
|
+
from typing import Tuple, List, Optional, Dict, Any
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
|
|
28
|
+
from dal import run_psql_query, info, error, confirm_with_user
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Extension:
|
|
33
|
+
"""PostgreSQL extension info."""
|
|
34
|
+
name: str
|
|
35
|
+
default_version: str
|
|
36
|
+
installed_version: Optional[str]
|
|
37
|
+
comment: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def list_extensions(service: str, json_output: bool = False) -> List[Extension]:
|
|
41
|
+
"""List all available and installed extensions."""
|
|
42
|
+
# Query available extensions
|
|
43
|
+
available_query = "SELECT name, default_version, comment FROM pg_available_extensions ORDER BY name"
|
|
44
|
+
code, output = run_psql_query(service, available_query)
|
|
45
|
+
if code != 0:
|
|
46
|
+
error(f"Failed to query available extensions: {output}")
|
|
47
|
+
|
|
48
|
+
# Parse available extensions
|
|
49
|
+
available = {}
|
|
50
|
+
for line in output.strip().split("\n"):
|
|
51
|
+
if not line:
|
|
52
|
+
continue
|
|
53
|
+
parts = line.split("|")
|
|
54
|
+
if len(parts) >= 2:
|
|
55
|
+
name = parts[0].strip()
|
|
56
|
+
version = parts[1].strip()
|
|
57
|
+
comment = parts[2].strip() if len(parts) > 2 else ""
|
|
58
|
+
available[name] = {"version": version, "comment": comment}
|
|
59
|
+
|
|
60
|
+
# Query installed extensions
|
|
61
|
+
installed_query = "SELECT extname, extversion FROM pg_extension"
|
|
62
|
+
code, output = run_psql_query(service, installed_query)
|
|
63
|
+
if code != 0:
|
|
64
|
+
error(f"Failed to query installed extensions: {output}")
|
|
65
|
+
|
|
66
|
+
# Parse installed extensions
|
|
67
|
+
installed = {}
|
|
68
|
+
for line in output.strip().split("\n"):
|
|
69
|
+
if not line:
|
|
70
|
+
continue
|
|
71
|
+
parts = line.split("|")
|
|
72
|
+
if len(parts) >= 2:
|
|
73
|
+
name = parts[0].strip()
|
|
74
|
+
version = parts[1].strip()
|
|
75
|
+
installed[name] = version
|
|
76
|
+
|
|
77
|
+
# Build extension list
|
|
78
|
+
extensions = []
|
|
79
|
+
for name, info in available.items():
|
|
80
|
+
ext = Extension(
|
|
81
|
+
name=name,
|
|
82
|
+
default_version=info["version"],
|
|
83
|
+
installed_version=installed.get(name),
|
|
84
|
+
comment=info["comment"]
|
|
85
|
+
)
|
|
86
|
+
extensions.append(ext)
|
|
87
|
+
|
|
88
|
+
if json_output:
|
|
89
|
+
print(json.dumps([{
|
|
90
|
+
"name": e.name,
|
|
91
|
+
"defaultVersion": e.default_version,
|
|
92
|
+
"installedVersion": e.installed_version,
|
|
93
|
+
"comment": e.comment
|
|
94
|
+
} for e in extensions], indent=2))
|
|
95
|
+
else:
|
|
96
|
+
# Print formatted output
|
|
97
|
+
installed_exts = [e for e in extensions if e.installed_version]
|
|
98
|
+
available_exts = [e for e in extensions if not e.installed_version]
|
|
99
|
+
|
|
100
|
+
print(f"\n{len(installed_exts)} Extension(s) installed:")
|
|
101
|
+
print("-" * 60)
|
|
102
|
+
if installed_exts:
|
|
103
|
+
for e in sorted(installed_exts, key=lambda x: x.name):
|
|
104
|
+
print(f" {e.name} (v{e.installed_version})")
|
|
105
|
+
else:
|
|
106
|
+
print(" (none)")
|
|
107
|
+
|
|
108
|
+
print(f"\n{len(available_exts)} Extension(s) available:")
|
|
109
|
+
print("-" * 60)
|
|
110
|
+
for e in sorted(available_exts, key=lambda x: x.name)[:30]:
|
|
111
|
+
desc = f" - {e.comment[:50]}..." if e.comment and len(e.comment) > 50 else f" - {e.comment}" if e.comment else ""
|
|
112
|
+
print(f" {e.name} (v{e.default_version}){desc}")
|
|
113
|
+
if len(available_exts) > 30:
|
|
114
|
+
print(f" ... and {len(available_exts) - 30} more")
|
|
115
|
+
|
|
116
|
+
return extensions
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_extension_dependencies(service: str, extension: str) -> List[str]:
|
|
120
|
+
"""Get dependencies for an extension."""
|
|
121
|
+
query = f"""
|
|
122
|
+
SELECT DISTINCT unnest(pev.requires) as dependency
|
|
123
|
+
FROM pg_available_extension_versions pev
|
|
124
|
+
WHERE pev.name = '{extension}' AND pev.requires IS NOT NULL
|
|
125
|
+
ORDER BY dependency
|
|
126
|
+
"""
|
|
127
|
+
code, output = run_psql_query(service, query)
|
|
128
|
+
if code != 0:
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
deps = []
|
|
132
|
+
for line in output.strip().split("\n"):
|
|
133
|
+
if line.strip():
|
|
134
|
+
deps.append(line.strip())
|
|
135
|
+
return deps
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def get_extension_dependents(service: str, extension: str) -> List[str]:
|
|
139
|
+
"""Get extensions that depend on this extension."""
|
|
140
|
+
query = f"""
|
|
141
|
+
SELECT e.extname AS dependent_extension
|
|
142
|
+
FROM pg_depend d
|
|
143
|
+
JOIN pg_extension e ON d.objid = e.oid
|
|
144
|
+
JOIN pg_extension ref_e ON d.refobjid = ref_e.oid
|
|
145
|
+
WHERE d.classid = 'pg_extension'::regclass
|
|
146
|
+
AND d.refclassid = 'pg_extension'::regclass
|
|
147
|
+
AND ref_e.extname = '{extension}'
|
|
148
|
+
ORDER BY dependent_extension
|
|
149
|
+
"""
|
|
150
|
+
code, output = run_psql_query(service, query)
|
|
151
|
+
if code != 0:
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
dependents = []
|
|
155
|
+
for line in output.strip().split("\n"):
|
|
156
|
+
if line.strip():
|
|
157
|
+
dependents.append(line.strip())
|
|
158
|
+
return dependents
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def is_extension_installed(service: str, extension: str) -> Tuple[bool, Optional[str]]:
|
|
162
|
+
"""Check if extension is installed, return (installed, version)."""
|
|
163
|
+
query = f"SELECT extversion FROM pg_extension WHERE extname = '{extension}'"
|
|
164
|
+
code, output = run_psql_query(service, query)
|
|
165
|
+
if code == 0 and output.strip():
|
|
166
|
+
return True, output.strip()
|
|
167
|
+
return False, None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def is_extension_available(service: str, extension: str) -> bool:
|
|
171
|
+
"""Check if extension is available in the image."""
|
|
172
|
+
query = f"SELECT 1 FROM pg_available_extensions WHERE name = '{extension}'"
|
|
173
|
+
code, output = run_psql_query(service, query)
|
|
174
|
+
return code == 0 and output.strip() == "1"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def install_extension(service: str, extension: str, version: Optional[str] = None) -> bool:
|
|
178
|
+
"""Install an extension with CASCADE (auto-install dependencies)."""
|
|
179
|
+
# Check if available
|
|
180
|
+
if not is_extension_available(service, extension):
|
|
181
|
+
error(f"Extension '{extension}' is not available in this database image")
|
|
182
|
+
|
|
183
|
+
# Check if already installed
|
|
184
|
+
installed, curr_ver = is_extension_installed(service, extension)
|
|
185
|
+
if installed:
|
|
186
|
+
info(f"Extension '{extension}' is already installed (v{curr_ver})")
|
|
187
|
+
return True
|
|
188
|
+
|
|
189
|
+
# Check dependencies and confirm - REQUIRES interactive terminal
|
|
190
|
+
deps = get_extension_dependencies(service, extension)
|
|
191
|
+
if deps:
|
|
192
|
+
print(f"\nInstalling '{extension}' will also install these dependencies: {', '.join(deps)}")
|
|
193
|
+
else:
|
|
194
|
+
print(f"\nAbout to install extension '{extension}'")
|
|
195
|
+
|
|
196
|
+
if not confirm_with_user("Continue? [y/N]"):
|
|
197
|
+
print("Cancelled.")
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
# Build install query
|
|
201
|
+
query = f'CREATE EXTENSION IF NOT EXISTS "{extension}"'
|
|
202
|
+
if version:
|
|
203
|
+
query += f" VERSION '{version}'"
|
|
204
|
+
query += " CASCADE" # Auto-install dependencies
|
|
205
|
+
|
|
206
|
+
info(f"Installing extension '{extension}'...")
|
|
207
|
+
code, output = run_psql_query(service, query)
|
|
208
|
+
if code != 0:
|
|
209
|
+
error(f"Failed to install extension: {output}")
|
|
210
|
+
|
|
211
|
+
# Verify installation
|
|
212
|
+
installed, ver = is_extension_installed(service, extension)
|
|
213
|
+
if installed:
|
|
214
|
+
info(f"Successfully installed '{extension}' (v{ver})")
|
|
215
|
+
if deps:
|
|
216
|
+
info(f"Dependencies installed: {', '.join(deps)}")
|
|
217
|
+
return True
|
|
218
|
+
else:
|
|
219
|
+
error("Extension installation failed - not found after CREATE EXTENSION")
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def uninstall_extension(service: str, extension: str) -> bool:
|
|
224
|
+
"""Uninstall an extension."""
|
|
225
|
+
# Check if installed
|
|
226
|
+
installed, ver = is_extension_installed(service, extension)
|
|
227
|
+
if not installed:
|
|
228
|
+
info(f"Extension '{extension}' is not installed")
|
|
229
|
+
return True
|
|
230
|
+
|
|
231
|
+
# Check for dependents
|
|
232
|
+
dependents = get_extension_dependents(service, extension)
|
|
233
|
+
if dependents:
|
|
234
|
+
error(f"Cannot uninstall '{extension}' - these extensions depend on it: {', '.join(dependents)}")
|
|
235
|
+
|
|
236
|
+
# Confirm - REQUIRES interactive terminal
|
|
237
|
+
print(f"\nAbout to uninstall '{extension}' (v{ver})")
|
|
238
|
+
if not confirm_with_user("Continue? [y/N]"):
|
|
239
|
+
print("Cancelled.")
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
# Uninstall
|
|
243
|
+
query = f'DROP EXTENSION IF EXISTS "{extension}"'
|
|
244
|
+
info(f"Uninstalling extension '{extension}'...")
|
|
245
|
+
code, output = run_psql_query(service, query)
|
|
246
|
+
if code != 0:
|
|
247
|
+
error(f"Failed to uninstall extension: {output}")
|
|
248
|
+
|
|
249
|
+
# Verify
|
|
250
|
+
installed, _ = is_extension_installed(service, extension)
|
|
251
|
+
if not installed:
|
|
252
|
+
info(f"Successfully uninstalled '{extension}'")
|
|
253
|
+
return True
|
|
254
|
+
else:
|
|
255
|
+
error("Extension uninstall failed - still found after DROP EXTENSION")
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def extension_info(service: str, extension: str, json_output: bool = False):
|
|
260
|
+
"""Show detailed info about an extension."""
|
|
261
|
+
# Check availability
|
|
262
|
+
available = is_extension_available(service, extension)
|
|
263
|
+
if not available:
|
|
264
|
+
error(f"Extension '{extension}' is not available in this database image")
|
|
265
|
+
|
|
266
|
+
# Get info
|
|
267
|
+
query = f"SELECT name, default_version, comment FROM pg_available_extensions WHERE name = '{extension}'"
|
|
268
|
+
code, output = run_psql_query(service, query)
|
|
269
|
+
if code != 0:
|
|
270
|
+
error(f"Failed to get extension info: {output}")
|
|
271
|
+
|
|
272
|
+
parts = output.strip().split("|")
|
|
273
|
+
name = parts[0].strip() if parts else extension
|
|
274
|
+
default_version = parts[1].strip() if len(parts) > 1 else "unknown"
|
|
275
|
+
comment = parts[2].strip() if len(parts) > 2 else ""
|
|
276
|
+
|
|
277
|
+
installed, installed_version = is_extension_installed(service, extension)
|
|
278
|
+
deps = get_extension_dependencies(service, extension)
|
|
279
|
+
dependents = get_extension_dependents(service, extension) if installed else []
|
|
280
|
+
|
|
281
|
+
if json_output:
|
|
282
|
+
print(json.dumps({
|
|
283
|
+
"name": name,
|
|
284
|
+
"defaultVersion": default_version,
|
|
285
|
+
"installedVersion": installed_version,
|
|
286
|
+
"comment": comment,
|
|
287
|
+
"dependencies": deps,
|
|
288
|
+
"dependents": dependents
|
|
289
|
+
}, indent=2))
|
|
290
|
+
else:
|
|
291
|
+
print(f"\nExtension: {name}")
|
|
292
|
+
print("-" * 40)
|
|
293
|
+
print(f" Default Version: {default_version}")
|
|
294
|
+
print(f" Installed: {'Yes (v' + installed_version + ')' if installed else 'No'}")
|
|
295
|
+
if comment:
|
|
296
|
+
print(f" Description: {comment}")
|
|
297
|
+
if deps:
|
|
298
|
+
print(f" Dependencies: {', '.join(deps)}")
|
|
299
|
+
if dependents:
|
|
300
|
+
print(f" Required by: {', '.join(dependents)}")
|
|
301
|
+
print()
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def main():
|
|
305
|
+
parser = argparse.ArgumentParser(
|
|
306
|
+
description="Manage PostgreSQL extensions for Railway services.",
|
|
307
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
308
|
+
epilog="""
|
|
309
|
+
Examples:
|
|
310
|
+
List all extensions (safe, no confirmation needed):
|
|
311
|
+
pg-extensions.py --service my-postgres list
|
|
312
|
+
|
|
313
|
+
Install an extension (requires interactive confirmation):
|
|
314
|
+
pg-extensions.py --service my-postgres install postgis
|
|
315
|
+
pg-extensions.py --service my-postgres install pgvector --version 0.5.0
|
|
316
|
+
|
|
317
|
+
Uninstall an extension (requires interactive confirmation):
|
|
318
|
+
pg-extensions.py --service my-postgres uninstall postgis
|
|
319
|
+
|
|
320
|
+
Get extension info (safe, no confirmation needed):
|
|
321
|
+
pg-extensions.py --service my-postgres info pg_stat_statements
|
|
322
|
+
|
|
323
|
+
IMPORTANT: Install and uninstall require interactive terminal confirmation.
|
|
324
|
+
They cannot be automated or run with piped input.
|
|
325
|
+
"""
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
parser.add_argument("--service", required=True, help="Service name (requires linked project)")
|
|
329
|
+
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
330
|
+
|
|
331
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
332
|
+
|
|
333
|
+
# list command (safe - no confirmation)
|
|
334
|
+
list_parser = subparsers.add_parser("list", help="List available and installed extensions")
|
|
335
|
+
|
|
336
|
+
# install command (requires interactive confirmation)
|
|
337
|
+
install_parser = subparsers.add_parser("install", help="Install an extension (requires confirmation)")
|
|
338
|
+
install_parser.add_argument("extension", help="Extension name")
|
|
339
|
+
install_parser.add_argument("--version", "-v", help="Specific version to install")
|
|
340
|
+
|
|
341
|
+
# uninstall command (requires interactive confirmation)
|
|
342
|
+
uninstall_parser = subparsers.add_parser("uninstall", help="Uninstall an extension (requires confirmation)")
|
|
343
|
+
uninstall_parser.add_argument("extension", help="Extension name")
|
|
344
|
+
|
|
345
|
+
# info command
|
|
346
|
+
info_parser = subparsers.add_parser("info", help="Show extension info")
|
|
347
|
+
info_parser.add_argument("extension", help="Extension name")
|
|
348
|
+
|
|
349
|
+
args = parser.parse_args()
|
|
350
|
+
|
|
351
|
+
if not args.command:
|
|
352
|
+
parser.print_help()
|
|
353
|
+
return 1
|
|
354
|
+
|
|
355
|
+
service = args.service
|
|
356
|
+
|
|
357
|
+
if args.command == "list":
|
|
358
|
+
list_extensions(service, json_output=args.json)
|
|
359
|
+
elif args.command == "install":
|
|
360
|
+
install_extension(service, args.extension, version=args.version)
|
|
361
|
+
elif args.command == "uninstall":
|
|
362
|
+
uninstall_extension(service, args.extension)
|
|
363
|
+
elif args.command == "info":
|
|
364
|
+
extension_info(service, args.extension, json_output=args.json)
|
|
365
|
+
|
|
366
|
+
return 0
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
if __name__ == "__main__":
|
|
370
|
+
sys.exit(main())
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Railway GraphQL API helper
|
|
3
|
+
# Usage: railway-api.sh '<graphql-query>' ['<variables-json>']
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SKILL_ID="use-railway"
|
|
8
|
+
SKILL_VERSION="${RAILWAY_SKILL_VERSION:-1.2.1}"
|
|
9
|
+
|
|
10
|
+
export RAILWAY_CALLER="${RAILWAY_CALLER:-skill:${SKILL_ID}@${SKILL_VERSION}}"
|
|
11
|
+
export RAILWAY_AGENT_SESSION="${RAILWAY_AGENT_SESSION:-railway-skill-$(date +%s)-$$}"
|
|
12
|
+
|
|
13
|
+
if ! command -v jq &>/dev/null; then
|
|
14
|
+
echo '{"error": "jq not installed. Install with: brew install jq"}'
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
CONFIG_FILE="$HOME/.railway/config.json"
|
|
19
|
+
|
|
20
|
+
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
21
|
+
echo '{"error": "Railway config not found. Run: railway login"}'
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
TOKEN=$(jq -r '.user.token' "$CONFIG_FILE")
|
|
26
|
+
|
|
27
|
+
if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then
|
|
28
|
+
echo '{"error": "No Railway token found. Run: railway login"}'
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if [[ -z "$1" ]]; then
|
|
33
|
+
echo '{"error": "No query provided"}'
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Build payload with query and optional variables
|
|
38
|
+
if [[ -n "$2" ]]; then
|
|
39
|
+
PAYLOAD=$(jq -n --arg q "$1" --argjson v "$2" '{query: $q, variables: $v}')
|
|
40
|
+
else
|
|
41
|
+
PAYLOAD=$(jq -n --arg q "$1" '{query: $q}')
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
HEADERS=(
|
|
45
|
+
-H "Authorization: Bearer $TOKEN"
|
|
46
|
+
-H "Content-Type: application/json"
|
|
47
|
+
-H "X-Railway-Skill-Id: $SKILL_ID"
|
|
48
|
+
-H "X-Railway-Skill-Version: $SKILL_VERSION"
|
|
49
|
+
-H "X-Railway-Agent-Session: $RAILWAY_AGENT_SESSION"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
curl -s https://backboard.railway.com/graphql/v2 "${HEADERS[@]}" -d "$PAYLOAD"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy-ssh
|
|
3
|
+
description: Zero-downtime deploys over SSH using rsync + atomic symlink swap + systemd unit reload. For VPS / bare-metal targets where a managed platform isn't viable.
|
|
4
|
+
triggers: [synapta deploy ssh, ssh deploy, rsync deploy, VPS, bare-metal, systemd, atomic deploy]
|
|
5
|
+
network: allowlist
|
|
6
|
+
tools: [Bash]
|
|
7
|
+
source:
|
|
8
|
+
origin: authored-by-synapta
|
|
9
|
+
reason: "Surprising community gap — no production-quality community SKILL.md for SSH deploys at adoption time."
|
|
10
|
+
citation_patterns:
|
|
11
|
+
- "DeployHQ rsync cheatsheet (https://www.deployhq.com/cheatsheets/rsync)"
|
|
12
|
+
- "Lambros Petrou — Server deploy scripts (https://www.lambrospetrou.com/articles/server-deploy-scripts/)"
|
|
13
|
+
- "Capistrano's atomic-deploy pattern (releases/ + current symlink)"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Deploy: SSH
|
|
17
|
+
|
|
18
|
+
Atomic, zero-downtime deploys to a single host (VPS / bare metal) over SSH. Pattern: `releases/<sha>/` directory + atomic `current` symlink swap + `systemctl reload`.
|
|
19
|
+
|
|
20
|
+
## Required credentials
|
|
21
|
+
|
|
22
|
+
| Env / config | Purpose |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `SSH_HOST` | host or hostname (e.g. `web01.example.com`) |
|
|
25
|
+
| `SSH_USER` | deploy user (NOT root) |
|
|
26
|
+
| `SSH_KEY_PATH` | path to deploy private key on the runner |
|
|
27
|
+
| `DEPLOY_ROOT` | path on host, e.g. `/srv/<app>` |
|
|
28
|
+
|
|
29
|
+
## Directory layout on host
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/srv/<app>/
|
|
33
|
+
├── releases/
|
|
34
|
+
│ ├── <sha1>/
|
|
35
|
+
│ ├── <sha2>/
|
|
36
|
+
│ └── <sha3>/ ← new release lands here first
|
|
37
|
+
├── shared/ ← persistent (uploads, logs, .env)
|
|
38
|
+
└── current -> releases/<sha3> ← atomic symlink
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Deploy steps
|
|
42
|
+
|
|
43
|
+
1. **Build artifact** in CI (or on host via separate `build` step). Produce a self-contained tarball.
|
|
44
|
+
2. **rsync** the new release to `releases/<sha>/` on the host. Use `--delete` and `--exclude=node_modules` (or vendor as needed).
|
|
45
|
+
3. **Link shared** dirs: `ln -s ../../shared/.env releases/<sha>/.env`, same for uploads/logs.
|
|
46
|
+
4. **Run any pre-flight** (DB migrations, asset compile) inside the new release dir, NOT the live one.
|
|
47
|
+
5. **Atomic swap**: `ln -sfn releases/<sha> current` then `mv current.tmp current` if the system requires the rename pattern.
|
|
48
|
+
6. **Reload**: `systemctl --user reload <app>` (or restart if reload isn't supported). The service unit points at `current/bin/<entry>`.
|
|
49
|
+
7. **Health check**: GET `/health` on the listening port. If unhealthy within N seconds, roll back.
|
|
50
|
+
8. **Prune**: keep the last 5 releases; remove older.
|
|
51
|
+
|
|
52
|
+
## Rollback
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
ssh "$SSH_HOST" "ln -sfn $DEPLOY_ROOT/releases/<previous-sha> $DEPLOY_ROOT/current && systemctl --user reload <app>"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The release dirs from previous successful deploys are intact, so rollback is one symlink swap.
|
|
59
|
+
|
|
60
|
+
## systemd service unit
|
|
61
|
+
|
|
62
|
+
```ini
|
|
63
|
+
[Unit]
|
|
64
|
+
Description=<app>
|
|
65
|
+
After=network.target
|
|
66
|
+
|
|
67
|
+
[Service]
|
|
68
|
+
WorkingDirectory=/srv/<app>/current
|
|
69
|
+
ExecStart=/srv/<app>/current/bin/<entry>
|
|
70
|
+
Restart=on-failure
|
|
71
|
+
EnvironmentFile=/srv/<app>/shared/.env
|
|
72
|
+
|
|
73
|
+
[Install]
|
|
74
|
+
WantedBy=default.target
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
User units (run as the deploy user) avoid needing root for reloads.
|
|
78
|
+
|
|
79
|
+
## Security defaults
|
|
80
|
+
|
|
81
|
+
- `SSH_USER` is NOT root. It can `systemctl --user` its own units but not modify the system.
|
|
82
|
+
- Deploy key is **deploy-only**: no shell, restricted to a `command=` in `authorized_keys` if possible.
|
|
83
|
+
- `.env` lives in `shared/` only on the host. CI never sees production secrets.
|
|
84
|
+
- Inbound port reachable only from the LB / Cloudflare / Tailscale; no public 22 if avoidable.
|
|
85
|
+
|
|
86
|
+
## Anti-patterns
|
|
87
|
+
|
|
88
|
+
- `scp` overwriting the live directory — not atomic, partial state visible
|
|
89
|
+
- Editing `.env` on the host without checksumming — drifts undetected
|
|
90
|
+
- `git pull` on the host as deploy mechanism — exposes git history + pulls partial state
|
|
91
|
+
- Restart-instead-of-reload by default — drops in-flight requests
|