@synapta/skills 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-deepener/SKILL.md +86 -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,2424 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Main AGENTS.md generator script
|
|
3
|
+
# Requires: Bash 4.3+ (for nameref variables)
|
|
4
|
+
# shellcheck disable=SC2034 # vars/scope_vars are used via nameref in template functions
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Check Bash version - we need 4.3+ for nameref variables (local -n)
|
|
8
|
+
if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3))); then
|
|
9
|
+
echo "Error: Bash 4.3+ required (found ${BASH_VERSION})." >&2
|
|
10
|
+
echo "On macOS: brew install bash" >&2
|
|
11
|
+
echo "Then run with: /opt/homebrew/bin/bash $0 $*" >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
|
|
17
|
+
TEMPLATE_DIR="$SKILL_DIR/assets"
|
|
18
|
+
|
|
19
|
+
# Source helper libraries
|
|
20
|
+
source "$SCRIPT_DIR/lib/template.sh"
|
|
21
|
+
source "$SCRIPT_DIR/lib/summary.sh"
|
|
22
|
+
source "$SCRIPT_DIR/lib/config-root.sh"
|
|
23
|
+
|
|
24
|
+
# Default options
|
|
25
|
+
PROJECT_DIR="${1:-.}"
|
|
26
|
+
STYLE="${STYLE:-thin}"
|
|
27
|
+
DRY_RUN=false
|
|
28
|
+
UPDATE_ONLY=false
|
|
29
|
+
FORCE=false
|
|
30
|
+
VERBOSE=false
|
|
31
|
+
CLAUDE_SHIM=false
|
|
32
|
+
CREATE_SYMLINKS=true
|
|
33
|
+
|
|
34
|
+
# Parse flags
|
|
35
|
+
while [[ $# -gt 0 ]]; do
|
|
36
|
+
case $1 in
|
|
37
|
+
--style=*)
|
|
38
|
+
STYLE="${1#*=}"
|
|
39
|
+
shift
|
|
40
|
+
;;
|
|
41
|
+
--dry-run)
|
|
42
|
+
DRY_RUN=true
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
--update)
|
|
46
|
+
UPDATE_ONLY=true
|
|
47
|
+
shift
|
|
48
|
+
;;
|
|
49
|
+
--force)
|
|
50
|
+
FORCE=true
|
|
51
|
+
shift
|
|
52
|
+
;;
|
|
53
|
+
--verbose|-v)
|
|
54
|
+
VERBOSE=true
|
|
55
|
+
shift
|
|
56
|
+
;;
|
|
57
|
+
--claude-shim)
|
|
58
|
+
CLAUDE_SHIM=true
|
|
59
|
+
shift
|
|
60
|
+
;;
|
|
61
|
+
--no-symlinks) # DEPRECATED: symlinks are always created now
|
|
62
|
+
echo "Warning: --no-symlinks is deprecated. CLAUDE.md symlinks are always created."
|
|
63
|
+
shift
|
|
64
|
+
;;
|
|
65
|
+
--help|-h)
|
|
66
|
+
cat <<EOF
|
|
67
|
+
Usage: generate-agents.sh [PROJECT_DIR] [OPTIONS]
|
|
68
|
+
|
|
69
|
+
Generate AGENTS.md files for a project following the public agents.md convention.
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--style=thin|verbose Template style (default: thin)
|
|
73
|
+
--dry-run Preview what will be created
|
|
74
|
+
--update Update existing files only
|
|
75
|
+
--force Force regeneration of existing files
|
|
76
|
+
--claude-shim Generate CLAUDE.md that imports AGENTS.md (root only, legacy)
|
|
77
|
+
--no-symlinks Skip creating CLAUDE.md/GEMINI.md symlinks (default: create them)
|
|
78
|
+
--verbose, -v Verbose output
|
|
79
|
+
--help, -h Show this help message
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
generate-agents.sh . # Generate thin root + scoped files
|
|
83
|
+
generate-agents.sh . --dry-run # Preview changes
|
|
84
|
+
generate-agents.sh . --style=verbose # Use verbose root template
|
|
85
|
+
generate-agents.sh . --update # Update existing files
|
|
86
|
+
generate-agents.sh . --no-symlinks # Skip CLAUDE.md/GEMINI.md symlinks
|
|
87
|
+
generate-agents.sh . --claude-shim # Generate CLAUDE.md @import shim instead of symlink
|
|
88
|
+
EOF
|
|
89
|
+
exit 0
|
|
90
|
+
;;
|
|
91
|
+
*)
|
|
92
|
+
PROJECT_DIR="$1"
|
|
93
|
+
shift
|
|
94
|
+
;;
|
|
95
|
+
esac
|
|
96
|
+
done
|
|
97
|
+
|
|
98
|
+
# Validate PROJECT_DIR exists
|
|
99
|
+
if [[ ! -d "$PROJECT_DIR" ]]; then
|
|
100
|
+
echo "Error: Project directory not found: $PROJECT_DIR" >&2
|
|
101
|
+
exit 1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Convert to absolute path before cd (so subsequent script calls work)
|
|
105
|
+
PROJECT_DIR="$(cd "$PROJECT_DIR" && pwd)"
|
|
106
|
+
cd "$PROJECT_DIR"
|
|
107
|
+
|
|
108
|
+
# Initialize summary tracking
|
|
109
|
+
init_summary
|
|
110
|
+
|
|
111
|
+
log() {
|
|
112
|
+
if [ "$VERBOSE" = true ]; then
|
|
113
|
+
echo "[INFO] $*" >&2
|
|
114
|
+
fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
error() {
|
|
118
|
+
echo "[ERROR] $*" >&2
|
|
119
|
+
exit 1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Detect Node.js package manager for a scope directory
|
|
123
|
+
# Walks up from scope dir to find package.json with lockfile
|
|
124
|
+
# Workspace-aware: checks workspace root for lockfiles if in a monorepo
|
|
125
|
+
detect_node_package_manager() {
|
|
126
|
+
local scope_dir="$1"
|
|
127
|
+
local search_dir="$scope_dir"
|
|
128
|
+
|
|
129
|
+
# First, check if we're in a workspace - lockfile will be at workspace root
|
|
130
|
+
local workspace_root
|
|
131
|
+
workspace_root=$(find_node_workspace_root "$scope_dir" || true)
|
|
132
|
+
if [ -n "$workspace_root" ]; then
|
|
133
|
+
[ -f "$workspace_root/pnpm-lock.yaml" ] && echo "pnpm" && return
|
|
134
|
+
[ -f "$workspace_root/yarn.lock" ] && echo "yarn" && return
|
|
135
|
+
[ -f "$workspace_root/bun.lockb" ] && echo "bun" && return
|
|
136
|
+
[ -f "$workspace_root/package-lock.json" ] && echo "npm" && return
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# Walk up to find package.json with lockfile
|
|
140
|
+
while [ "$search_dir" != "." ] && [ "$search_dir" != "/" ]; do
|
|
141
|
+
if [ -f "$search_dir/package.json" ]; then
|
|
142
|
+
# Found package.json, check for lockfiles
|
|
143
|
+
[ -f "$search_dir/pnpm-lock.yaml" ] && echo "pnpm" && return
|
|
144
|
+
[ -f "$search_dir/yarn.lock" ] && echo "yarn" && return
|
|
145
|
+
[ -f "$search_dir/bun.lockb" ] && echo "bun" && return
|
|
146
|
+
[ -f "$search_dir/package-lock.json" ] && echo "npm" && return
|
|
147
|
+
# No lockfile, default to npm
|
|
148
|
+
echo "npm"
|
|
149
|
+
return
|
|
150
|
+
fi
|
|
151
|
+
search_dir=$(dirname "$search_dir")
|
|
152
|
+
done
|
|
153
|
+
|
|
154
|
+
# Check root as last resort
|
|
155
|
+
if [ -f "package.json" ]; then
|
|
156
|
+
[ -f "pnpm-lock.yaml" ] && echo "pnpm" && return
|
|
157
|
+
[ -f "yarn.lock" ] && echo "yarn" && return
|
|
158
|
+
[ -f "bun.lockb" ] && echo "bun" && return
|
|
159
|
+
echo "npm"
|
|
160
|
+
return
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# Fallback: use from PROJECT_INFO if available
|
|
164
|
+
if [ -n "${PROJECT_INFO:-}" ]; then
|
|
165
|
+
local pkg_mgr
|
|
166
|
+
pkg_mgr=$(echo "$PROJECT_INFO" | jq -r '.package_manager // "unknown"')
|
|
167
|
+
if [ "$pkg_mgr" != "unknown" ] && [ "$pkg_mgr" != "go" ] && [ "$pkg_mgr" != "composer" ]; then
|
|
168
|
+
echo "$pkg_mgr"
|
|
169
|
+
return
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
echo "npm" # Ultimate fallback
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Detect CSS approach/framework used in project
|
|
177
|
+
# Checks package.json dependencies and config files
|
|
178
|
+
detect_css_approach() {
|
|
179
|
+
local config_root="$1"
|
|
180
|
+
local pkg_json="$config_root/package.json"
|
|
181
|
+
local approaches=()
|
|
182
|
+
|
|
183
|
+
# Check package.json dependencies
|
|
184
|
+
if [ -f "$pkg_json" ]; then
|
|
185
|
+
local deps
|
|
186
|
+
deps=$(jq -r '(.dependencies // {}) + (.devDependencies // {}) | keys[]' "$pkg_json" 2>/dev/null || echo "")
|
|
187
|
+
|
|
188
|
+
# Tailwind CSS
|
|
189
|
+
if echo "$deps" | grep -q "^tailwindcss$"; then
|
|
190
|
+
approaches+=("Tailwind CSS")
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# styled-components
|
|
194
|
+
if echo "$deps" | grep -q "^styled-components$"; then
|
|
195
|
+
approaches+=("styled-components")
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# Emotion
|
|
199
|
+
if echo "$deps" | grep -q "^@emotion/"; then
|
|
200
|
+
approaches+=("Emotion")
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# CSS Modules (indicated by config or common patterns)
|
|
204
|
+
if [ -f "$config_root/postcss.config.js" ] || [ -f "$config_root/postcss.config.mjs" ]; then
|
|
205
|
+
if ! printf '%s\n' "${approaches[@]}" | grep -q "Tailwind"; then
|
|
206
|
+
approaches+=("CSS Modules")
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Sass/SCSS
|
|
211
|
+
if echo "$deps" | grep -q "^sass$"; then
|
|
212
|
+
approaches+=("Sass/SCSS")
|
|
213
|
+
fi
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# Default to CSS Modules if nothing detected but tsconfig exists
|
|
217
|
+
if [ ${#approaches[@]} -eq 0 ]; then
|
|
218
|
+
[ -f "$config_root/tsconfig.json" ] && approaches+=("CSS Modules")
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Join approaches with comma. `local IFS` is function-scoped and does
|
|
222
|
+
# not tamper with the caller's IFS — safe under strict SC2030/SC2031
|
|
223
|
+
# semantics. Suppress opengrep's false positive on the `IFS=` pattern.
|
|
224
|
+
local IFS=', ' # nosemgrep: bash.lang.security.ifs-tampering.ifs-tampering
|
|
225
|
+
echo "${approaches[*]}"
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# Byte budget enforcement (OpenAI Codex default: 32 KiB for combined instructions)
|
|
229
|
+
BYTE_BUDGET="${BYTE_BUDGET:-32768}"
|
|
230
|
+
|
|
231
|
+
enforce_byte_budget() {
|
|
232
|
+
local file="$1"
|
|
233
|
+
local budget="$2"
|
|
234
|
+
|
|
235
|
+
[ ! -f "$file" ] && return 0
|
|
236
|
+
|
|
237
|
+
local size
|
|
238
|
+
size=$(wc -c < "$file")
|
|
239
|
+
|
|
240
|
+
if [ "$size" -le "$budget" ]; then
|
|
241
|
+
log "File size: $size bytes (within $budget budget)"
|
|
242
|
+
return 0
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
log "File exceeds byte budget ($size > $budget), pruning..."
|
|
246
|
+
|
|
247
|
+
local content
|
|
248
|
+
content=$(cat "$file")
|
|
249
|
+
local pruned=false
|
|
250
|
+
|
|
251
|
+
# Strategy 1: Reduce golden samples to 5
|
|
252
|
+
if echo "$content" | grep -q "AGENTS-GENERATED:START golden-samples"; then
|
|
253
|
+
local sample_count
|
|
254
|
+
sample_count=$(echo "$content" | sed -n '/AGENTS-GENERATED:START golden-samples/,/AGENTS-GENERATED:END golden-samples/p' | grep -c "^|" || echo 0)
|
|
255
|
+
if [ "$sample_count" -gt 7 ]; then # header + separator + 5 data rows = 7
|
|
256
|
+
content=$(echo "$content" | awk '
|
|
257
|
+
/AGENTS-GENERATED:START golden-samples/ { in_section=1; count=0; print; next }
|
|
258
|
+
/AGENTS-GENERATED:END golden-samples/ { in_section=0; print; next }
|
|
259
|
+
in_section && /^\|/ { count++; if (count <= 7) print; next }
|
|
260
|
+
{ print }
|
|
261
|
+
')
|
|
262
|
+
pruned=true
|
|
263
|
+
log " - Reduced golden samples to 5"
|
|
264
|
+
fi
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
# Strategy 2: Reduce heuristics to 5
|
|
268
|
+
if echo "$content" | grep -q "AGENTS-GENERATED:START heuristics"; then
|
|
269
|
+
local heuristic_count
|
|
270
|
+
heuristic_count=$(echo "$content" | sed -n '/AGENTS-GENERATED:START heuristics/,/AGENTS-GENERATED:END heuristics/p' | grep -c "^|" || echo 0)
|
|
271
|
+
if [ "$heuristic_count" -gt 7 ]; then # header + separator + 5 data rows = 7
|
|
272
|
+
content=$(echo "$content" | awk '
|
|
273
|
+
/AGENTS-GENERATED:START heuristics/ { in_section=1; count=0; print; next }
|
|
274
|
+
/AGENTS-GENERATED:END heuristics/ { in_section=0; print; next }
|
|
275
|
+
in_section && /^\|/ { count++; if (count <= 7) print; next }
|
|
276
|
+
{ print }
|
|
277
|
+
')
|
|
278
|
+
pruned=true
|
|
279
|
+
log " - Reduced heuristics to 5"
|
|
280
|
+
fi
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
# Strategy 3: Reduce utilities to 5
|
|
284
|
+
if echo "$content" | grep -q "AGENTS-GENERATED:START utilities"; then
|
|
285
|
+
local utility_count
|
|
286
|
+
utility_count=$(echo "$content" | sed -n '/AGENTS-GENERATED:START utilities/,/AGENTS-GENERATED:END utilities/p' | grep -c "^|" || echo 0)
|
|
287
|
+
if [ "$utility_count" -gt 7 ]; then # header + separator + 5 data rows = 7
|
|
288
|
+
content=$(echo "$content" | awk '
|
|
289
|
+
/AGENTS-GENERATED:START utilities/ { in_section=1; count=0; print; next }
|
|
290
|
+
/AGENTS-GENERATED:END utilities/ { in_section=0; print; next }
|
|
291
|
+
in_section && /^\|/ { count++; if (count <= 7) print; next }
|
|
292
|
+
{ print }
|
|
293
|
+
')
|
|
294
|
+
pruned=true
|
|
295
|
+
log " - Reduced utilities to 5"
|
|
296
|
+
fi
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
# Write pruned content
|
|
300
|
+
if [ "$pruned" = true ]; then
|
|
301
|
+
echo "$content" > "$file"
|
|
302
|
+
local new_size
|
|
303
|
+
new_size=$(wc -c < "$file")
|
|
304
|
+
echo "⚠️ Pruned due to size budget ($size → $new_size bytes)"
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
return 0
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# Detect project
|
|
311
|
+
log "Detecting project type..."
|
|
312
|
+
PROJECT_INFO=$("$SCRIPT_DIR/detect-project.sh" "$PROJECT_DIR")
|
|
313
|
+
[ "$VERBOSE" = true ] && echo "$PROJECT_INFO" | jq . >&2
|
|
314
|
+
|
|
315
|
+
LANGUAGE=$(echo "$PROJECT_INFO" | jq -r '.language')
|
|
316
|
+
VERSION=$(echo "$PROJECT_INFO" | jq -r '.version')
|
|
317
|
+
PROJECT_TYPE=$(echo "$PROJECT_INFO" | jq -r '.type')
|
|
318
|
+
|
|
319
|
+
[ "$LANGUAGE" = "unknown" ] && error "Could not detect project language"
|
|
320
|
+
|
|
321
|
+
# Detect scopes
|
|
322
|
+
log "Detecting scopes..."
|
|
323
|
+
SCOPES_INFO=$("$SCRIPT_DIR/detect-scopes.sh" "$PROJECT_DIR")
|
|
324
|
+
[ "$VERBOSE" = true ] && echo "$SCOPES_INFO" | jq . >&2
|
|
325
|
+
|
|
326
|
+
# Map language to stack filter for extract-commands.sh
|
|
327
|
+
get_stack_filter() {
|
|
328
|
+
local lang="$1"
|
|
329
|
+
case "$lang" in
|
|
330
|
+
go) echo "go" ;;
|
|
331
|
+
php) echo "php" ;;
|
|
332
|
+
python) echo "python" ;;
|
|
333
|
+
typescript|javascript) echo "node" ;;
|
|
334
|
+
*) echo "auto" ;;
|
|
335
|
+
esac
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
# Extract commands
|
|
339
|
+
log "Extracting build commands..."
|
|
340
|
+
PRIMARY_STACK=$(get_stack_filter "$LANGUAGE")
|
|
341
|
+
COMMANDS=$("$SCRIPT_DIR/extract-commands.sh" "$PROJECT_DIR" "$PRIMARY_STACK")
|
|
342
|
+
[ "$VERBOSE" = true ] && echo "$COMMANDS" | jq . >&2
|
|
343
|
+
|
|
344
|
+
# Extract documentation (README, CONTRIBUTING, SECURITY, etc.)
|
|
345
|
+
log "Extracting documentation..."
|
|
346
|
+
DOCS_INFO=$("$SCRIPT_DIR/extract-documentation.sh" "$PROJECT_DIR")
|
|
347
|
+
[ "$VERBOSE" = true ] && echo "$DOCS_INFO" | jq . >&2
|
|
348
|
+
|
|
349
|
+
# Extract platform files (.github/, .gitlab/, etc.)
|
|
350
|
+
log "Extracting platform files..."
|
|
351
|
+
PLATFORM_INFO=$("$SCRIPT_DIR/extract-platform-files.sh" "$PROJECT_DIR")
|
|
352
|
+
[ "$VERBOSE" = true ] && echo "$PLATFORM_INFO" | jq . >&2
|
|
353
|
+
|
|
354
|
+
# Extract IDE settings (.editorconfig, .vscode/, etc.)
|
|
355
|
+
log "Extracting IDE settings..."
|
|
356
|
+
IDE_INFO=$("$SCRIPT_DIR/extract-ide-settings.sh" "$PROJECT_DIR")
|
|
357
|
+
[ "$VERBOSE" = true ] && echo "$IDE_INFO" | jq . >&2
|
|
358
|
+
|
|
359
|
+
# Extract AI agent configs (.cursor/, .claude/, etc.)
|
|
360
|
+
log "Extracting AI agent configs..."
|
|
361
|
+
AGENT_INFO=$("$SCRIPT_DIR/extract-agent-configs.sh" "$PROJECT_DIR")
|
|
362
|
+
[ "$VERBOSE" = true ] && echo "$AGENT_INFO" | jq . >&2
|
|
363
|
+
|
|
364
|
+
# Generate file map
|
|
365
|
+
log "Generating file map..."
|
|
366
|
+
FILE_MAP=$("$SCRIPT_DIR/generate-file-map.sh" "$PROJECT_DIR" 2>/dev/null || echo "")
|
|
367
|
+
|
|
368
|
+
# Detect golden samples
|
|
369
|
+
log "Detecting golden samples..."
|
|
370
|
+
GOLDEN_SAMPLES=$("$SCRIPT_DIR/detect-golden-samples.sh" "$PROJECT_DIR" 2>/dev/null || echo "")
|
|
371
|
+
|
|
372
|
+
# Detect utilities
|
|
373
|
+
log "Detecting utilities..."
|
|
374
|
+
UTILITIES_LIST=$("$SCRIPT_DIR/detect-utilities.sh" "$PROJECT_DIR" 2>/dev/null || echo "")
|
|
375
|
+
|
|
376
|
+
# Detect heuristics
|
|
377
|
+
log "Detecting heuristics..."
|
|
378
|
+
HEURISTICS=$("$SCRIPT_DIR/detect-heuristics.sh" "$PROJECT_DIR" 2>/dev/null || echo "")
|
|
379
|
+
|
|
380
|
+
# Extract quality configs (detailed linter/formatter settings)
|
|
381
|
+
log "Extracting quality configs..."
|
|
382
|
+
QUALITY_CONFIG=$("$SCRIPT_DIR/extract-quality-configs.sh" "$PROJECT_DIR" 2>/dev/null || echo '{}')
|
|
383
|
+
[ "$VERBOSE" = true ] && echo "$QUALITY_CONFIG" | jq . >&2
|
|
384
|
+
|
|
385
|
+
# Extract CI commands
|
|
386
|
+
log "Extracting CI commands..."
|
|
387
|
+
CI_INFO=$("$SCRIPT_DIR/extract-ci-commands.sh" "$PROJECT_DIR" 2>/dev/null || echo '{}')
|
|
388
|
+
[ "$VERBOSE" = true ] && echo "$CI_INFO" | jq . >&2
|
|
389
|
+
|
|
390
|
+
# Extract CI rules (version matrices, quality gates, coverage thresholds)
|
|
391
|
+
log "Extracting CI rules..."
|
|
392
|
+
CI_RULES=$("$SCRIPT_DIR/extract-ci-rules.sh" "$PROJECT_DIR" 2>/dev/null || echo '{}')
|
|
393
|
+
[ "$VERBOSE" = true ] && echo "$CI_RULES" | jq . >&2
|
|
394
|
+
|
|
395
|
+
# Extract GitHub repository settings
|
|
396
|
+
log "Extracting GitHub settings..."
|
|
397
|
+
GITHUB_SETTINGS=$("$SCRIPT_DIR/extract-github-settings.sh" "$PROJECT_DIR" 2>/dev/null || echo '{}')
|
|
398
|
+
[ "$VERBOSE" = true ] && echo "$GITHUB_SETTINGS" | jq . >&2
|
|
399
|
+
|
|
400
|
+
# Extract GitHub rulesets
|
|
401
|
+
log "Extracting GitHub rulesets..."
|
|
402
|
+
GITHUB_RULESETS=$("$SCRIPT_DIR/extract-github-rulesets.sh" "$PROJECT_DIR" 2>/dev/null || echo '{"rulesets":[],"merge_queue":false,"required_checks":[],"signed_commits":false}')
|
|
403
|
+
[ "$VERBOSE" = true ] && echo "$GITHUB_RULESETS" | jq . >&2
|
|
404
|
+
|
|
405
|
+
# Extract ADRs
|
|
406
|
+
log "Extracting ADRs..."
|
|
407
|
+
ADR_INFO=$("$SCRIPT_DIR/extract-adrs.sh" "$PROJECT_DIR" 2>/dev/null || echo '{"adr_count":0,"adr_directory":null,"adrs":[]}')
|
|
408
|
+
[ "$VERBOSE" = true ] && echo "$ADR_INFO" | jq . >&2
|
|
409
|
+
|
|
410
|
+
# Extract architecture / module boundary rules
|
|
411
|
+
log "Extracting architecture rules..."
|
|
412
|
+
ARCH_RULES=$("$SCRIPT_DIR/extract-architecture-rules.sh" "$PROJECT_DIR" 2>/dev/null || echo '{}')
|
|
413
|
+
[ "$VERBOSE" = true ] && echo "$ARCH_RULES" | jq . >&2
|
|
414
|
+
|
|
415
|
+
# Determine command source confidence
|
|
416
|
+
# Priority: CI > Makefile > package.json/composer.json > fallback defaults
|
|
417
|
+
get_command_source() {
|
|
418
|
+
local ci_system
|
|
419
|
+
ci_system=$(echo "$CI_INFO" | jq -r '.ci_system // "none"')
|
|
420
|
+
|
|
421
|
+
if [ "$ci_system" != "none" ] && [ "$ci_system" != "null" ]; then
|
|
422
|
+
local ci_commands
|
|
423
|
+
ci_commands=$(echo "$CI_INFO" | jq -r '.github_actions.run_commands // .gitlab_ci.script_commands // [] | length')
|
|
424
|
+
if [ "$ci_commands" -gt 0 ]; then
|
|
425
|
+
echo "CI ($ci_system)"
|
|
426
|
+
return 0
|
|
427
|
+
fi
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
if [ -f "Makefile" ]; then
|
|
431
|
+
echo "Makefile"
|
|
432
|
+
return 0
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
case "$LANGUAGE" in
|
|
436
|
+
"typescript"|"javascript")
|
|
437
|
+
[ -f "package.json" ] && echo "package.json" && return 0
|
|
438
|
+
;;
|
|
439
|
+
"php")
|
|
440
|
+
[ -f "composer.json" ] && echo "composer.json" && return 0
|
|
441
|
+
;;
|
|
442
|
+
"python")
|
|
443
|
+
[ -f "pyproject.toml" ] && echo "pyproject.toml" && return 0
|
|
444
|
+
[ -f "setup.py" ] && echo "setup.py" && return 0
|
|
445
|
+
;;
|
|
446
|
+
"go")
|
|
447
|
+
[ -f "go.mod" ] && echo "go.mod" && return 0
|
|
448
|
+
;;
|
|
449
|
+
esac
|
|
450
|
+
|
|
451
|
+
echo "defaults"
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
COMMAND_SOURCE=$(get_command_source)
|
|
455
|
+
log "Command source: $COMMAND_SOURCE"
|
|
456
|
+
|
|
457
|
+
# Analyze git history for conventions
|
|
458
|
+
log "Analyzing git history..."
|
|
459
|
+
GIT_HISTORY=$("$SCRIPT_DIR/analyze-git-history.sh" "$PROJECT_DIR" 2>/dev/null || echo '{}')
|
|
460
|
+
[ "$VERBOSE" = true ] && echo "$GIT_HISTORY" | jq . >&2
|
|
461
|
+
|
|
462
|
+
# Helper: Safe jq extraction that filters null values
|
|
463
|
+
# Usage: jq_safe "$json" '.path.to.value'
|
|
464
|
+
jq_safe() {
|
|
465
|
+
local json="$1"
|
|
466
|
+
local path="$2"
|
|
467
|
+
local result
|
|
468
|
+
result=$(echo "$json" | jq -r "$path // empty | select(. != null and . != \"null\" and . != \"\")")
|
|
469
|
+
echo "$result"
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
# Helper: Build quality standards from quality config
|
|
473
|
+
build_quality_standards() {
|
|
474
|
+
local quality_json="$1"
|
|
475
|
+
local standards=""
|
|
476
|
+
|
|
477
|
+
# Get detected tools
|
|
478
|
+
local tools
|
|
479
|
+
tools=$(echo "$quality_json" | jq -r '.detected_tools | if . and . != null then join(", ") else "" end | select(. != "")')
|
|
480
|
+
[ -n "$tools" ] && standards="$standards- Quality tools: $tools\n"
|
|
481
|
+
|
|
482
|
+
# PHPStan level
|
|
483
|
+
local phpstan_level
|
|
484
|
+
phpstan_level=$(jq_safe "$quality_json" '.phpstan.level')
|
|
485
|
+
[ -n "$phpstan_level" ] && standards="$standards- PHPStan level: $phpstan_level (do not lower)\n"
|
|
486
|
+
|
|
487
|
+
# TypeScript strict mode
|
|
488
|
+
local ts_strict
|
|
489
|
+
ts_strict=$(jq_safe "$quality_json" '.typescript.strict')
|
|
490
|
+
[ "$ts_strict" = "true" ] && standards="$standards- TypeScript: strict mode enabled\n"
|
|
491
|
+
|
|
492
|
+
# Line length settings
|
|
493
|
+
local line_length
|
|
494
|
+
line_length=$(jq_safe "$quality_json" '.golangci_lint.line_length // .prettier.print_width // .black.line_length // .ruff.line_length')
|
|
495
|
+
[ -n "$line_length" ] && standards="$standards- Line length: $line_length\n"
|
|
496
|
+
|
|
497
|
+
# ESLint extends
|
|
498
|
+
local eslint_extends
|
|
499
|
+
eslint_extends=$(jq_safe "$quality_json" '.eslint.extends')
|
|
500
|
+
[ -n "$eslint_extends" ] && standards="$standards- ESLint: extends $eslint_extends\n"
|
|
501
|
+
|
|
502
|
+
# PHP-CS-Fixer ruleset
|
|
503
|
+
local php_cs_ruleset
|
|
504
|
+
php_cs_ruleset=$(jq_safe "$quality_json" '.php_cs_fixer.rule_set')
|
|
505
|
+
[ -n "$php_cs_ruleset" ] && standards="$standards- PHP-CS-Fixer: $php_cs_ruleset rules\n"
|
|
506
|
+
|
|
507
|
+
# Mypy strict
|
|
508
|
+
local mypy_strict
|
|
509
|
+
mypy_strict=$(jq_safe "$quality_json" '.mypy.strict')
|
|
510
|
+
[ "$mypy_strict" = "True" ] || [ "$mypy_strict" = "true" ] && standards="$standards- Mypy: strict mode enabled\n"
|
|
511
|
+
|
|
512
|
+
# Ruff select rules
|
|
513
|
+
local ruff_select
|
|
514
|
+
ruff_select=$(jq_safe "$quality_json" '.ruff.select')
|
|
515
|
+
[ -n "$ruff_select" ] && standards="$standards- Ruff: $ruff_select\n"
|
|
516
|
+
|
|
517
|
+
# Default if nothing found
|
|
518
|
+
[ -z "$standards" ] && standards="- Follow project linting and formatting rules\n- Write tests for new functionality\n- Keep functions focused and well-documented\n"
|
|
519
|
+
|
|
520
|
+
echo -e "$standards"
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
# Helper: Build CI/Quality Gates section from CI rules
|
|
524
|
+
build_ci_rules_section() {
|
|
525
|
+
local ci_json="$1"
|
|
526
|
+
local section=""
|
|
527
|
+
|
|
528
|
+
local platform
|
|
529
|
+
platform=$(echo "$ci_json" | jq -r '.ci_platform // empty')
|
|
530
|
+
[[ -z "$platform" ]] && return 0
|
|
531
|
+
|
|
532
|
+
section="## CI/Quality Gates\n"
|
|
533
|
+
section="${section}> Platform: ${platform}\n\n"
|
|
534
|
+
|
|
535
|
+
# Version matrix
|
|
536
|
+
local versions_block=""
|
|
537
|
+
local php_versions
|
|
538
|
+
php_versions=$(echo "$ci_json" | jq -r '.php_versions // [] | if length > 0 then "PHP " + join(", ") else "" end')
|
|
539
|
+
[[ -n "$php_versions" ]] && versions_block="${versions_block}- ${php_versions}\n"
|
|
540
|
+
|
|
541
|
+
local node_versions
|
|
542
|
+
node_versions=$(echo "$ci_json" | jq -r '.node_versions // [] | if length > 0 then "Node " + join(", ") else "" end')
|
|
543
|
+
[[ -n "$node_versions" ]] && versions_block="${versions_block}- ${node_versions}\n"
|
|
544
|
+
|
|
545
|
+
local go_version
|
|
546
|
+
go_version=$(echo "$ci_json" | jq -r '.go_version // empty')
|
|
547
|
+
[[ -n "$go_version" ]] && versions_block="${versions_block}- Go ${go_version}\n"
|
|
548
|
+
|
|
549
|
+
local python_versions
|
|
550
|
+
python_versions=$(echo "$ci_json" | jq -r '.python_versions // [] | if length > 0 then "Python " + join(", ") else "" end')
|
|
551
|
+
[[ -n "$python_versions" ]] && versions_block="${versions_block}- ${python_versions}\n"
|
|
552
|
+
|
|
553
|
+
if [[ -n "$versions_block" ]]; then
|
|
554
|
+
section="${section}### Version Matrix\n${versions_block}\n"
|
|
555
|
+
fi
|
|
556
|
+
|
|
557
|
+
# Quality gates
|
|
558
|
+
local gates
|
|
559
|
+
gates=$(echo "$ci_json" | jq -r '.quality_gates // [] | if length > 0 then .[] else empty end')
|
|
560
|
+
if [[ -n "$gates" ]]; then
|
|
561
|
+
section="${section}### Quality Gates (must pass before merge)\n"
|
|
562
|
+
while IFS= read -r gate; do
|
|
563
|
+
section="${section}- \`${gate}\`\n"
|
|
564
|
+
done <<< "$gates"
|
|
565
|
+
section="${section}\n"
|
|
566
|
+
fi
|
|
567
|
+
|
|
568
|
+
# Coverage threshold
|
|
569
|
+
local coverage
|
|
570
|
+
coverage=$(echo "$ci_json" | jq -r '.coverage_threshold // empty')
|
|
571
|
+
[[ -n "$coverage" ]] && section="${section}### Coverage\n- Minimum threshold: ${coverage}\n\n"
|
|
572
|
+
|
|
573
|
+
# Security workflows
|
|
574
|
+
local sec_wfs
|
|
575
|
+
sec_wfs=$(echo "$ci_json" | jq -r '.security_workflows // [] | if length > 0 then .[] else empty end')
|
|
576
|
+
if [[ -n "$sec_wfs" ]]; then
|
|
577
|
+
section="${section}### Security Workflows\n"
|
|
578
|
+
while IFS= read -r wf; do
|
|
579
|
+
section="${section}- \`${wf}\`\n"
|
|
580
|
+
done <<< "$sec_wfs"
|
|
581
|
+
section="${section}\n"
|
|
582
|
+
fi
|
|
583
|
+
|
|
584
|
+
# Pinned actions
|
|
585
|
+
local pinned
|
|
586
|
+
pinned=$(echo "$ci_json" | jq -r '.pinned_actions // empty')
|
|
587
|
+
if [[ "$pinned" == "true" ]]; then
|
|
588
|
+
section="${section}### Constraints\n- Actions are SHA-pinned — maintain pinning when updating\n"
|
|
589
|
+
fi
|
|
590
|
+
|
|
591
|
+
# Linter configs
|
|
592
|
+
local linters
|
|
593
|
+
linters=$(echo "$ci_json" | jq -r '.linter_configs // [] | if length > 0 then .[] else empty end')
|
|
594
|
+
if [[ -n "$linters" ]]; then
|
|
595
|
+
section="${section}- Linter configs: "
|
|
596
|
+
local first=true
|
|
597
|
+
while IFS= read -r cfg; do
|
|
598
|
+
if $first; then
|
|
599
|
+
section="${section}\`${cfg}\`"
|
|
600
|
+
first=false
|
|
601
|
+
else
|
|
602
|
+
section="${section}, \`${cfg}\`"
|
|
603
|
+
fi
|
|
604
|
+
done <<< "$linters"
|
|
605
|
+
section="${section}\n"
|
|
606
|
+
fi
|
|
607
|
+
|
|
608
|
+
echo -e "$section"
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
# Helper: Build module boundaries section from architecture rules
|
|
612
|
+
build_module_boundaries() {
|
|
613
|
+
local arch_json="$1"
|
|
614
|
+
local section=""
|
|
615
|
+
|
|
616
|
+
# Check if we have any data
|
|
617
|
+
local framework
|
|
618
|
+
framework=$(echo "$arch_json" | jq -r '.framework // empty' 2>/dev/null)
|
|
619
|
+
[ -z "$framework" ] && return 0
|
|
620
|
+
|
|
621
|
+
local rule_count
|
|
622
|
+
rule_count=$(echo "$arch_json" | jq -r '.rules | length // 0' 2>/dev/null)
|
|
623
|
+
local internal_count
|
|
624
|
+
internal_count=$(echo "$arch_json" | jq -r '.internal_packages | length // 0' 2>/dev/null)
|
|
625
|
+
local has_layers
|
|
626
|
+
has_layers=$(echo "$arch_json" | jq -r '.layer_definitions | length > 0' 2>/dev/null)
|
|
627
|
+
|
|
628
|
+
[ "$rule_count" = "0" ] && [ "$internal_count" = "0" ] && [ "$has_layers" != "true" ] && return 0
|
|
629
|
+
|
|
630
|
+
section="## Module Boundaries\n"
|
|
631
|
+
section="${section}> Source: ${framework}\n\n"
|
|
632
|
+
|
|
633
|
+
# Internal packages (Go)
|
|
634
|
+
if [ "$internal_count" -gt 0 ]; then
|
|
635
|
+
section="${section}### Internal Packages (compiler-enforced)\n"
|
|
636
|
+
while IFS= read -r pkg; do
|
|
637
|
+
section="${section}- \`${pkg}\`\n"
|
|
638
|
+
done < <(echo "$arch_json" | jq -r '.internal_packages[]' 2>/dev/null)
|
|
639
|
+
section="${section}\n"
|
|
640
|
+
fi
|
|
641
|
+
|
|
642
|
+
# Layer definitions
|
|
643
|
+
if [ "$has_layers" = "true" ]; then
|
|
644
|
+
local arch_pattern
|
|
645
|
+
arch_pattern=$(echo "$arch_json" | jq -r '.layer_definitions.architecture_pattern // empty' 2>/dev/null)
|
|
646
|
+
if [ -n "$arch_pattern" ]; then
|
|
647
|
+
section="${section}### Architecture Pattern\n"
|
|
648
|
+
section="${section}- ${arch_pattern}\n\n"
|
|
649
|
+
fi
|
|
650
|
+
|
|
651
|
+
# Non-pattern layer definitions (deptrac etc.)
|
|
652
|
+
local layer_keys
|
|
653
|
+
layer_keys=$(echo "$arch_json" | jq -r '.layer_definitions | keys[] | select(. != "architecture_pattern")' 2>/dev/null)
|
|
654
|
+
if [ -n "$layer_keys" ]; then
|
|
655
|
+
section="${section}### Layers\n"
|
|
656
|
+
while IFS= read -r layer; do
|
|
657
|
+
local values
|
|
658
|
+
values=$(echo "$arch_json" | jq -r --arg l "$layer" '.layer_definitions[$l] | if type == "array" then join(", ") else tostring end' 2>/dev/null)
|
|
659
|
+
section="${section}- **${layer}**: ${values}\n"
|
|
660
|
+
done <<< "$layer_keys"
|
|
661
|
+
section="${section}\n"
|
|
662
|
+
fi
|
|
663
|
+
fi
|
|
664
|
+
|
|
665
|
+
# Dependency rules
|
|
666
|
+
if [ "$rule_count" -gt 0 ]; then
|
|
667
|
+
section="${section}### Dependency Rules\n"
|
|
668
|
+
section="${section}| Source | Target | Rule | Reason |\n"
|
|
669
|
+
section="${section}|--------|--------|------|--------|\n"
|
|
670
|
+
while IFS= read -r rule_line; do
|
|
671
|
+
section="${section}${rule_line}\n"
|
|
672
|
+
done < <(echo "$arch_json" | jq -r '.rules[] |
|
|
673
|
+
"| " + .source + " | `" + .target + "` | " + .type + " | " + (.reason // "-") + " |"' 2>/dev/null)
|
|
674
|
+
fi
|
|
675
|
+
|
|
676
|
+
echo -e "$section"
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
# Helper: Build workflow section from git history
|
|
680
|
+
build_workflow_info() {
|
|
681
|
+
local git_json="$1"
|
|
682
|
+
local workflow=""
|
|
683
|
+
|
|
684
|
+
# Commit convention
|
|
685
|
+
local convention
|
|
686
|
+
convention=$(echo "$git_json" | jq -r '.commit_convention.convention // "unknown" | select(. != null)')
|
|
687
|
+
[ -z "$convention" ] && convention="unknown"
|
|
688
|
+
local confidence
|
|
689
|
+
confidence=$(echo "$git_json" | jq -r '.commit_convention.confidence // 0 | select(. != null)')
|
|
690
|
+
[ -z "$confidence" ] && confidence=0
|
|
691
|
+
|
|
692
|
+
if [ "$convention" != "unknown" ] && [ "$confidence" -gt 30 ]; then
|
|
693
|
+
case "$convention" in
|
|
694
|
+
"conventional-commits")
|
|
695
|
+
workflow="$workflow- Commits: Use Conventional Commits (feat:, fix:, docs:, etc.)\n"
|
|
696
|
+
;;
|
|
697
|
+
"tag-prefix")
|
|
698
|
+
workflow="$workflow- Commits: Use [TAG] prefix style\n"
|
|
699
|
+
;;
|
|
700
|
+
"emoji")
|
|
701
|
+
workflow="$workflow- Commits: Use emoji prefixes\n"
|
|
702
|
+
;;
|
|
703
|
+
"ticket-reference")
|
|
704
|
+
workflow="$workflow- Commits: Include ticket references (JIRA-123, #123)\n"
|
|
705
|
+
;;
|
|
706
|
+
esac
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
# Merge strategy
|
|
710
|
+
local merge_strategy
|
|
711
|
+
merge_strategy=$(echo "$git_json" | jq -r '.merge_strategy.strategy // "unknown"')
|
|
712
|
+
case "$merge_strategy" in
|
|
713
|
+
"squash-and-merge")
|
|
714
|
+
workflow="$workflow- PRs: Squash and merge\n"
|
|
715
|
+
;;
|
|
716
|
+
"merge-commits")
|
|
717
|
+
workflow="$workflow- PRs: Create merge commits\n"
|
|
718
|
+
;;
|
|
719
|
+
esac
|
|
720
|
+
|
|
721
|
+
# Branch naming
|
|
722
|
+
local branch_pattern
|
|
723
|
+
branch_pattern=$(echo "$git_json" | jq -r '.branch_naming.pattern // "unknown"')
|
|
724
|
+
local uses_feature
|
|
725
|
+
uses_feature=$(echo "$git_json" | jq -r '.branch_naming.uses_feature_branches // false')
|
|
726
|
+
|
|
727
|
+
if [ "$uses_feature" = "true" ]; then
|
|
728
|
+
workflow="$workflow- Branches: feature/, fix/, hotfix/ prefixes\n"
|
|
729
|
+
elif [ "$branch_pattern" = "ticket-based" ]; then
|
|
730
|
+
workflow="$workflow- Branches: Include ticket reference in name\n"
|
|
731
|
+
fi
|
|
732
|
+
|
|
733
|
+
# Release pattern
|
|
734
|
+
local release_pattern
|
|
735
|
+
release_pattern=$(echo "$git_json" | jq -r '.releases.pattern // "unknown"')
|
|
736
|
+
local uses_v
|
|
737
|
+
uses_v=$(echo "$git_json" | jq -r '.releases.uses_v_prefix // false')
|
|
738
|
+
|
|
739
|
+
if [ "$release_pattern" = "semver" ]; then
|
|
740
|
+
[ "$uses_v" = "true" ] && workflow="$workflow- Releases: Semantic versioning (vX.Y.Z)\n" || workflow="$workflow- Releases: Semantic versioning (X.Y.Z)\n"
|
|
741
|
+
elif [ "$release_pattern" = "calver" ]; then
|
|
742
|
+
workflow="$workflow- Releases: Calendar versioning (YYYY.MM.DD)\n"
|
|
743
|
+
fi
|
|
744
|
+
|
|
745
|
+
# Default branch
|
|
746
|
+
local default_branch
|
|
747
|
+
default_branch=$(echo "$git_json" | jq -r '.default_branch // "main"')
|
|
748
|
+
[ -n "$default_branch" ] && [ "$default_branch" != "null" ] && workflow="$workflow- Default branch: $default_branch\n"
|
|
749
|
+
|
|
750
|
+
echo -e "$workflow"
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
# Generate root AGENTS.md
|
|
754
|
+
ROOT_FILE="$PROJECT_DIR/AGENTS.md"
|
|
755
|
+
|
|
756
|
+
if [ -f "$ROOT_FILE" ] && [ "$FORCE" = false ] && [ "$UPDATE_ONLY" = false ]; then
|
|
757
|
+
log "Root AGENTS.md already exists, skipping (use --force to regenerate)"
|
|
758
|
+
elif [ "$DRY_RUN" = true ]; then
|
|
759
|
+
echo "[DRY-RUN] Would create/update: $ROOT_FILE"
|
|
760
|
+
else
|
|
761
|
+
log "Generating root AGENTS.md..."
|
|
762
|
+
|
|
763
|
+
# Select template
|
|
764
|
+
if [ "$STYLE" = "verbose" ]; then
|
|
765
|
+
TEMPLATE="$TEMPLATE_DIR/root-verbose.md"
|
|
766
|
+
else
|
|
767
|
+
TEMPLATE="$TEMPLATE_DIR/root-thin.md"
|
|
768
|
+
fi
|
|
769
|
+
|
|
770
|
+
# Prepare template variables
|
|
771
|
+
declare -A vars
|
|
772
|
+
vars[TIMESTAMP]=$(get_timestamp)
|
|
773
|
+
vars[VERIFIED_TIMESTAMP]="never"
|
|
774
|
+
vars[COMMAND_SOURCE]="$COMMAND_SOURCE"
|
|
775
|
+
vars[LANGUAGE_CONVENTIONS]=$(get_language_conventions "$LANGUAGE" "$VERSION")
|
|
776
|
+
|
|
777
|
+
# Command extraction - only set if non-empty (leaves placeholder for row deletion)
|
|
778
|
+
set_if_present() {
|
|
779
|
+
local key="$1"
|
|
780
|
+
local value="$2"
|
|
781
|
+
if [ -n "$value" ] && [ "$value" != "null" ]; then
|
|
782
|
+
vars[$key]="$value"
|
|
783
|
+
fi
|
|
784
|
+
return 0
|
|
785
|
+
}
|
|
786
|
+
set_if_present INSTALL_CMD "$(echo "$COMMANDS" | jq -r '.install // empty')"
|
|
787
|
+
set_if_present TYPECHECK_CMD "$(echo "$COMMANDS" | jq -r '.typecheck // empty')"
|
|
788
|
+
set_if_present LINT_CMD "$(echo "$COMMANDS" | jq -r '.lint // empty')"
|
|
789
|
+
set_if_present FORMAT_CMD "$(echo "$COMMANDS" | jq -r '.format // empty')"
|
|
790
|
+
set_if_present TEST_CMD "$(echo "$COMMANDS" | jq -r '.test // empty')"
|
|
791
|
+
set_if_present TEST_SINGLE_CMD "$(echo "$COMMANDS" | jq -r '.test_single // empty')"
|
|
792
|
+
set_if_present BUILD_CMD "$(echo "$COMMANDS" | jq -r '.build // empty')"
|
|
793
|
+
|
|
794
|
+
# Time estimates - check verification JSON first, then use heuristics
|
|
795
|
+
VERIFICATION_JSON="$PROJECT_DIR/.agents/command-verification.json"
|
|
796
|
+
get_verified_time() {
|
|
797
|
+
local pattern="$1"
|
|
798
|
+
local default="$2"
|
|
799
|
+
if [ -f "$VERIFICATION_JSON" ]; then
|
|
800
|
+
local duration_ms
|
|
801
|
+
# Try to find command in verification JSON using regex pattern
|
|
802
|
+
duration_ms=$(jq -r --arg pattern "$pattern" '.commands | to_entries[] | select(.key | test($pattern; "i")) | .value.duration_ms // empty' "$VERIFICATION_JSON" 2>/dev/null | head -1)
|
|
803
|
+
if [ -n "$duration_ms" ] && [ "$duration_ms" != "null" ] && [ "$duration_ms" -gt 0 ] 2>/dev/null; then
|
|
804
|
+
# Convert ms to human-readable
|
|
805
|
+
if [ "$duration_ms" -lt 1000 ]; then
|
|
806
|
+
echo "~${duration_ms}ms"
|
|
807
|
+
elif [ "$duration_ms" -lt 60000 ]; then
|
|
808
|
+
echo "~$((duration_ms / 1000))s"
|
|
809
|
+
else
|
|
810
|
+
echo "~$((duration_ms / 60000))m"
|
|
811
|
+
fi
|
|
812
|
+
return
|
|
813
|
+
fi
|
|
814
|
+
fi
|
|
815
|
+
echo "$default"
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
# Check if we have verification data
|
|
819
|
+
if [ -f "$VERIFICATION_JSON" ]; then
|
|
820
|
+
verified_at=$(jq -r '.verified_at // empty' "$VERIFICATION_JSON" 2>/dev/null | cut -dT -f1)
|
|
821
|
+
if [ -n "$verified_at" ]; then
|
|
822
|
+
vars[VERIFIED_TIMESTAMP]="$verified_at"
|
|
823
|
+
vars[VERIFIED_STATUS]=" (verified ✓)"
|
|
824
|
+
else
|
|
825
|
+
vars[VERIFIED_TIMESTAMP]="never"
|
|
826
|
+
vars[VERIFIED_STATUS]=" (unverified)"
|
|
827
|
+
fi
|
|
828
|
+
else
|
|
829
|
+
vars[VERIFIED_TIMESTAMP]="never"
|
|
830
|
+
vars[VERIFIED_STATUS]=" (unverified)"
|
|
831
|
+
fi
|
|
832
|
+
|
|
833
|
+
vars[TYPECHECK_TIME]=$(get_verified_time "typecheck" "~15s")
|
|
834
|
+
vars[LINT_TIME]=$(get_verified_time "lint|cs-fixer|eslint" "~10s")
|
|
835
|
+
vars[FORMAT_TIME]=$(get_verified_time "format|prettier|black|cs-fixer fix$" "~5s")
|
|
836
|
+
vars[TEST_TIME]=$(get_verified_time "test|phpunit|jest|pytest" "~30s")
|
|
837
|
+
vars[BUILD_TIME]=$(get_verified_time "build" "~30s")
|
|
838
|
+
|
|
839
|
+
# File map
|
|
840
|
+
vars[FILE_MAP]="$FILE_MAP"
|
|
841
|
+
|
|
842
|
+
# Golden samples, utilities, and heuristics from detection scripts
|
|
843
|
+
vars[GOLDEN_SAMPLES]="$GOLDEN_SAMPLES"
|
|
844
|
+
vars[UTILITIES_LIST]="$UTILITIES_LIST"
|
|
845
|
+
|
|
846
|
+
# Add workflow conventions from git analysis to heuristics
|
|
847
|
+
workflow_info=$(build_workflow_info "$GIT_HISTORY")
|
|
848
|
+
workflow_heuristics=""
|
|
849
|
+
# Convert workflow info to heuristic table rows
|
|
850
|
+
if echo "$workflow_info" | grep -q "Commits:"; then
|
|
851
|
+
commit_convention=$(echo "$workflow_info" | grep "Commits:" | sed 's/- Commits: //')
|
|
852
|
+
workflow_heuristics="${workflow_heuristics}| Committing | $commit_convention |
|
|
853
|
+
"
|
|
854
|
+
fi
|
|
855
|
+
if echo "$workflow_info" | grep -q "PRs:"; then
|
|
856
|
+
pr_strategy=$(echo "$workflow_info" | grep "PRs:" | sed 's/- PRs: //')
|
|
857
|
+
workflow_heuristics="${workflow_heuristics}| Merging PRs | $pr_strategy |
|
|
858
|
+
"
|
|
859
|
+
fi
|
|
860
|
+
if echo "$workflow_info" | grep -q "Branches:"; then
|
|
861
|
+
branch_convention=$(echo "$workflow_info" | grep "Branches:" | sed 's/- Branches: //')
|
|
862
|
+
workflow_heuristics="${workflow_heuristics}| Creating branches | Use $branch_convention |
|
|
863
|
+
"
|
|
864
|
+
fi
|
|
865
|
+
|
|
866
|
+
# Combine detected heuristics with workflow heuristics
|
|
867
|
+
# Ensure no trailing blank lines
|
|
868
|
+
combined_heuristics=""
|
|
869
|
+
if [ -n "$HEURISTICS" ]; then
|
|
870
|
+
# Trim blank lines and trailing whitespace
|
|
871
|
+
combined_heuristics=$(printf '%s' "$HEURISTICS" | sed '/^[[:space:]]*$/d')
|
|
872
|
+
fi
|
|
873
|
+
if [ -n "$workflow_heuristics" ]; then
|
|
874
|
+
# Add newline separator only if both have content
|
|
875
|
+
if [ -n "$combined_heuristics" ]; then
|
|
876
|
+
combined_heuristics="${combined_heuristics}
|
|
877
|
+
${workflow_heuristics}"
|
|
878
|
+
else
|
|
879
|
+
combined_heuristics="$workflow_heuristics"
|
|
880
|
+
fi
|
|
881
|
+
fi
|
|
882
|
+
# Remove any trailing newlines
|
|
883
|
+
combined_heuristics=$(printf '%s' "$combined_heuristics" | sed '/^[[:space:]]*$/d')
|
|
884
|
+
vars[HEURISTICS]="$combined_heuristics"
|
|
885
|
+
|
|
886
|
+
# Repository settings (from GitHub API)
|
|
887
|
+
build_repo_settings() {
|
|
888
|
+
local settings_json="$1"
|
|
889
|
+
local available
|
|
890
|
+
available=$(echo "$settings_json" | jq -r '.available // false')
|
|
891
|
+
|
|
892
|
+
[ "$available" != "true" ] && return 0
|
|
893
|
+
|
|
894
|
+
local content=""
|
|
895
|
+
local default_branch merge_strategies required_approvals required_checks require_up_to_date
|
|
896
|
+
|
|
897
|
+
default_branch=$(echo "$settings_json" | jq -r '.default_branch')
|
|
898
|
+
merge_strategies=$(echo "$settings_json" | jq -r '.merge_strategies | join(", ")')
|
|
899
|
+
required_approvals=$(echo "$settings_json" | jq -r '.required_approvals')
|
|
900
|
+
required_checks=$(echo "$settings_json" | jq -r 'if .required_checks | length > 0 then .required_checks | map("`" + . + "`") | join(", ") else "" end')
|
|
901
|
+
require_up_to_date=$(echo "$settings_json" | jq -r '.require_up_to_date')
|
|
902
|
+
|
|
903
|
+
content="- **Default branch:** \`$default_branch\`\n"
|
|
904
|
+
[ -n "$merge_strategies" ] && content="${content}- **Merge strategy:** $merge_strategies\n"
|
|
905
|
+
[ "$required_approvals" != "0" ] && [ "$required_approvals" != "null" ] && content="${content}- **Required approvals:** $required_approvals\n"
|
|
906
|
+
[ -n "$required_checks" ] && content="${content}- **Required checks:** $required_checks\n"
|
|
907
|
+
[ "$require_up_to_date" = "true" ] && content="${content}- **Require up-to-date:** yes — rebase before merge\n"
|
|
908
|
+
|
|
909
|
+
printf '%b' "$content"
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
# Append ruleset information to repo settings
|
|
913
|
+
build_ruleset_settings() {
|
|
914
|
+
local rulesets_json="$1"
|
|
915
|
+
local ruleset_count
|
|
916
|
+
ruleset_count=$(echo "$rulesets_json" | jq '.rulesets | length')
|
|
917
|
+
|
|
918
|
+
[ "$ruleset_count" -eq 0 ] && return 0
|
|
919
|
+
|
|
920
|
+
local content=""
|
|
921
|
+
local merge_queue signed_commits required_checks
|
|
922
|
+
|
|
923
|
+
merge_queue=$(echo "$rulesets_json" | jq -r '.merge_queue')
|
|
924
|
+
signed_commits=$(echo "$rulesets_json" | jq -r '.signed_commits')
|
|
925
|
+
required_checks=$(echo "$rulesets_json" | jq -r 'if .required_checks | length > 0 then .required_checks | map("`" + . + "`") | join(", ") else "" end')
|
|
926
|
+
|
|
927
|
+
[ "$merge_queue" = "true" ] && content="${content}- **Merge queue:** enabled — PRs merge via queue, not direct merge\n"
|
|
928
|
+
[ "$signed_commits" = "true" ] && content="${content}- **Signed commits:** required\n"
|
|
929
|
+
[ -n "$required_checks" ] && content="${content}- **Required checks (rulesets):** $required_checks\n"
|
|
930
|
+
|
|
931
|
+
# List active rulesets
|
|
932
|
+
local ruleset_names
|
|
933
|
+
ruleset_names=$(echo "$rulesets_json" | jq -r '[.rulesets[].name] | join(", ")')
|
|
934
|
+
[ -n "$ruleset_names" ] && content="${content}- **Active rulesets:** $ruleset_names\n"
|
|
935
|
+
|
|
936
|
+
printf '%b' "$content"
|
|
937
|
+
}
|
|
938
|
+
vars[REPO_SETTINGS]=$(build_repo_settings "$GITHUB_SETTINGS")
|
|
939
|
+
ruleset_settings=$(build_ruleset_settings "$GITHUB_RULESETS")
|
|
940
|
+
if [ -n "$ruleset_settings" ]; then
|
|
941
|
+
vars[REPO_SETTINGS]="${vars[REPO_SETTINGS]}${vars[REPO_SETTINGS]:+$'\n'}${ruleset_settings}"
|
|
942
|
+
fi
|
|
943
|
+
|
|
944
|
+
# Module boundaries (from architecture rules)
|
|
945
|
+
vars[MODULE_BOUNDARIES]=$(build_module_boundaries "$ARCH_RULES")
|
|
946
|
+
|
|
947
|
+
# CI/Quality Gates section (from CI rules)
|
|
948
|
+
vars[CI_RULES_SECTION]=$(build_ci_rules_section "$CI_RULES")
|
|
949
|
+
|
|
950
|
+
# Key Decisions (from ADRs)
|
|
951
|
+
build_key_decisions() {
|
|
952
|
+
local adr_json="$1"
|
|
953
|
+
local adr_count
|
|
954
|
+
adr_count=$(echo "$adr_json" | jq -r '.adr_count')
|
|
955
|
+
|
|
956
|
+
[ "$adr_count" -eq 0 ] || [ "$adr_count" = "null" ] && return 0
|
|
957
|
+
|
|
958
|
+
local adr_dir content=""
|
|
959
|
+
adr_dir=$(echo "$adr_json" | jq -r '.adr_directory')
|
|
960
|
+
|
|
961
|
+
for i in $(seq 0 $((adr_count - 1))); do
|
|
962
|
+
local title status summary file
|
|
963
|
+
file=$(echo "$adr_json" | jq -r ".adrs[$i].file")
|
|
964
|
+
title=$(echo "$adr_json" | jq -r ".adrs[$i].title")
|
|
965
|
+
status=$(echo "$adr_json" | jq -r ".adrs[$i].status")
|
|
966
|
+
summary=$(echo "$adr_json" | jq -r ".adrs[$i].summary")
|
|
967
|
+
|
|
968
|
+
# Show status badge only for non-Accepted (Accepted is the norm)
|
|
969
|
+
local status_badge=""
|
|
970
|
+
if [ "$status" != "Accepted" ] && [ "$status" != "Unknown" ]; then
|
|
971
|
+
status_badge=" [$status]"
|
|
972
|
+
fi
|
|
973
|
+
|
|
974
|
+
content="${content}- **${title}**${status_badge} — ${summary} → [\`${file}\`](${adr_dir}/${file})\n"
|
|
975
|
+
done
|
|
976
|
+
|
|
977
|
+
printf '%b' "$content"
|
|
978
|
+
}
|
|
979
|
+
vars[KEY_DECISIONS]=$(build_key_decisions "$ADR_INFO")
|
|
980
|
+
|
|
981
|
+
# Contribution rules — detect issue-before-PR, AI disclosure, PR templates
|
|
982
|
+
build_contribution_rules() {
|
|
983
|
+
local rules=""
|
|
984
|
+
|
|
985
|
+
# Check CONTRIBUTING.md for issue-before-PR
|
|
986
|
+
if [ -f "CONTRIBUTING.md" ]; then
|
|
987
|
+
if grep -qi "open an issue\|issue first\|issue before\|create an issue" CONTRIBUTING.md 2>/dev/null; then
|
|
988
|
+
rules="$rules\n- **Issue first**: Open an issue and get approval before submitting a PR"
|
|
989
|
+
fi
|
|
990
|
+
if grep -qi "AI\|artificial intelligence\|copilot\|generated\|LLM\|disclose" CONTRIBUTING.md 2>/dev/null; then
|
|
991
|
+
rules="$rules\n- **AI disclosure**: This project requires disclosure of AI-assisted contributions in PR descriptions"
|
|
992
|
+
fi
|
|
993
|
+
fi
|
|
994
|
+
|
|
995
|
+
# Check PR template for issue linking
|
|
996
|
+
for tmpl in .github/pull_request_template.md .github/PULL_REQUEST_TEMPLATE/*.md; do
|
|
997
|
+
if [ -f "$tmpl" ] && grep -qi "Fixes #\|Closes #\|Related issue\|Issue number" "$tmpl" 2>/dev/null; then
|
|
998
|
+
rules="$rules\n- **Link issues**: PR template requires linking to an issue (Fixes #NNN)"
|
|
999
|
+
break
|
|
1000
|
+
fi
|
|
1001
|
+
done
|
|
1002
|
+
|
|
1003
|
+
# Check branch protection for linked issues requirement
|
|
1004
|
+
local protection
|
|
1005
|
+
protection=$(echo "$GITHUB_SETTINGS" | jq -r '.required_linked_issues // false' 2>/dev/null)
|
|
1006
|
+
if [ "$protection" = "true" ]; then
|
|
1007
|
+
rules="$rules\n- **Linked issues required**: Branch protection requires PRs to reference an issue"
|
|
1008
|
+
fi
|
|
1009
|
+
|
|
1010
|
+
[ -n "$rules" ] && printf '%b' "$rules" || true
|
|
1011
|
+
}
|
|
1012
|
+
vars[CONTRIBUTION_RULES]=$(build_contribution_rules)
|
|
1013
|
+
|
|
1014
|
+
# Codebase state - detect migrations, deprecations
|
|
1015
|
+
codebase_state=""
|
|
1016
|
+
[ -d "migrations" ] || [ -d "db/migrate" ] && codebase_state="$codebase_state\n- Database migrations present in migrations/"
|
|
1017
|
+
[ -d "prisma/migrations" ] && codebase_state="$codebase_state\n- Prisma migrations present"
|
|
1018
|
+
grep -rq "DEPRECATED\|@deprecated" --include="*.ts" --include="*.go" --include="*.php" --include="*.py" . 2>/dev/null && \
|
|
1019
|
+
codebase_state="$codebase_state\n- Contains deprecated code (grep for @deprecated)"
|
|
1020
|
+
[ -z "$codebase_state" ] && codebase_state="- No known migrations or tech debt documented"
|
|
1021
|
+
vars[CODEBASE_STATE]=$(echo -e "$codebase_state")
|
|
1022
|
+
|
|
1023
|
+
# Terminology - leave empty for now, needs manual curation
|
|
1024
|
+
vars[TERMINOLOGY]=""
|
|
1025
|
+
|
|
1026
|
+
# Scope index
|
|
1027
|
+
vars[SCOPE_INDEX]=$(build_scope_index "$SCOPES_INFO")
|
|
1028
|
+
|
|
1029
|
+
# Verbose template additional vars
|
|
1030
|
+
if [ "$STYLE" = "verbose" ]; then
|
|
1031
|
+
# Use extracted documentation data where available
|
|
1032
|
+
readme_desc=$(echo "$DOCS_INFO" | jq -r '.readme.description // empty')
|
|
1033
|
+
if [ -n "$readme_desc" ]; then
|
|
1034
|
+
vars[PROJECT_DESCRIPTION]="$readme_desc"
|
|
1035
|
+
else
|
|
1036
|
+
vars[PROJECT_DESCRIPTION]="TODO: Add project description"
|
|
1037
|
+
fi
|
|
1038
|
+
|
|
1039
|
+
vars[LANGUAGE]="$LANGUAGE"
|
|
1040
|
+
vars[VERSION]="$VERSION"
|
|
1041
|
+
vars[BUILD_TOOL]=$(echo "$PROJECT_INFO" | jq -r '.build_tool')
|
|
1042
|
+
vars[FRAMEWORK]=$(echo "$PROJECT_INFO" | jq -r '.framework')
|
|
1043
|
+
vars[PROJECT_TYPE]="$PROJECT_TYPE"
|
|
1044
|
+
vars[BUILD_CMD]=$(echo "$COMMANDS" | jq -r '.build')
|
|
1045
|
+
|
|
1046
|
+
# Extract quality standards from contributing guidelines AND quality config
|
|
1047
|
+
contributing_rules=$(echo "$DOCS_INFO" | jq -r '.contributing.code_style // empty')
|
|
1048
|
+
quality_standards=$(build_quality_standards "$QUALITY_CONFIG")
|
|
1049
|
+
if [ -n "$contributing_rules" ] && [ "$contributing_rules" != "null" ]; then
|
|
1050
|
+
# Combine contributing rules with detected quality config
|
|
1051
|
+
vars[QUALITY_STANDARDS]="$contributing_rules
|
|
1052
|
+
$quality_standards"
|
|
1053
|
+
else
|
|
1054
|
+
vars[QUALITY_STANDARDS]="$quality_standards"
|
|
1055
|
+
fi
|
|
1056
|
+
|
|
1057
|
+
# Extract security guidelines
|
|
1058
|
+
security_policy=$(echo "$DOCS_INFO" | jq -r '.security.policy // empty')
|
|
1059
|
+
if [ -n "$security_policy" ] && [ "$security_policy" != "null" ]; then
|
|
1060
|
+
vars[SECURITY_SPECIFIC]="$security_policy"
|
|
1061
|
+
else
|
|
1062
|
+
vars[SECURITY_SPECIFIC]="- Report vulnerabilities via security@project or SECURITY.md
|
|
1063
|
+
- Never commit secrets or credentials"
|
|
1064
|
+
fi
|
|
1065
|
+
|
|
1066
|
+
vars[TEST_COVERAGE]="40"
|
|
1067
|
+
|
|
1068
|
+
# Try to get test commands from CI info or fall back to detected commands
|
|
1069
|
+
test_cmd=$(echo "$COMMANDS" | jq -r '.test')
|
|
1070
|
+
ci_system=$(echo "$CI_INFO" | jq -r '.ci_system // "none"')
|
|
1071
|
+
|
|
1072
|
+
# Look for specific test commands in CI
|
|
1073
|
+
ci_test_cmd=""
|
|
1074
|
+
case "$ci_system" in
|
|
1075
|
+
"github-actions")
|
|
1076
|
+
ci_test_cmd=$(echo "$CI_INFO" | jq -r '.github_actions.run_commands[]? | select(. | test("test|phpunit|jest|pytest|go test"; "i"))' 2>/dev/null | head -1)
|
|
1077
|
+
;;
|
|
1078
|
+
"gitlab-ci")
|
|
1079
|
+
ci_test_cmd=$(echo "$CI_INFO" | jq -r '.gitlab_ci.script_commands[]? | select(. | test("test|phpunit|jest|pytest|go test"; "i"))' 2>/dev/null | head -1)
|
|
1080
|
+
;;
|
|
1081
|
+
esac
|
|
1082
|
+
|
|
1083
|
+
vars[TEST_FAST_CMD]="${test_cmd:-make test}"
|
|
1084
|
+
if [ -n "$ci_test_cmd" ]; then
|
|
1085
|
+
vars[TEST_FULL_CMD]="$ci_test_cmd"
|
|
1086
|
+
else
|
|
1087
|
+
vars[TEST_FULL_CMD]="${test_cmd:-make test}"
|
|
1088
|
+
fi
|
|
1089
|
+
|
|
1090
|
+
# Check if docs exist
|
|
1091
|
+
[ -d "./docs" ] && vars[ARCHITECTURE_DOC]="./docs/architecture.md" || vars[ARCHITECTURE_DOC]="(not available)"
|
|
1092
|
+
[ -d "./docs" ] && vars[API_DOC]="./docs/api.md" || vars[API_DOC]="(not available)"
|
|
1093
|
+
|
|
1094
|
+
# Use extracted contributing file path or default
|
|
1095
|
+
contrib_file=$(echo "$DOCS_INFO" | jq -r '.contributing.file // empty')
|
|
1096
|
+
if [ -n "$contrib_file" ] && [ "$contrib_file" != "null" ]; then
|
|
1097
|
+
vars[CONTRIBUTING_DOC]="./$contrib_file"
|
|
1098
|
+
else
|
|
1099
|
+
vars[CONTRIBUTING_DOC]="./CONTRIBUTING.md"
|
|
1100
|
+
fi
|
|
1101
|
+
fi
|
|
1102
|
+
|
|
1103
|
+
# Language-specific conflict resolution, never-do rules, and code examples
|
|
1104
|
+
case "$LANGUAGE" in
|
|
1105
|
+
"go")
|
|
1106
|
+
vars[LANGUAGE_SPECIFIC_CONFLICT_RESOLUTION]="- For Go-specific patterns, defer to language idioms and standard library conventions"
|
|
1107
|
+
vars[LANGUAGE_SPECIFIC_NEVER]="- Commit go.sum without go.mod changes"
|
|
1108
|
+
vars[CODE_EXAMPLES]="**Good:** \`if err != nil { return fmt.Errorf(\"op failed: %w\", err) }\`
|
|
1109
|
+
**Avoid:** \`if err != nil { panic(err) }\` or ignoring errors"
|
|
1110
|
+
vars[GOOD_EXAMPLE]="\`\`\`go
|
|
1111
|
+
// Wrap errors with context
|
|
1112
|
+
if err != nil {
|
|
1113
|
+
return fmt.Errorf(\"failed to process %s: %w\", item, err)
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Use structured logging
|
|
1117
|
+
slog.Info(\"operation completed\", \"item\", item, \"duration\", elapsed)
|
|
1118
|
+
\`\`\`"
|
|
1119
|
+
vars[BAD_EXAMPLE]="\`\`\`go
|
|
1120
|
+
// Don't panic on recoverable errors
|
|
1121
|
+
if err != nil {
|
|
1122
|
+
panic(err) // Use return instead
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// Don't use fmt.Println for logging
|
|
1126
|
+
fmt.Println(\"something happened\") // Use slog/log package
|
|
1127
|
+
\`\`\`"
|
|
1128
|
+
;;
|
|
1129
|
+
"php")
|
|
1130
|
+
vars[LANGUAGE_SPECIFIC_CONFLICT_RESOLUTION]="- For PHP-specific patterns, follow PSR standards"
|
|
1131
|
+
vars[LANGUAGE_SPECIFIC_NEVER]="- Commit composer.lock without composer.json changes
|
|
1132
|
+
- Modify core framework files"
|
|
1133
|
+
vars[CODE_EXAMPLES]="**Good:** Constructor injection, typed properties, return types
|
|
1134
|
+
**Avoid:** Service locator, untyped parameters, \`@var\` without types"
|
|
1135
|
+
vars[GOOD_EXAMPLE]="\`\`\`php
|
|
1136
|
+
// Use constructor injection with typed properties
|
|
1137
|
+
public function __construct(
|
|
1138
|
+
private readonly UserRepository \$userRepository,
|
|
1139
|
+
private readonly LoggerInterface \$logger,
|
|
1140
|
+
) {}
|
|
1141
|
+
|
|
1142
|
+
// Always use return types and parameter types
|
|
1143
|
+
public function findById(int \$id): ?User
|
|
1144
|
+
{
|
|
1145
|
+
return \$this->userRepository->find(\$id);
|
|
1146
|
+
}
|
|
1147
|
+
\`\`\`"
|
|
1148
|
+
vars[BAD_EXAMPLE]="\`\`\`php
|
|
1149
|
+
// Don't use service locator or globals
|
|
1150
|
+
\$user = Container::get('user.repository')->find(\$id);
|
|
1151
|
+
|
|
1152
|
+
// Don't omit types
|
|
1153
|
+
public function process(\$data) // Missing types
|
|
1154
|
+
{
|
|
1155
|
+
return \$data; // Missing return type
|
|
1156
|
+
}
|
|
1157
|
+
\`\`\`"
|
|
1158
|
+
;;
|
|
1159
|
+
"typescript")
|
|
1160
|
+
vars[LANGUAGE_SPECIFIC_CONFLICT_RESOLUTION]="- For TypeScript/JavaScript patterns, follow project eslint/prettier config"
|
|
1161
|
+
vars[LANGUAGE_SPECIFIC_NEVER]="- Commit package-lock.json without package.json changes
|
|
1162
|
+
- Use any type without justification"
|
|
1163
|
+
vars[CODE_EXAMPLES]="**Good:** Strict types, async/await, destructuring
|
|
1164
|
+
**Avoid:** \`any\` type, callback hell, mutable state in components"
|
|
1165
|
+
vars[GOOD_EXAMPLE]="\`\`\`typescript
|
|
1166
|
+
// Use explicit types and async/await
|
|
1167
|
+
async function fetchUser(id: string): Promise<User | null> {
|
|
1168
|
+
const response = await api.get<User>(\\\`/users/\\\${id}\\\`);
|
|
1169
|
+
return response.data;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// Use destructuring and const
|
|
1173
|
+
const { name, email } = user;
|
|
1174
|
+
\`\`\`"
|
|
1175
|
+
vars[BAD_EXAMPLE]="\`\`\`typescript
|
|
1176
|
+
// Don't use 'any' without justification
|
|
1177
|
+
function process(data: any): any { // Type properly
|
|
1178
|
+
return data;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Don't use var or nested callbacks
|
|
1182
|
+
var result; // Use const/let
|
|
1183
|
+
fetchData(function(data) { // Use async/await
|
|
1184
|
+
processData(data, function(result) { ... });
|
|
1185
|
+
});
|
|
1186
|
+
\`\`\`"
|
|
1187
|
+
;;
|
|
1188
|
+
"python")
|
|
1189
|
+
vars[LANGUAGE_SPECIFIC_CONFLICT_RESOLUTION]="- For Python-specific patterns, follow PEP 8 and project tooling (ruff/black)"
|
|
1190
|
+
vars[LANGUAGE_SPECIFIC_NEVER]="- Commit requirements.txt without pyproject.toml changes
|
|
1191
|
+
- Use print() for logging in production code"
|
|
1192
|
+
vars[CODE_EXAMPLES]="**Good:** Type hints, dataclasses, context managers
|
|
1193
|
+
**Avoid:** Bare \`except:\`, mutable default args, \`print()\` for logging"
|
|
1194
|
+
vars[GOOD_EXAMPLE]="\`\`\`python
|
|
1195
|
+
# Use type hints and dataclasses
|
|
1196
|
+
from dataclasses import dataclass
|
|
1197
|
+
|
|
1198
|
+
@dataclass
|
|
1199
|
+
class User:
|
|
1200
|
+
name: str
|
|
1201
|
+
email: str
|
|
1202
|
+
|
|
1203
|
+
def find_user(user_id: int) -> User | None:
|
|
1204
|
+
\"\"\"Find user by ID.\"\"\"
|
|
1205
|
+
return db.query(User).filter_by(id=user_id).first()
|
|
1206
|
+
\`\`\`"
|
|
1207
|
+
vars[BAD_EXAMPLE]="\`\`\`python
|
|
1208
|
+
# Don't use bare except or mutable defaults
|
|
1209
|
+
def process(items=[]): # Mutable default arg!
|
|
1210
|
+
try:
|
|
1211
|
+
return do_something(items)
|
|
1212
|
+
except: # Too broad, catches KeyboardInterrupt
|
|
1213
|
+
pass
|
|
1214
|
+
|
|
1215
|
+
# Don't use print() for logging
|
|
1216
|
+
print(f\"Processing {item}\") # Use logging module
|
|
1217
|
+
\`\`\`"
|
|
1218
|
+
;;
|
|
1219
|
+
*)
|
|
1220
|
+
vars[LANGUAGE_SPECIFIC_CONFLICT_RESOLUTION]=""
|
|
1221
|
+
vars[LANGUAGE_SPECIFIC_NEVER]=""
|
|
1222
|
+
vars[CODE_EXAMPLES]=""
|
|
1223
|
+
vars[GOOD_EXAMPLE]="TODO: Add language-specific good patterns"
|
|
1224
|
+
vars[BAD_EXAMPLE]="TODO: Add language-specific anti-patterns"
|
|
1225
|
+
;;
|
|
1226
|
+
esac
|
|
1227
|
+
|
|
1228
|
+
# Render template (smart mode respects --update flag)
|
|
1229
|
+
render_template_smart "$TEMPLATE" "$ROOT_FILE" vars "$UPDATE_ONLY"
|
|
1230
|
+
|
|
1231
|
+
# Enforce byte budget for agent instruction limits
|
|
1232
|
+
enforce_byte_budget "$ROOT_FILE" "$BYTE_BUDGET"
|
|
1233
|
+
|
|
1234
|
+
if [ "$UPDATE_ONLY" = true ]; then
|
|
1235
|
+
echo "✅ Updated: $ROOT_FILE"
|
|
1236
|
+
else
|
|
1237
|
+
echo "✅ Created: $ROOT_FILE"
|
|
1238
|
+
fi
|
|
1239
|
+
fi
|
|
1240
|
+
|
|
1241
|
+
# Generate CLAUDE.md shim if requested
|
|
1242
|
+
if [ "$CLAUDE_SHIM" = true ]; then
|
|
1243
|
+
CLAUDE_FILE="$PROJECT_DIR/CLAUDE.md"
|
|
1244
|
+
if [ -f "$CLAUDE_FILE" ] && [ "$FORCE" = false ]; then
|
|
1245
|
+
log "CLAUDE.md already exists, skipping (use --force to regenerate)"
|
|
1246
|
+
elif [ "$DRY_RUN" = true ]; then
|
|
1247
|
+
echo "[DRY-RUN] Would create: $CLAUDE_FILE"
|
|
1248
|
+
else
|
|
1249
|
+
cat > "$CLAUDE_FILE" << 'CLAUDESHIM'
|
|
1250
|
+
<!-- Auto-generated shim for Claude Code compatibility -->
|
|
1251
|
+
<!-- Source of truth: AGENTS.md -->
|
|
1252
|
+
<!-- Re-generate with: generate-agents.sh --claude-shim -->
|
|
1253
|
+
|
|
1254
|
+
@import AGENTS.md
|
|
1255
|
+
|
|
1256
|
+
<!-- Add Claude-specific overrides below if needed -->
|
|
1257
|
+
CLAUDESHIM
|
|
1258
|
+
echo "✅ Created: $CLAUDE_FILE (shim importing AGENTS.md)"
|
|
1259
|
+
fi
|
|
1260
|
+
fi
|
|
1261
|
+
|
|
1262
|
+
# Auto-detect Claude Code environment and ensure CLAUDE.md symlinks exist
|
|
1263
|
+
|
|
1264
|
+
# Create compatibility symlinks if requested (root level)
|
|
1265
|
+
# Subdirectory symlinks are created after scoped files are generated (see below).
|
|
1266
|
+
if [ "$CREATE_SYMLINKS" = true ] && [ "$CLAUDE_SHIM" = false ]; then
|
|
1267
|
+
for symlink_name in CLAUDE.md GEMINI.md; do
|
|
1268
|
+
SYMLINK_FILE="$PROJECT_DIR/$symlink_name"
|
|
1269
|
+
if [ -L "$SYMLINK_FILE" ] || [ ! -e "$SYMLINK_FILE" ]; then
|
|
1270
|
+
if [ "$DRY_RUN" = true ]; then
|
|
1271
|
+
echo "[DRY-RUN] Would symlink: $SYMLINK_FILE → AGENTS.md"
|
|
1272
|
+
else
|
|
1273
|
+
ln -sf AGENTS.md "$SYMLINK_FILE"
|
|
1274
|
+
echo "✅ Symlinked: $SYMLINK_FILE → AGENTS.md"
|
|
1275
|
+
fi
|
|
1276
|
+
else
|
|
1277
|
+
log "$symlink_name already exists (not a symlink), skipping (use --force to replace)"
|
|
1278
|
+
if [ "$FORCE" = true ]; then
|
|
1279
|
+
rm -f "$SYMLINK_FILE"
|
|
1280
|
+
ln -s AGENTS.md "$SYMLINK_FILE"
|
|
1281
|
+
echo "✅ Replaced: $SYMLINK_FILE → AGENTS.md (--force)"
|
|
1282
|
+
fi
|
|
1283
|
+
fi
|
|
1284
|
+
done
|
|
1285
|
+
fi
|
|
1286
|
+
|
|
1287
|
+
# Generate scoped AGENTS.md files
|
|
1288
|
+
SCOPE_COUNT=$(echo "$SCOPES_INFO" | jq '.scopes | length')
|
|
1289
|
+
|
|
1290
|
+
if [ "$SCOPE_COUNT" -eq 0 ]; then
|
|
1291
|
+
log "No scopes detected (no directories with sufficient source files)"
|
|
1292
|
+
else
|
|
1293
|
+
log "Generating $SCOPE_COUNT scoped AGENTS.md files..."
|
|
1294
|
+
|
|
1295
|
+
while read -r scope; do
|
|
1296
|
+
SCOPE_PATH=$(echo "$scope" | jq -r '.path')
|
|
1297
|
+
SCOPE_TYPE=$(echo "$scope" | jq -r '.type')
|
|
1298
|
+
SCOPE_FILE="$PROJECT_DIR/$SCOPE_PATH/AGENTS.md"
|
|
1299
|
+
|
|
1300
|
+
if [ -f "$SCOPE_FILE" ] && [ "$FORCE" = false ] && [ "$UPDATE_ONLY" = false ]; then
|
|
1301
|
+
log "Scoped AGENTS.md already exists: $SCOPE_PATH, skipping"
|
|
1302
|
+
continue
|
|
1303
|
+
fi
|
|
1304
|
+
|
|
1305
|
+
if [ "$DRY_RUN" = true ]; then
|
|
1306
|
+
echo "[DRY-RUN] Would create/update: $SCOPE_FILE"
|
|
1307
|
+
continue
|
|
1308
|
+
fi
|
|
1309
|
+
|
|
1310
|
+
# Select template based on scope type
|
|
1311
|
+
SCOPE_TEMPLATE="$TEMPLATE_DIR/scoped/$SCOPE_TYPE.md"
|
|
1312
|
+
|
|
1313
|
+
if [ ! -f "$SCOPE_TEMPLATE" ]; then
|
|
1314
|
+
log "No template for scope type: $SCOPE_TYPE, skipping $SCOPE_PATH"
|
|
1315
|
+
continue
|
|
1316
|
+
fi
|
|
1317
|
+
|
|
1318
|
+
# Try to extract commands from scope directory (for monorepos)
|
|
1319
|
+
SCOPE_COMMANDS=""
|
|
1320
|
+
if [ -f "$SCOPE_PATH/package.json" ] || [ -f "$SCOPE_PATH/composer.json" ] || [ -f "$SCOPE_PATH/pyproject.toml" ] || [ -f "$SCOPE_PATH/go.mod" ]; then
|
|
1321
|
+
SCOPE_COMMANDS=$("$SCRIPT_DIR/extract-commands.sh" "$SCOPE_PATH" 2>/dev/null || echo '')
|
|
1322
|
+
log "Found scope-local config in $SCOPE_PATH"
|
|
1323
|
+
fi
|
|
1324
|
+
# Fall back to root commands if scope has no local config
|
|
1325
|
+
[ -z "$SCOPE_COMMANDS" ] && SCOPE_COMMANDS="$COMMANDS"
|
|
1326
|
+
|
|
1327
|
+
# Prepare scoped template variables
|
|
1328
|
+
declare -A scope_vars
|
|
1329
|
+
scope_vars[TIMESTAMP]=$(get_timestamp)
|
|
1330
|
+
scope_vars[SCOPE_NAME]=$(basename "$SCOPE_PATH")
|
|
1331
|
+
scope_vars[SCOPE_DESCRIPTION]=$(get_scope_description "$SCOPE_TYPE")
|
|
1332
|
+
scope_vars[FILE_PATH]="<file>"
|
|
1333
|
+
scope_vars[HOUSE_RULES]=""
|
|
1334
|
+
|
|
1335
|
+
# Helper to only set var if value is non-empty (leaves placeholder for row deletion)
|
|
1336
|
+
set_scope_if_present() {
|
|
1337
|
+
local key="$1"
|
|
1338
|
+
local value="$2"
|
|
1339
|
+
if [ -n "$value" ] && [ "$value" != "null" ]; then
|
|
1340
|
+
scope_vars[$key]="$value"
|
|
1341
|
+
fi
|
|
1342
|
+
return 0
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
# Generate scope-specific file map
|
|
1346
|
+
# Usage: generate_scope_file_map <path> <ext1> [ext2] [ext3]...
|
|
1347
|
+
generate_scope_file_map() {
|
|
1348
|
+
local scope_path="$1"
|
|
1349
|
+
shift # Remove first arg, leaving extensions
|
|
1350
|
+
local extensions=("$@")
|
|
1351
|
+
local result=""
|
|
1352
|
+
|
|
1353
|
+
# Build find pattern for multiple extensions
|
|
1354
|
+
local find_args=()
|
|
1355
|
+
for ext in "${extensions[@]}"; do
|
|
1356
|
+
if [[ ${#find_args[@]} -gt 0 ]]; then
|
|
1357
|
+
find_args+=("-o")
|
|
1358
|
+
fi
|
|
1359
|
+
find_args+=("-name" "*.$ext")
|
|
1360
|
+
done
|
|
1361
|
+
|
|
1362
|
+
# Find key files (most recently modified, largest, or entry points)
|
|
1363
|
+
local files
|
|
1364
|
+
files=$(find "$scope_path" -maxdepth 2 -type f \( "${find_args[@]}" \) 2>/dev/null | head -5)
|
|
1365
|
+
|
|
1366
|
+
if [ -n "$files" ]; then
|
|
1367
|
+
result="| File | Purpose |\n|------|---------|"
|
|
1368
|
+
while IFS= read -r file; do
|
|
1369
|
+
local rel_path="${file#"$PROJECT_DIR"/}"
|
|
1370
|
+
# Try to extract purpose from first docblock or comment
|
|
1371
|
+
local purpose
|
|
1372
|
+
purpose=$(head -20 "$file" 2>/dev/null | grep -E '^\s*(//|#|\*|/\*\*)' | head -1 | sed 's/^[[:space:]]*[/*#]*[[:space:]]*//' | cut -c1-50)
|
|
1373
|
+
[ -z "$purpose" ] && purpose="(add description)"
|
|
1374
|
+
result="$result\n| \`$rel_path\` | $purpose |"
|
|
1375
|
+
done <<< "$files"
|
|
1376
|
+
fi
|
|
1377
|
+
echo -e "$result"
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
# Generate scope-specific golden samples
|
|
1381
|
+
# Usage: generate_scope_golden_samples <path> <ext1> [ext2] [ext3]...
|
|
1382
|
+
generate_scope_golden_samples() {
|
|
1383
|
+
local scope_path="$1"
|
|
1384
|
+
shift # Remove first arg, leaving extensions
|
|
1385
|
+
local extensions=("$@")
|
|
1386
|
+
local result=""
|
|
1387
|
+
|
|
1388
|
+
# Build find pattern for multiple extensions
|
|
1389
|
+
local find_args=()
|
|
1390
|
+
for ext in "${extensions[@]}"; do
|
|
1391
|
+
if [[ ${#find_args[@]} -gt 0 ]]; then
|
|
1392
|
+
find_args+=("-o")
|
|
1393
|
+
fi
|
|
1394
|
+
find_args+=("-name" "*.$ext")
|
|
1395
|
+
done
|
|
1396
|
+
|
|
1397
|
+
# Look for well-documented files with tests
|
|
1398
|
+
local sample
|
|
1399
|
+
# shellcheck disable=SC2038 # Source files rarely have special chars
|
|
1400
|
+
sample=$(find "$scope_path" -maxdepth 2 -type f \( "${find_args[@]}" \) 2>/dev/null | \
|
|
1401
|
+
xargs -I{} sh -c 'wc -l "{}" | grep -v "^0"' 2>/dev/null | \
|
|
1402
|
+
sort -rn | head -1 | awk '{print $2}')
|
|
1403
|
+
|
|
1404
|
+
if [ -n "$sample" ] && [ -f "$sample" ]; then
|
|
1405
|
+
local rel_path="${sample#"$PROJECT_DIR"/}"
|
|
1406
|
+
result="| Pattern | Reference |\n|---------|-----------|"
|
|
1407
|
+
result="$result\n| Standard implementation | \`$rel_path\` |"
|
|
1408
|
+
fi
|
|
1409
|
+
echo -e "$result"
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
# Language-specific variables
|
|
1413
|
+
case "$SCOPE_TYPE" in
|
|
1414
|
+
"backend-go")
|
|
1415
|
+
scope_vars[GO_VERSION]="$VERSION"
|
|
1416
|
+
scope_vars[GO_MINOR_VERSION]=$(echo "$VERSION" | cut -d. -f2)
|
|
1417
|
+
scope_vars[GO_TOOLS]="golangci-lint, gofmt"
|
|
1418
|
+
scope_vars[ENV_VARS]="See .env.example"
|
|
1419
|
+
set_scope_if_present BUILD_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.build // empty')"
|
|
1420
|
+
|
|
1421
|
+
# Build whole-line placeholders for setup section
|
|
1422
|
+
scope_vars[INSTALL_LINE]="- Install: \`go mod download\`"
|
|
1423
|
+
[ -n "${scope_vars[GO_VERSION]:-}" ] && [ "${scope_vars[GO_VERSION]}" != "unknown" ] && \
|
|
1424
|
+
scope_vars[GO_VERSION_LINE]="- Go version: ${scope_vars[GO_VERSION]}"
|
|
1425
|
+
[ -n "${scope_vars[GO_TOOLS]:-}" ] && \
|
|
1426
|
+
scope_vars[GO_TOOLS_LINE]="- Required tools: ${scope_vars[GO_TOOLS]}"
|
|
1427
|
+
[ -n "${scope_vars[ENV_VARS]:-}" ] && \
|
|
1428
|
+
scope_vars[ENV_VARS_LINE]="- Environment variables: ${scope_vars[ENV_VARS]}"
|
|
1429
|
+
|
|
1430
|
+
# Build whole-line placeholders for commands section
|
|
1431
|
+
scope_vars[VET_LINE]="- Vet (static analysis): \`go vet ./...\`"
|
|
1432
|
+
scope_vars[FORMAT_LINE]="- Format: \`gofmt -w .\`"
|
|
1433
|
+
scope_vars[LINT_LINE]="- Lint: \`golangci-lint run ./...\`"
|
|
1434
|
+
scope_vars[TEST_LINE]="- Test: \`go test -v -race ./...\`"
|
|
1435
|
+
scope_vars[TEST_SINGLE_LINE]="- Test specific: \`go test -v -race -run TestName ./...\`"
|
|
1436
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
1437
|
+
scope_vars[BUILD_LINE]="- Build: \`${scope_vars[BUILD_CMD]}\`"
|
|
1438
|
+
|
|
1439
|
+
# Build checklist lines
|
|
1440
|
+
scope_vars[TEST_CHECKLIST_LINE]="- [ ] Tests pass: \`go test -v -race ./...\`"
|
|
1441
|
+
scope_vars[LINT_CHECKLIST_LINE]="- [ ] Lint clean: \`golangci-lint run ./...\`"
|
|
1442
|
+
scope_vars[FORMAT_CHECKLIST_LINE]="- [ ] Formatted: \`gofmt -w .\`"
|
|
1443
|
+
|
|
1444
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "go")
|
|
1445
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "go")
|
|
1446
|
+
;;
|
|
1447
|
+
|
|
1448
|
+
"backend-php")
|
|
1449
|
+
scope_vars[PHP_VERSION]="$VERSION"
|
|
1450
|
+
FRAMEWORK=$(echo "$PROJECT_INFO" | jq -r '.framework')
|
|
1451
|
+
scope_vars[FRAMEWORK]="$FRAMEWORK"
|
|
1452
|
+
scope_vars[PHP_EXTENSIONS]="json, mbstring, xml"
|
|
1453
|
+
scope_vars[ENV_VARS]="See .env.example"
|
|
1454
|
+
scope_vars[PHPSTAN_LEVEL]="10"
|
|
1455
|
+
set_scope_if_present BUILD_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.build // empty')"
|
|
1456
|
+
|
|
1457
|
+
if [ "$FRAMEWORK" = "typo3" ]; then
|
|
1458
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- TYPO3-specific: Use dependency injection, follow TYPO3 CGL"
|
|
1459
|
+
scope_vars[FRAMEWORK_DOCS_LINE]="- TYPO3 documentation: https://docs.typo3.org"
|
|
1460
|
+
else
|
|
1461
|
+
scope_vars[FRAMEWORK_CONVENTIONS]=""
|
|
1462
|
+
scope_vars[FRAMEWORK_DOCS_LINE]=""
|
|
1463
|
+
fi
|
|
1464
|
+
|
|
1465
|
+
# Build whole-line placeholders for setup section
|
|
1466
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer install\`"
|
|
1467
|
+
[ -n "${scope_vars[PHP_VERSION]:-}" ] && [ "${scope_vars[PHP_VERSION]}" != "unknown" ] && \
|
|
1468
|
+
scope_vars[PHP_VERSION_LINE]="- PHP version: ${scope_vars[PHP_VERSION]}"
|
|
1469
|
+
[ -n "${scope_vars[FRAMEWORK]:-}" ] && [ "${scope_vars[FRAMEWORK]}" != "none" ] && \
|
|
1470
|
+
scope_vars[FRAMEWORK_LINE]="- Framework: ${scope_vars[FRAMEWORK]}"
|
|
1471
|
+
[ -n "${scope_vars[PHP_EXTENSIONS]:-}" ] && \
|
|
1472
|
+
scope_vars[PHP_EXTENSIONS_LINE]="- Required extensions: ${scope_vars[PHP_EXTENSIONS]}"
|
|
1473
|
+
[ -n "${scope_vars[ENV_VARS]:-}" ] && \
|
|
1474
|
+
scope_vars[ENV_VARS_LINE]="- Environment variables: ${scope_vars[ENV_VARS]}"
|
|
1475
|
+
|
|
1476
|
+
# Build whole-line placeholders for commands section
|
|
1477
|
+
scope_vars[TYPECHECK_LINE]="- Typecheck: \`vendor/bin/phpstan analyze --level=${scope_vars[PHPSTAN_LEVEL]}\`"
|
|
1478
|
+
scope_vars[FORMAT_LINE]="- Format: \`vendor/bin/php-cs-fixer fix\`"
|
|
1479
|
+
scope_vars[LINT_LINE]="- Lint: \`php -l\`"
|
|
1480
|
+
scope_vars[TEST_LINE]="- Test: \`vendor/bin/phpunit\`"
|
|
1481
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
1482
|
+
scope_vars[BUILD_LINE]="- Build: \`${scope_vars[BUILD_CMD]}\`"
|
|
1483
|
+
|
|
1484
|
+
# Build checklist lines
|
|
1485
|
+
scope_vars[TEST_CHECKLIST_LINE]="- [ ] Tests pass: \`vendor/bin/phpunit\`"
|
|
1486
|
+
scope_vars[TYPECHECK_CHECKLIST_LINE]="- [ ] PHPStan Level ${scope_vars[PHPSTAN_LEVEL]} clean: \`vendor/bin/phpstan analyze\`"
|
|
1487
|
+
scope_vars[FORMAT_CHECKLIST_LINE]="- [ ] PSR-12 compliant: \`vendor/bin/php-cs-fixer fix --dry-run\`"
|
|
1488
|
+
|
|
1489
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "php")
|
|
1490
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "php")
|
|
1491
|
+
;;
|
|
1492
|
+
|
|
1493
|
+
"typo3-extension")
|
|
1494
|
+
scope_vars[PHP_VERSION]="$VERSION"
|
|
1495
|
+
TYPO3_VERSION=$(jq -r '.require."typo3/cms-core" // .["require-dev"]."typo3/cms-core" // "^12.4 || ^13.4"' composer.json 2>/dev/null || echo "^12.4 || ^13.4")
|
|
1496
|
+
scope_vars[TYPO3_VERSION]="$TYPO3_VERSION"
|
|
1497
|
+
scope_vars[PHPSTAN_LEVEL]="10"
|
|
1498
|
+
|
|
1499
|
+
# Extract extension key and vendor from composer.json
|
|
1500
|
+
EXT_KEY=$(jq -r '.extra."typo3/cms"."extension-key" // empty' composer.json 2>/dev/null || echo "")
|
|
1501
|
+
if [ -z "$EXT_KEY" ]; then
|
|
1502
|
+
EXT_KEY=$(basename "$PROJECT_DIR" | tr '-' '_')
|
|
1503
|
+
fi
|
|
1504
|
+
scope_vars[EXT_KEY]="$EXT_KEY"
|
|
1505
|
+
|
|
1506
|
+
VENDOR=$(jq -r '.name // empty' composer.json 2>/dev/null | cut -d'/' -f1 || echo "Vendor")
|
|
1507
|
+
scope_vars[VENDOR]="$VENDOR"
|
|
1508
|
+
scope_vars[REQUIRED_EXTENSIONS]="See ext_emconf.php"
|
|
1509
|
+
scope_vars[HOUSE_RULES]=""
|
|
1510
|
+
|
|
1511
|
+
# Build whole-line placeholders for setup section
|
|
1512
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer install\` or via Extension Manager"
|
|
1513
|
+
[ -n "${scope_vars[PHP_VERSION]:-}" ] && [ "${scope_vars[PHP_VERSION]}" != "unknown" ] && \
|
|
1514
|
+
scope_vars[PHP_VERSION_LINE]="- PHP version: ${scope_vars[PHP_VERSION]}"
|
|
1515
|
+
[ -n "${scope_vars[TYPO3_VERSION]:-}" ] && \
|
|
1516
|
+
scope_vars[TYPO3_VERSION_LINE]="- TYPO3 version: ${scope_vars[TYPO3_VERSION]}"
|
|
1517
|
+
scope_vars[DEV_SETUP_LINE]="- Local dev: \`ddev start && ddev composer install\`"
|
|
1518
|
+
[ -n "${scope_vars[REQUIRED_EXTENSIONS]:-}" ] && \
|
|
1519
|
+
scope_vars[REQUIRED_EXTENSIONS_LINE]="- Required extensions: ${scope_vars[REQUIRED_EXTENSIONS]}"
|
|
1520
|
+
|
|
1521
|
+
# Build commands table
|
|
1522
|
+
scope_vars[COMMANDS_TABLE]="| Task | Command |
|
|
1523
|
+
|------|---------|
|
|
1524
|
+
| Lint | \`composer ci:php:lint\` |
|
|
1525
|
+
| CS Fix | \`composer ci:php:cs-fixer\` |
|
|
1526
|
+
| PHPStan | \`composer ci:php:stan\` |
|
|
1527
|
+
| Unit tests | \`composer ci:tests:unit\` |
|
|
1528
|
+
| Functional | \`composer ci:tests:functional\` |
|
|
1529
|
+
| All CI | \`composer ci\` |"
|
|
1530
|
+
|
|
1531
|
+
scope_vars[DDEV_ALTERNATIVE]="Alternative with ddev:
|
|
1532
|
+
- \`ddev composer ci:tests:unit\`
|
|
1533
|
+
- \`ddev exec vendor/bin/phpunit -c Tests/Build/UnitTests.xml\`"
|
|
1534
|
+
|
|
1535
|
+
# Build checklist lines
|
|
1536
|
+
scope_vars[CI_CHECKLIST_LINE]="- [ ] \`composer ci\` passes (lint, cs-fixer, phpstan, tests)"
|
|
1537
|
+
scope_vars[PHPSTAN_CHECKLIST_LINE]="- [ ] PHPStan level ${scope_vars[PHPSTAN_LEVEL]} clean"
|
|
1538
|
+
scope_vars[TYPO3_VERSION_CHECKLIST_LINE]="- [ ] Tested on target TYPO3 versions (${scope_vars[TYPO3_VERSION]})"
|
|
1539
|
+
|
|
1540
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "php")
|
|
1541
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "php")
|
|
1542
|
+
;;
|
|
1543
|
+
|
|
1544
|
+
"typo3-project")
|
|
1545
|
+
scope_vars[PHP_VERSION]="$VERSION"
|
|
1546
|
+
TYPO3_VERSION=$(jq -r '.require."typo3/cms-core" // "^12.4 || ^13.4"' composer.json 2>/dev/null || echo "^12.4 || ^13.4")
|
|
1547
|
+
scope_vars[TYPO3_VERSION]="$TYPO3_VERSION"
|
|
1548
|
+
scope_vars[HOUSE_RULES]=""
|
|
1549
|
+
|
|
1550
|
+
# Build whole-line placeholders for setup section
|
|
1551
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer install\`"
|
|
1552
|
+
[ -n "${scope_vars[PHP_VERSION]:-}" ] && [ "${scope_vars[PHP_VERSION]}" != "unknown" ] && \
|
|
1553
|
+
scope_vars[PHP_VERSION_LINE]="- PHP version: ${scope_vars[PHP_VERSION]}"
|
|
1554
|
+
[ -n "${scope_vars[TYPO3_VERSION]:-}" ] && \
|
|
1555
|
+
scope_vars[TYPO3_VERSION_LINE]="- TYPO3 version: ${scope_vars[TYPO3_VERSION]}"
|
|
1556
|
+
scope_vars[DEV_SETUP_LINE]="- Local dev: \`ddev start && ddev composer install\`"
|
|
1557
|
+
|
|
1558
|
+
# Detect composer mode
|
|
1559
|
+
if [ -f "public/typo3" ] || [ -d "public/typo3" ]; then
|
|
1560
|
+
scope_vars[COMPOSER_MODE_LINE]="- Composer mode: enabled (public/ web root)"
|
|
1561
|
+
fi
|
|
1562
|
+
|
|
1563
|
+
# Build commands table
|
|
1564
|
+
scope_vars[COMMANDS_TABLE]="| Task | Command |
|
|
1565
|
+
|------|---------|
|
|
1566
|
+
| Clear cache | \`vendor/bin/typo3 cache:flush\` |
|
|
1567
|
+
| Warmup cache | \`vendor/bin/typo3 cache:warmup\` |
|
|
1568
|
+
| Update DB | \`vendor/bin/typo3 database:updateschema\` |
|
|
1569
|
+
| List commands | \`vendor/bin/typo3 list\` |"
|
|
1570
|
+
|
|
1571
|
+
# Build checklist lines
|
|
1572
|
+
scope_vars[CI_CHECKLIST_LINE]="- [ ] Site configuration valid"
|
|
1573
|
+
scope_vars[TYPO3_VERSION_CHECKLIST_LINE]="- [ ] Tested on TYPO3 ${scope_vars[TYPO3_VERSION]}"
|
|
1574
|
+
|
|
1575
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "php")
|
|
1576
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "php")
|
|
1577
|
+
;;
|
|
1578
|
+
|
|
1579
|
+
"oro-bundle")
|
|
1580
|
+
scope_vars[PHP_VERSION]="$VERSION"
|
|
1581
|
+
ORO_VERSION=$(jq -r '.require."oro/platform" // .require."oro/commerce" // .require."oro/crm" // "^6.0"' composer.json 2>/dev/null || echo "^6.0")
|
|
1582
|
+
scope_vars[ORO_VERSION]="$ORO_VERSION"
|
|
1583
|
+
scope_vars[PHPSTAN_LEVEL]="8"
|
|
1584
|
+
scope_vars[HOUSE_RULES]=""
|
|
1585
|
+
|
|
1586
|
+
# Build whole-line placeholders for setup section
|
|
1587
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer install\`"
|
|
1588
|
+
[ -n "${scope_vars[PHP_VERSION]:-}" ] && [ "${scope_vars[PHP_VERSION]}" != "unknown" ] && \
|
|
1589
|
+
scope_vars[PHP_VERSION_LINE]="- PHP version: ${scope_vars[PHP_VERSION]}"
|
|
1590
|
+
[ -n "${scope_vars[ORO_VERSION]:-}" ] && \
|
|
1591
|
+
scope_vars[ORO_VERSION_LINE]="- Oro version: ${scope_vars[ORO_VERSION]}"
|
|
1592
|
+
scope_vars[DATABASE_LINE]="- Database: PostgreSQL (recommended) or MySQL"
|
|
1593
|
+
scope_vars[SETUP_COMMANDS]="- Required: \`bin/console oro:install\` for fresh setup
|
|
1594
|
+
- Cache clear: \`bin/console cache:clear\`
|
|
1595
|
+
- Assets: \`bin/console oro:assets:install\`"
|
|
1596
|
+
|
|
1597
|
+
# Build commands table
|
|
1598
|
+
scope_vars[COMMANDS_TABLE]="| Task | Command |
|
|
1599
|
+
|------|---------|
|
|
1600
|
+
| Lint | \`bin/console lint:yaml src/\` |
|
|
1601
|
+
| CS Fix | \`vendor/bin/php-cs-fixer fix\` |
|
|
1602
|
+
| PHPStan | \`vendor/bin/phpstan analyse\` |
|
|
1603
|
+
| Unit tests | \`vendor/bin/phpunit --testsuite=unit\` |
|
|
1604
|
+
| Functional | \`vendor/bin/phpunit --testsuite=functional\` |
|
|
1605
|
+
| Behat | \`vendor/bin/behat\` |
|
|
1606
|
+
| Full CI | \`bin/console oro:test:all\` |"
|
|
1607
|
+
|
|
1608
|
+
# Build checklist lines
|
|
1609
|
+
scope_vars[CACHE_CHECKLIST_LINE]="- [ ] \`bin/console cache:clear\` runs without errors"
|
|
1610
|
+
scope_vars[PHPSTAN_CHECKLIST_LINE]="- [ ] PHPStan passes at configured level"
|
|
1611
|
+
scope_vars[UNIT_TEST_CHECKLIST_LINE]="- [ ] Unit tests pass: \`vendor/bin/phpunit --testsuite=unit\`"
|
|
1612
|
+
|
|
1613
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "php")
|
|
1614
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "php")
|
|
1615
|
+
;;
|
|
1616
|
+
|
|
1617
|
+
"oro-project")
|
|
1618
|
+
scope_vars[PHP_VERSION]="$VERSION"
|
|
1619
|
+
ORO_VERSION=$(jq -r '.require."oro/platform" // .require."oro/commerce" // .require."oro/crm" // "^6.0"' composer.json 2>/dev/null || echo "^6.0")
|
|
1620
|
+
scope_vars[ORO_VERSION]="$ORO_VERSION"
|
|
1621
|
+
scope_vars[PHPSTAN_LEVEL]="8"
|
|
1622
|
+
scope_vars[HOUSE_RULES]=""
|
|
1623
|
+
|
|
1624
|
+
# Build whole-line placeholders for setup section
|
|
1625
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer install\`"
|
|
1626
|
+
[ -n "${scope_vars[PHP_VERSION]:-}" ] && [ "${scope_vars[PHP_VERSION]}" != "unknown" ] && \
|
|
1627
|
+
scope_vars[PHP_VERSION_LINE]="- PHP version: ${scope_vars[PHP_VERSION]}"
|
|
1628
|
+
[ -n "${scope_vars[ORO_VERSION]:-}" ] && \
|
|
1629
|
+
scope_vars[ORO_VERSION_LINE]="- Oro version: ${scope_vars[ORO_VERSION]}"
|
|
1630
|
+
scope_vars[DATABASE_LINE]="- Database: PostgreSQL (recommended) or MySQL"
|
|
1631
|
+
scope_vars[MESSAGE_QUEUE_LINE]="- Message queue: Required for background jobs"
|
|
1632
|
+
|
|
1633
|
+
# Build commands table
|
|
1634
|
+
scope_vars[COMMANDS_TABLE]="| Task | Command |
|
|
1635
|
+
|------|---------|
|
|
1636
|
+
| Install | \`bin/console oro:install\` |
|
|
1637
|
+
| Update platform | \`bin/console oro:platform:update\` |
|
|
1638
|
+
| Clear cache | \`bin/console cache:clear\` |
|
|
1639
|
+
| Install assets | \`bin/console oro:assets:install\` |
|
|
1640
|
+
| Message queue | \`bin/console oro:message-queue:consume\` |
|
|
1641
|
+
| Run cron | \`bin/console oro:cron\` |
|
|
1642
|
+
| Unit tests | \`vendor/bin/phpunit --testsuite=unit\` |"
|
|
1643
|
+
|
|
1644
|
+
# Build checklist lines
|
|
1645
|
+
scope_vars[CACHE_CHECKLIST_LINE]="- [ ] \`bin/console cache:clear\` runs without errors"
|
|
1646
|
+
scope_vars[PHPSTAN_CHECKLIST_LINE]="- [ ] PHPStan passes at configured level"
|
|
1647
|
+
scope_vars[UNIT_TEST_CHECKLIST_LINE]="- [ ] Unit tests pass"
|
|
1648
|
+
|
|
1649
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "php")
|
|
1650
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "php")
|
|
1651
|
+
;;
|
|
1652
|
+
|
|
1653
|
+
"symfony")
|
|
1654
|
+
scope_vars[PHP_VERSION]="$VERSION"
|
|
1655
|
+
SYMFONY_VERSION=$(jq -r '.require."symfony/framework-bundle" // .require."symfony/symfony" // "^7.0"' composer.json 2>/dev/null || echo "^7.0")
|
|
1656
|
+
scope_vars[SYMFONY_VERSION]="$SYMFONY_VERSION"
|
|
1657
|
+
scope_vars[PHPSTAN_LEVEL]="9"
|
|
1658
|
+
scope_vars[HOUSE_RULES]=""
|
|
1659
|
+
|
|
1660
|
+
# Build whole-line placeholders for setup section
|
|
1661
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer install\`"
|
|
1662
|
+
[ -n "${scope_vars[PHP_VERSION]:-}" ] && [ "${scope_vars[PHP_VERSION]}" != "unknown" ] && \
|
|
1663
|
+
scope_vars[PHP_VERSION_LINE]="- PHP version: ${scope_vars[PHP_VERSION]}"
|
|
1664
|
+
[ -n "${scope_vars[SYMFONY_VERSION]:-}" ] && \
|
|
1665
|
+
scope_vars[SYMFONY_VERSION_LINE]="- Symfony version: ${scope_vars[SYMFONY_VERSION]}"
|
|
1666
|
+
# Detect database from doctrine config
|
|
1667
|
+
if [ -f "config/packages/doctrine.yaml" ]; then
|
|
1668
|
+
scope_vars[DATABASE_LINE]="- Database: See config/packages/doctrine.yaml"
|
|
1669
|
+
fi
|
|
1670
|
+
|
|
1671
|
+
# Build commands table
|
|
1672
|
+
scope_vars[COMMANDS_TABLE]="| Task | Command |
|
|
1673
|
+
|------|---------|
|
|
1674
|
+
| Lint | \`bin/console lint:yaml config/\` |
|
|
1675
|
+
| CS Fix | \`vendor/bin/php-cs-fixer fix\` |
|
|
1676
|
+
| PHPStan | \`vendor/bin/phpstan analyse\` |
|
|
1677
|
+
| Unit tests | \`vendor/bin/phpunit\` |
|
|
1678
|
+
| Clear cache | \`bin/console cache:clear\` |
|
|
1679
|
+
| Debug routes | \`bin/console debug:router\` |
|
|
1680
|
+
| Debug container | \`bin/console debug:container\` |"
|
|
1681
|
+
|
|
1682
|
+
# Build checklist lines
|
|
1683
|
+
scope_vars[PHPSTAN_CHECKLIST_LINE]="- [ ] PHPStan level ${scope_vars[PHPSTAN_LEVEL]} clean"
|
|
1684
|
+
scope_vars[CS_CHECKLIST_LINE]="- [ ] Code style clean: \`vendor/bin/php-cs-fixer fix --dry-run\`"
|
|
1685
|
+
scope_vars[TEST_CHECKLIST_LINE]="- [ ] Tests pass: \`vendor/bin/phpunit\`"
|
|
1686
|
+
|
|
1687
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "php")
|
|
1688
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "php")
|
|
1689
|
+
;;
|
|
1690
|
+
|
|
1691
|
+
"backend-python")
|
|
1692
|
+
scope_vars[PYTHON_VERSION]="$VERSION"
|
|
1693
|
+
scope_vars[PACKAGE_MANAGER]=$(echo "$PROJECT_INFO" | jq -r '.package_manager')
|
|
1694
|
+
scope_vars[ENV_VARS]="See .env or .env.example"
|
|
1695
|
+
# Populate all command variables from scope-local extraction
|
|
1696
|
+
set_scope_if_present INSTALL_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.install // empty')"
|
|
1697
|
+
set_scope_if_present TYPECHECK_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.typecheck // empty')"
|
|
1698
|
+
set_scope_if_present LINT_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.lint // empty')"
|
|
1699
|
+
set_scope_if_present FORMAT_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.format // empty')"
|
|
1700
|
+
set_scope_if_present TEST_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.test // empty')"
|
|
1701
|
+
set_scope_if_present TEST_SINGLE_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.test_single // empty')"
|
|
1702
|
+
set_scope_if_present BUILD_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.build // empty')"
|
|
1703
|
+
set_scope_if_present DEV_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.dev // empty')"
|
|
1704
|
+
|
|
1705
|
+
# Build whole-line placeholders for setup section
|
|
1706
|
+
[ -n "${scope_vars[INSTALL_CMD]:-}" ] && \
|
|
1707
|
+
scope_vars[INSTALL_LINE]="- Install: \`${scope_vars[INSTALL_CMD]}\`"
|
|
1708
|
+
[ -n "${scope_vars[PYTHON_VERSION]:-}" ] && [ "${scope_vars[PYTHON_VERSION]}" != "unknown" ] && \
|
|
1709
|
+
scope_vars[PYTHON_VERSION_LINE]="- Python version: ${scope_vars[PYTHON_VERSION]}"
|
|
1710
|
+
[ -n "${scope_vars[PACKAGE_MANAGER]:-}" ] && [ "${scope_vars[PACKAGE_MANAGER]}" != "unknown" ] && \
|
|
1711
|
+
scope_vars[PACKAGE_MANAGER_LINE]="- Package manager: ${scope_vars[PACKAGE_MANAGER]}"
|
|
1712
|
+
[ -n "${scope_vars[ENV_VARS]:-}" ] && \
|
|
1713
|
+
scope_vars[ENV_VARS_LINE]="- Environment variables: ${scope_vars[ENV_VARS]}"
|
|
1714
|
+
|
|
1715
|
+
# Build whole-line placeholders for commands section
|
|
1716
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
1717
|
+
scope_vars[TYPECHECK_LINE]="- Typecheck: \`${scope_vars[TYPECHECK_CMD]}\`"
|
|
1718
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
1719
|
+
scope_vars[FORMAT_LINE]="- Format: \`${scope_vars[FORMAT_CMD]}\`"
|
|
1720
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1721
|
+
scope_vars[LINT_LINE]="- Lint: \`${scope_vars[LINT_CMD]}\`"
|
|
1722
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1723
|
+
scope_vars[TEST_LINE]="- Test: \`${scope_vars[TEST_CMD]}\`"
|
|
1724
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
1725
|
+
scope_vars[BUILD_LINE]="- Build: \`${scope_vars[BUILD_CMD]}\`"
|
|
1726
|
+
|
|
1727
|
+
# Build checklist lines
|
|
1728
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1729
|
+
scope_vars[TEST_CHECKLIST_LINE]="- [ ] Tests pass: \`${scope_vars[TEST_CMD]}\`"
|
|
1730
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
1731
|
+
scope_vars[TYPECHECK_CHECKLIST_LINE]="- [ ] Type check clean: \`${scope_vars[TYPECHECK_CMD]}\`"
|
|
1732
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1733
|
+
scope_vars[LINT_CHECKLIST_LINE]="- [ ] Lint clean: \`${scope_vars[LINT_CMD]}\`"
|
|
1734
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
1735
|
+
scope_vars[FORMAT_CHECKLIST_LINE]="- [ ] Formatted: \`${scope_vars[FORMAT_CMD]}\`"
|
|
1736
|
+
|
|
1737
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "py")
|
|
1738
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "py")
|
|
1739
|
+
;;
|
|
1740
|
+
|
|
1741
|
+
"backend-typescript")
|
|
1742
|
+
# CRITICAL: Get Node version from nearest package.json,
|
|
1743
|
+
# NOT from root PROJECT_INFO (which may be PHP/Go/Python)
|
|
1744
|
+
_node_config_root=$(find_node_config_root "$SCOPE_PATH") || _node_config_root="."
|
|
1745
|
+
|
|
1746
|
+
# Extract Node-specific metadata from correct config root
|
|
1747
|
+
_node_version=$(get_node_version "$_node_config_root") || _node_version=""
|
|
1748
|
+
scope_vars[NODE_VERSION]="$_node_version"
|
|
1749
|
+
|
|
1750
|
+
# Package manager from scope's config root
|
|
1751
|
+
scope_vars[PACKAGE_MANAGER]=$(detect_node_package_manager "$SCOPE_PATH")
|
|
1752
|
+
scope_vars[ENV_VARS]="See .env or .env.example"
|
|
1753
|
+
|
|
1754
|
+
# Extract commands using Node-specific extraction from config root
|
|
1755
|
+
_node_commands=$("$SCRIPT_DIR/extract-commands.sh" "$_node_config_root" node 2>/dev/null || echo '{}')
|
|
1756
|
+
|
|
1757
|
+
set_scope_if_present INSTALL_CMD "$(echo "$_node_commands" | jq -r '.install // empty')"
|
|
1758
|
+
set_scope_if_present TYPECHECK_CMD "$(echo "$_node_commands" | jq -r '.typecheck // empty')"
|
|
1759
|
+
set_scope_if_present LINT_CMD "$(echo "$_node_commands" | jq -r '.lint // empty')"
|
|
1760
|
+
set_scope_if_present FORMAT_CMD "$(echo "$_node_commands" | jq -r '.format // empty')"
|
|
1761
|
+
set_scope_if_present TEST_CMD "$(echo "$_node_commands" | jq -r '.test // empty')"
|
|
1762
|
+
set_scope_if_present TEST_SINGLE_CMD "$(echo "$_node_commands" | jq -r '.test_single // empty')"
|
|
1763
|
+
set_scope_if_present BUILD_CMD "$(echo "$_node_commands" | jq -r '.build // empty')"
|
|
1764
|
+
set_scope_if_present DEV_CMD "$(echo "$_node_commands" | jq -r '.dev // empty')"
|
|
1765
|
+
|
|
1766
|
+
# Build whole-line placeholders for setup section
|
|
1767
|
+
[ -n "${scope_vars[INSTALL_CMD]:-}" ] && \
|
|
1768
|
+
scope_vars[INSTALL_LINE]="- Install: \`${scope_vars[INSTALL_CMD]}\`"
|
|
1769
|
+
[ -n "${scope_vars[NODE_VERSION]:-}" ] && [ "${scope_vars[NODE_VERSION]}" != "unknown" ] && \
|
|
1770
|
+
scope_vars[NODE_VERSION_LINE]="- Node version: ${scope_vars[NODE_VERSION]}"
|
|
1771
|
+
[ -n "${scope_vars[PACKAGE_MANAGER]:-}" ] && [ "${scope_vars[PACKAGE_MANAGER]}" != "unknown" ] && \
|
|
1772
|
+
scope_vars[PACKAGE_MANAGER_LINE]="- Package manager: ${scope_vars[PACKAGE_MANAGER]}"
|
|
1773
|
+
[ -n "${scope_vars[ENV_VARS]:-}" ] && \
|
|
1774
|
+
scope_vars[ENV_VARS_LINE]="- Environment variables: ${scope_vars[ENV_VARS]}"
|
|
1775
|
+
|
|
1776
|
+
# Build whole-line placeholders for commands section
|
|
1777
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
1778
|
+
scope_vars[TYPECHECK_LINE]="- Typecheck (project-wide): \`${scope_vars[TYPECHECK_CMD]}\`"
|
|
1779
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
1780
|
+
scope_vars[FORMAT_LINE]="- Format: \`${scope_vars[FORMAT_CMD]}\`"
|
|
1781
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1782
|
+
scope_vars[LINT_LINE]="- Lint: \`${scope_vars[LINT_CMD]}\`"
|
|
1783
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1784
|
+
scope_vars[TEST_LINE]="- Test: \`${scope_vars[TEST_CMD]}\`"
|
|
1785
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
1786
|
+
scope_vars[BUILD_LINE]="- Build: \`${scope_vars[BUILD_CMD]}\`"
|
|
1787
|
+
[ -n "${scope_vars[DEV_CMD]:-}" ] && \
|
|
1788
|
+
scope_vars[DEV_LINE]="- Dev server: \`${scope_vars[DEV_CMD]}\`"
|
|
1789
|
+
|
|
1790
|
+
# Build checklist lines
|
|
1791
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1792
|
+
scope_vars[TEST_CHECKLIST_LINE]="- [ ] Tests pass: \`${scope_vars[TEST_CMD]}\`"
|
|
1793
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
1794
|
+
scope_vars[TYPECHECK_CHECKLIST_LINE]="- [ ] Type check clean: \`${scope_vars[TYPECHECK_CMD]}\`"
|
|
1795
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1796
|
+
scope_vars[LINT_CHECKLIST_LINE]="- [ ] Lint clean: \`${scope_vars[LINT_CMD]}\`"
|
|
1797
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
1798
|
+
scope_vars[FORMAT_CHECKLIST_LINE]="- [ ] Formatted: \`${scope_vars[FORMAT_CMD]}\`"
|
|
1799
|
+
|
|
1800
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "ts" "tsx")
|
|
1801
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "ts" "tsx")
|
|
1802
|
+
;;
|
|
1803
|
+
|
|
1804
|
+
"frontend-typescript")
|
|
1805
|
+
# CRITICAL: Get Node version and framework from nearest package.json,
|
|
1806
|
+
# NOT from root PROJECT_INFO (which may be PHP/Go/Python)
|
|
1807
|
+
_node_config_root=$(find_node_config_root "$SCOPE_PATH") || _node_config_root="."
|
|
1808
|
+
|
|
1809
|
+
# Extract Node-specific metadata from correct config root
|
|
1810
|
+
_node_version=$(get_node_version "$_node_config_root") || _node_version=""
|
|
1811
|
+
scope_vars[NODE_VERSION]="$_node_version"
|
|
1812
|
+
|
|
1813
|
+
_js_framework=$(get_js_framework "$_node_config_root") || _js_framework=""
|
|
1814
|
+
FRAMEWORK="$_js_framework"
|
|
1815
|
+
scope_vars[FRAMEWORK]="$FRAMEWORK"
|
|
1816
|
+
|
|
1817
|
+
# Package manager from scope's config root
|
|
1818
|
+
scope_vars[PACKAGE_MANAGER]=$(detect_node_package_manager "$SCOPE_PATH")
|
|
1819
|
+
scope_vars[ENV_VARS]="See .env.example"
|
|
1820
|
+
|
|
1821
|
+
# Extract commands using Node-specific extraction from config root
|
|
1822
|
+
_node_commands=$("$SCRIPT_DIR/extract-commands.sh" "$_node_config_root" node 2>/dev/null || echo '{}')
|
|
1823
|
+
|
|
1824
|
+
set_scope_if_present INSTALL_CMD "$(echo "$_node_commands" | jq -r '.install // empty')"
|
|
1825
|
+
set_scope_if_present TYPECHECK_CMD "$(echo "$_node_commands" | jq -r '.typecheck // empty')"
|
|
1826
|
+
set_scope_if_present LINT_CMD "$(echo "$_node_commands" | jq -r '.lint // empty')"
|
|
1827
|
+
set_scope_if_present FORMAT_CMD "$(echo "$_node_commands" | jq -r '.format // empty')"
|
|
1828
|
+
set_scope_if_present TEST_CMD "$(echo "$_node_commands" | jq -r '.test // empty')"
|
|
1829
|
+
set_scope_if_present BUILD_CMD "$(echo "$_node_commands" | jq -r '.build // empty')"
|
|
1830
|
+
set_scope_if_present DEV_CMD "$(echo "$_node_commands" | jq -r '.dev // empty')"
|
|
1831
|
+
|
|
1832
|
+
# TypeScript strict mode - check, don't assume
|
|
1833
|
+
_ts_strict=$(get_ts_strict_mode "$_node_config_root") || _ts_strict=""
|
|
1834
|
+
scope_vars[TS_STRICT]="$_ts_strict"
|
|
1835
|
+
|
|
1836
|
+
# Conditional TS_STRICT_LINE based on actual tsconfig.json
|
|
1837
|
+
if [[ "${scope_vars[TS_STRICT]}" == "true" ]]; then
|
|
1838
|
+
scope_vars[TS_STRICT_LINE]="- TypeScript strict mode enabled (verified from tsconfig.json)"
|
|
1839
|
+
elif [[ "${scope_vars[TS_STRICT]}" == "false" ]]; then
|
|
1840
|
+
scope_vars[TS_STRICT_LINE]="- TypeScript strict mode: disabled (consider enabling)"
|
|
1841
|
+
else
|
|
1842
|
+
scope_vars[TS_STRICT_LINE]="- Follow tsconfig.json compiler options"
|
|
1843
|
+
fi
|
|
1844
|
+
|
|
1845
|
+
# CSS approach detection
|
|
1846
|
+
_css_approach=$(detect_css_approach "$_node_config_root") || _css_approach=""
|
|
1847
|
+
scope_vars[CSS_APPROACH]="$_css_approach"
|
|
1848
|
+
|
|
1849
|
+
# CSS approach line (only show if detected)
|
|
1850
|
+
[[ -n "${scope_vars[CSS_APPROACH]:-}" ]] && \
|
|
1851
|
+
scope_vars[CSS_APPROACH_LINE]="- CSS: ${scope_vars[CSS_APPROACH]}"
|
|
1852
|
+
|
|
1853
|
+
# Build whole-line placeholders for commands section
|
|
1854
|
+
[ -n "${scope_vars[INSTALL_CMD]:-}" ] && \
|
|
1855
|
+
scope_vars[INSTALL_LINE]="- Install: \`${scope_vars[INSTALL_CMD]}\`"
|
|
1856
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
1857
|
+
scope_vars[TYPECHECK_LINE]="- Typecheck: \`${scope_vars[TYPECHECK_CMD]}\`"
|
|
1858
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1859
|
+
scope_vars[LINT_LINE]="- Lint: \`${scope_vars[LINT_CMD]}\`"
|
|
1860
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
1861
|
+
scope_vars[FORMAT_LINE]="- Format: \`${scope_vars[FORMAT_CMD]}\`"
|
|
1862
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1863
|
+
scope_vars[TEST_LINE]="- Test: \`${scope_vars[TEST_CMD]}\`"
|
|
1864
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
1865
|
+
scope_vars[BUILD_LINE]="- Build: \`${scope_vars[BUILD_CMD]}\`"
|
|
1866
|
+
[ -n "${scope_vars[DEV_CMD]:-}" ] && \
|
|
1867
|
+
scope_vars[DEV_LINE]="- Dev server: \`${scope_vars[DEV_CMD]}\`"
|
|
1868
|
+
|
|
1869
|
+
# Framework-specific component style and conventions
|
|
1870
|
+
case "$FRAMEWORK" in
|
|
1871
|
+
"react")
|
|
1872
|
+
scope_vars[COMPONENT_STYLE_LINE]="- Use functional components with hooks"
|
|
1873
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Avoid class components"
|
|
1874
|
+
scope_vars[FRAMEWORK_DOCS]="https://react.dev"
|
|
1875
|
+
scope_vars[FRAMEWORK_DOCS_LINE]="- Check React documentation: https://react.dev"
|
|
1876
|
+
;;
|
|
1877
|
+
"next.js")
|
|
1878
|
+
scope_vars[COMPONENT_STYLE_LINE]="- Use functional components with hooks"
|
|
1879
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Use App Router (app/)
|
|
1880
|
+
- Server Components by default"
|
|
1881
|
+
scope_vars[FRAMEWORK_DOCS]="https://nextjs.org/docs"
|
|
1882
|
+
scope_vars[FRAMEWORK_DOCS_LINE]="- Check Next.js documentation: https://nextjs.org/docs"
|
|
1883
|
+
;;
|
|
1884
|
+
"vue")
|
|
1885
|
+
scope_vars[COMPONENT_STYLE_LINE]="- Use Composition API with script setup"
|
|
1886
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Avoid Options API for new code"
|
|
1887
|
+
scope_vars[FRAMEWORK_DOCS]="https://vuejs.org/guide"
|
|
1888
|
+
scope_vars[FRAMEWORK_DOCS_LINE]="- Check Vue documentation: https://vuejs.org/guide"
|
|
1889
|
+
;;
|
|
1890
|
+
"svelte")
|
|
1891
|
+
scope_vars[COMPONENT_STYLE_LINE]="- Use Svelte component syntax"
|
|
1892
|
+
scope_vars[FRAMEWORK_CONVENTIONS]=""
|
|
1893
|
+
scope_vars[FRAMEWORK_DOCS]="https://svelte.dev/docs"
|
|
1894
|
+
scope_vars[FRAMEWORK_DOCS_LINE]="- Check Svelte documentation: https://svelte.dev/docs"
|
|
1895
|
+
;;
|
|
1896
|
+
"angular")
|
|
1897
|
+
scope_vars[COMPONENT_STYLE_LINE]="- Use standalone components"
|
|
1898
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Follow Angular style guide"
|
|
1899
|
+
scope_vars[FRAMEWORK_DOCS]="https://angular.dev"
|
|
1900
|
+
scope_vars[FRAMEWORK_DOCS_LINE]="- Check Angular documentation: https://angular.dev"
|
|
1901
|
+
;;
|
|
1902
|
+
*)
|
|
1903
|
+
scope_vars[COMPONENT_STYLE_LINE]=""
|
|
1904
|
+
scope_vars[FRAMEWORK_CONVENTIONS]=""
|
|
1905
|
+
scope_vars[FRAMEWORK_DOCS]=""
|
|
1906
|
+
scope_vars[FRAMEWORK_DOCS_LINE]=""
|
|
1907
|
+
;;
|
|
1908
|
+
esac
|
|
1909
|
+
|
|
1910
|
+
# Build whole-line placeholders (disappear cleanly when empty)
|
|
1911
|
+
[ -n "${scope_vars[NODE_VERSION]:-}" ] && [ "${scope_vars[NODE_VERSION]}" != "unknown" ] && \
|
|
1912
|
+
scope_vars[NODE_VERSION_LINE]="- Node version: ${scope_vars[NODE_VERSION]}"
|
|
1913
|
+
[ -n "${scope_vars[FRAMEWORK]:-}" ] && [ "${scope_vars[FRAMEWORK]}" != "none" ] && \
|
|
1914
|
+
scope_vars[FRAMEWORK_LINE]="- Framework: ${scope_vars[FRAMEWORK]}"
|
|
1915
|
+
[ -n "${scope_vars[PACKAGE_MANAGER]:-}" ] && [ "${scope_vars[PACKAGE_MANAGER]}" != "unknown" ] && \
|
|
1916
|
+
scope_vars[PACKAGE_MANAGER_LINE]="- Package manager: ${scope_vars[PACKAGE_MANAGER]}"
|
|
1917
|
+
[ -n "${scope_vars[ENV_VARS]:-}" ] && \
|
|
1918
|
+
scope_vars[ENV_VARS_LINE]="- Environment variables: ${scope_vars[ENV_VARS]}"
|
|
1919
|
+
|
|
1920
|
+
# Build checklist lines (only when command exists)
|
|
1921
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1922
|
+
scope_vars[TEST_CHECKLIST_LINE]="- [ ] Tests pass: \`${scope_vars[TEST_CMD]}\`"
|
|
1923
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
1924
|
+
scope_vars[TYPECHECK_CHECKLIST_LINE]="- [ ] TypeScript compiles: \`${scope_vars[TYPECHECK_CMD]}\`"
|
|
1925
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1926
|
+
scope_vars[LINT_CHECKLIST_LINE]="- [ ] Lint clean: \`${scope_vars[LINT_CMD]}\`"
|
|
1927
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
1928
|
+
scope_vars[FORMAT_CHECKLIST_LINE]="- [ ] Formatted: \`${scope_vars[FORMAT_CMD]}\`"
|
|
1929
|
+
|
|
1930
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "ts" "tsx")
|
|
1931
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "ts" "tsx")
|
|
1932
|
+
;;
|
|
1933
|
+
|
|
1934
|
+
"cli")
|
|
1935
|
+
scope_vars[LANGUAGE]="$LANGUAGE"
|
|
1936
|
+
CLI_FRAMEWORK="standard"
|
|
1937
|
+
[ -f "go.mod" ] && grep -q "github.com/spf13/cobra" go.mod 2>/dev/null && CLI_FRAMEWORK="cobra"
|
|
1938
|
+
[ -f "go.mod" ] && grep -q "github.com/urfave/cli" go.mod 2>/dev/null && CLI_FRAMEWORK="urfave/cli"
|
|
1939
|
+
scope_vars[CLI_FRAMEWORK]="$CLI_FRAMEWORK"
|
|
1940
|
+
scope_vars[BUILD_OUTPUT_PATH]="./bin/"
|
|
1941
|
+
build_cmd="$(echo "$SCOPE_COMMANDS" | jq -r '.build // empty')"
|
|
1942
|
+
[ -n "$build_cmd" ] && scope_vars[SETUP_INSTRUCTIONS]="- Build: \`$build_cmd\`"
|
|
1943
|
+
set_scope_if_present BUILD_CMD "$build_cmd"
|
|
1944
|
+
scope_vars[RUN_CMD]="./bin/$(basename "$PROJECT_DIR")"
|
|
1945
|
+
set_scope_if_present TEST_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.test // empty')"
|
|
1946
|
+
set_scope_if_present LINT_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.lint // empty')"
|
|
1947
|
+
|
|
1948
|
+
# Build whole-line placeholders for setup section
|
|
1949
|
+
[ -n "${scope_vars[CLI_FRAMEWORK]:-}" ] && [ "${scope_vars[CLI_FRAMEWORK]}" != "standard" ] && \
|
|
1950
|
+
scope_vars[CLI_FRAMEWORK_LINE]="- CLI framework: ${scope_vars[CLI_FRAMEWORK]}"
|
|
1951
|
+
[ -n "${scope_vars[BUILD_OUTPUT_PATH]:-}" ] && \
|
|
1952
|
+
scope_vars[BUILD_OUTPUT_LINE]="- Build output: ${scope_vars[BUILD_OUTPUT_PATH]}"
|
|
1953
|
+
|
|
1954
|
+
# Build whole-line placeholders for commands section
|
|
1955
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
1956
|
+
scope_vars[BUILD_LINE]="- Build CLI: \`${scope_vars[BUILD_CMD]}\`"
|
|
1957
|
+
[ -n "${scope_vars[RUN_CMD]:-}" ] && \
|
|
1958
|
+
scope_vars[RUN_LINE]="- Run CLI: \`${scope_vars[RUN_CMD]}\`"
|
|
1959
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
1960
|
+
scope_vars[TEST_LINE]="- Test: \`${scope_vars[TEST_CMD]}\`"
|
|
1961
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
1962
|
+
scope_vars[LINT_LINE]="- Lint: \`${scope_vars[LINT_CMD]}\`"
|
|
1963
|
+
|
|
1964
|
+
# Build convention and help lines
|
|
1965
|
+
[ -n "${scope_vars[CLI_FRAMEWORK]:-}" ] && [ "${scope_vars[CLI_FRAMEWORK]}" != "standard" ] && \
|
|
1966
|
+
scope_vars[CLI_FRAMEWORK_CONVENTION_LINE]="- Use flag parsing library consistently (${scope_vars[CLI_FRAMEWORK]})"
|
|
1967
|
+
[ -n "${scope_vars[CLI_FRAMEWORK]:-}" ] && [ "${scope_vars[CLI_FRAMEWORK]}" != "standard" ] && \
|
|
1968
|
+
scope_vars[CLI_FRAMEWORK_DOCS_LINE]="- Review ${scope_vars[CLI_FRAMEWORK]} documentation"
|
|
1969
|
+
|
|
1970
|
+
# Detect language for CLI scope
|
|
1971
|
+
cli_ext="go"
|
|
1972
|
+
[ -f "package.json" ] && cli_ext="ts"
|
|
1973
|
+
[ -f "setup.py" ] || [ -f "pyproject.toml" ] && cli_ext="py"
|
|
1974
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "$cli_ext")
|
|
1975
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "$cli_ext")
|
|
1976
|
+
;;
|
|
1977
|
+
|
|
1978
|
+
"backend-python")
|
|
1979
|
+
scope_vars[PYTHON_VERSION]="$VERSION"
|
|
1980
|
+
BUILD_TOOL=$(echo "$PROJECT_INFO" | jq -r '.build_tool')
|
|
1981
|
+
scope_vars[PACKAGE_MANAGER]="$BUILD_TOOL"
|
|
1982
|
+
scope_vars[ENV_VARS]="See .env or .env.example"
|
|
1983
|
+
|
|
1984
|
+
# Extract commands from detected commands
|
|
1985
|
+
scope_vars[INSTALL_CMD]=$(echo "$COMMANDS" | jq -r '.install // empty')
|
|
1986
|
+
scope_vars[LINT_CMD]=$(echo "$COMMANDS" | jq -r '.lint // empty')
|
|
1987
|
+
scope_vars[FORMAT_CMD]=$(echo "$COMMANDS" | jq -r '.format // empty')
|
|
1988
|
+
scope_vars[TEST_CMD]=$(echo "$COMMANDS" | jq -r '.test // empty')
|
|
1989
|
+
scope_vars[TYPECHECK_CMD]=$(echo "$COMMANDS" | jq -r '.typecheck // empty')
|
|
1990
|
+
scope_vars[BUILD_CMD]=$(echo "$COMMANDS" | jq -r '.build // empty')
|
|
1991
|
+
|
|
1992
|
+
# Set defaults based on build tool
|
|
1993
|
+
case "$BUILD_TOOL" in
|
|
1994
|
+
"poetry")
|
|
1995
|
+
[ -z "${scope_vars[INSTALL_CMD]}" ] && scope_vars[INSTALL_CMD]="poetry install"
|
|
1996
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="poetry run ruff check ."
|
|
1997
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="poetry run ruff format ."
|
|
1998
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="poetry run pytest"
|
|
1999
|
+
scope_vars[VENV_CMD]="poetry shell"
|
|
2000
|
+
;;
|
|
2001
|
+
"pip"|"uv")
|
|
2002
|
+
[ -z "${scope_vars[INSTALL_CMD]}" ] && scope_vars[INSTALL_CMD]="pip install -e ."
|
|
2003
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="ruff check ."
|
|
2004
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="ruff format ."
|
|
2005
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="pytest"
|
|
2006
|
+
scope_vars[VENV_CMD]="python -m venv .venv && source .venv/bin/activate"
|
|
2007
|
+
;;
|
|
2008
|
+
*)
|
|
2009
|
+
scope_vars[VENV_CMD]="python -m venv .venv && source .venv/bin/activate"
|
|
2010
|
+
;;
|
|
2011
|
+
esac
|
|
2012
|
+
|
|
2013
|
+
# Detect framework for conventions
|
|
2014
|
+
FRAMEWORK=$(echo "$PROJECT_INFO" | jq -r '.framework')
|
|
2015
|
+
case "$FRAMEWORK" in
|
|
2016
|
+
"django")
|
|
2017
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Follow Django conventions (apps, models, views)
|
|
2018
|
+
- Use Django ORM, avoid raw SQL"
|
|
2019
|
+
scope_vars[FRAMEWORK_DOCS]="https://docs.djangoproject.com"
|
|
2020
|
+
;;
|
|
2021
|
+
"fastapi")
|
|
2022
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Use Pydantic models for validation
|
|
2023
|
+
- Async handlers where appropriate"
|
|
2024
|
+
scope_vars[FRAMEWORK_DOCS]="https://fastapi.tiangolo.com"
|
|
2025
|
+
;;
|
|
2026
|
+
"flask")
|
|
2027
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Use Blueprints for modular design
|
|
2028
|
+
- Flask-SQLAlchemy for database"
|
|
2029
|
+
scope_vars[FRAMEWORK_DOCS]="https://flask.palletsprojects.com"
|
|
2030
|
+
;;
|
|
2031
|
+
*)
|
|
2032
|
+
scope_vars[FRAMEWORK_CONVENTIONS]=""
|
|
2033
|
+
scope_vars[FRAMEWORK_DOCS]=""
|
|
2034
|
+
;;
|
|
2035
|
+
esac
|
|
2036
|
+
scope_vars[HOUSE_RULES]=""
|
|
2037
|
+
;;
|
|
2038
|
+
|
|
2039
|
+
"backend-typescript")
|
|
2040
|
+
scope_vars[NODE_VERSION]="$VERSION"
|
|
2041
|
+
BUILD_TOOL=$(echo "$PROJECT_INFO" | jq -r '.build_tool')
|
|
2042
|
+
scope_vars[PACKAGE_MANAGER]="$BUILD_TOOL"
|
|
2043
|
+
scope_vars[ENV_VARS]="See .env or .env.example"
|
|
2044
|
+
|
|
2045
|
+
# Extract commands from detected commands
|
|
2046
|
+
scope_vars[INSTALL_CMD]="$BUILD_TOOL install"
|
|
2047
|
+
scope_vars[LINT_CMD]=$(echo "$COMMANDS" | jq -r '.lint // empty')
|
|
2048
|
+
scope_vars[FORMAT_CMD]=$(echo "$COMMANDS" | jq -r '.format // empty')
|
|
2049
|
+
scope_vars[TEST_CMD]=$(echo "$COMMANDS" | jq -r '.test // empty')
|
|
2050
|
+
scope_vars[TYPECHECK_CMD]=$(echo "$COMMANDS" | jq -r '.typecheck // empty')
|
|
2051
|
+
scope_vars[BUILD_CMD]=$(echo "$COMMANDS" | jq -r '.build // empty')
|
|
2052
|
+
scope_vars[DEV_CMD]=$(echo "$COMMANDS" | jq -r '.dev // empty')
|
|
2053
|
+
|
|
2054
|
+
# Set defaults based on package manager
|
|
2055
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="$BUILD_TOOL run lint"
|
|
2056
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="$BUILD_TOOL run format"
|
|
2057
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="$BUILD_TOOL test"
|
|
2058
|
+
[ -z "${scope_vars[TYPECHECK_CMD]}" ] && scope_vars[TYPECHECK_CMD]="$BUILD_TOOL run typecheck"
|
|
2059
|
+
[ -z "${scope_vars[BUILD_CMD]}" ] && scope_vars[BUILD_CMD]="$BUILD_TOOL run build"
|
|
2060
|
+
[ -z "${scope_vars[DEV_CMD]}" ] && scope_vars[DEV_CMD]="$BUILD_TOOL run dev"
|
|
2061
|
+
|
|
2062
|
+
# Detect framework
|
|
2063
|
+
FRAMEWORK=$(echo "$PROJECT_INFO" | jq -r '.framework')
|
|
2064
|
+
case "$FRAMEWORK" in
|
|
2065
|
+
"express")
|
|
2066
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Use middleware pattern
|
|
2067
|
+
- Async error handling with express-async-handler"
|
|
2068
|
+
scope_vars[FRAMEWORK_DOCS]="https://expressjs.com"
|
|
2069
|
+
;;
|
|
2070
|
+
"nestjs")
|
|
2071
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Use decorators and modules
|
|
2072
|
+
- Dependency injection via constructors"
|
|
2073
|
+
scope_vars[FRAMEWORK_DOCS]="https://docs.nestjs.com"
|
|
2074
|
+
;;
|
|
2075
|
+
"fastify")
|
|
2076
|
+
scope_vars[FRAMEWORK_CONVENTIONS]="- Use schema validation
|
|
2077
|
+
- Plugin architecture"
|
|
2078
|
+
scope_vars[FRAMEWORK_DOCS]="https://www.fastify.io/docs"
|
|
2079
|
+
;;
|
|
2080
|
+
*)
|
|
2081
|
+
scope_vars[FRAMEWORK_CONVENTIONS]=""
|
|
2082
|
+
scope_vars[FRAMEWORK_DOCS]=""
|
|
2083
|
+
;;
|
|
2084
|
+
esac
|
|
2085
|
+
scope_vars[HOUSE_RULES]=""
|
|
2086
|
+
;;
|
|
2087
|
+
|
|
2088
|
+
"testing")
|
|
2089
|
+
set_scope_if_present TEST_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.test // empty')"
|
|
2090
|
+
set_scope_if_present TEST_SINGLE_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.test_single // empty')"
|
|
2091
|
+
set_scope_if_present TEST_COVERAGE_CMD "$(echo "$SCOPE_COMMANDS" | jq -r '.test_coverage // empty')"
|
|
2092
|
+
|
|
2093
|
+
# Build whole-line placeholders for commands section
|
|
2094
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
2095
|
+
scope_vars[TEST_LINE]="- Run all tests: \`${scope_vars[TEST_CMD]}\`"
|
|
2096
|
+
[ -n "${scope_vars[TEST_SINGLE_CMD]:-}" ] && \
|
|
2097
|
+
scope_vars[TEST_SINGLE_LINE]="- Run specific test file: \`${scope_vars[TEST_SINGLE_CMD]}\`"
|
|
2098
|
+
[ -n "${scope_vars[TEST_COVERAGE_CMD]:-}" ] && \
|
|
2099
|
+
scope_vars[TEST_COVERAGE_LINE]="- Run with coverage: \`${scope_vars[TEST_COVERAGE_CMD]}\`"
|
|
2100
|
+
|
|
2101
|
+
# Detect test file extension
|
|
2102
|
+
test_ext="php"
|
|
2103
|
+
[ -f "go.mod" ] && test_ext="go"
|
|
2104
|
+
[ -f "package.json" ] && test_ext="ts"
|
|
2105
|
+
[ -f "pyproject.toml" ] && test_ext="py"
|
|
2106
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "$test_ext")
|
|
2107
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "$test_ext")
|
|
2108
|
+
;;
|
|
2109
|
+
|
|
2110
|
+
"documentation")
|
|
2111
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "md")
|
|
2112
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "md")
|
|
2113
|
+
;;
|
|
2114
|
+
|
|
2115
|
+
"examples")
|
|
2116
|
+
# Detect example file extension
|
|
2117
|
+
ex_ext="go"
|
|
2118
|
+
[ -f "package.json" ] && ex_ext="ts"
|
|
2119
|
+
[ -f "pyproject.toml" ] && ex_ext="py"
|
|
2120
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "$ex_ext")
|
|
2121
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "$ex_ext")
|
|
2122
|
+
;;
|
|
2123
|
+
|
|
2124
|
+
"resources")
|
|
2125
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "*")
|
|
2126
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=""
|
|
2127
|
+
;;
|
|
2128
|
+
|
|
2129
|
+
"claude-code-skill")
|
|
2130
|
+
# Extract plugin info (if plugin.json exists)
|
|
2131
|
+
plugin_name=$(jq -r '.name // "unknown"' .claude-plugin/plugin.json 2>/dev/null || echo "unknown")
|
|
2132
|
+
plugin_version=$(jq -r '.version // "unknown"' .claude-plugin/plugin.json 2>/dev/null || echo "unknown")
|
|
2133
|
+
skill_count=$(find skills -maxdepth 2 -name "SKILL.md" -type f 2>/dev/null | wc -l)
|
|
2134
|
+
|
|
2135
|
+
# Build whole-line placeholders
|
|
2136
|
+
if [ -f ".claude-plugin/plugin.json" ]; then
|
|
2137
|
+
scope_vars[PLUGIN_JSON_LINE]="- Plugin: $plugin_name v$plugin_version"
|
|
2138
|
+
fi
|
|
2139
|
+
scope_vars[SKILLS_LINE]="- Skills: $skill_count skill(s) in \`skills/\`"
|
|
2140
|
+
if [ -f "composer.json" ]; then
|
|
2141
|
+
scope_vars[INSTALL_LINE]="- Install: \`composer require $(jq -r '.name // "vendor/package"' composer.json 2>/dev/null)\`"
|
|
2142
|
+
fi
|
|
2143
|
+
|
|
2144
|
+
# Build command lines if shell scripts exist
|
|
2145
|
+
sh_count=$(find . -maxdepth 5 -name "*.sh" -type f 2>/dev/null | wc -l)
|
|
2146
|
+
if [ "$sh_count" -gt 0 ]; then
|
|
2147
|
+
scope_vars[LINT_LINE]="- Lint scripts: \`shellcheck skills/*/scripts/*.sh\`"
|
|
2148
|
+
scope_vars[LINT_CHECKLIST_LINE]="- [ ] ShellCheck passes: \`shellcheck skills/*/scripts/*.sh\`"
|
|
2149
|
+
fi
|
|
2150
|
+
scope_vars[VALIDATE_LINE]="- Validate plugin: \`jq . .claude-plugin/plugin.json\`"
|
|
2151
|
+
|
|
2152
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "sh")
|
|
2153
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "md")
|
|
2154
|
+
scope_vars[HOUSE_RULES]=""
|
|
2155
|
+
;;
|
|
2156
|
+
|
|
2157
|
+
"docker")
|
|
2158
|
+
# Detect Docker and Compose versions from CI or system
|
|
2159
|
+
docker_version="latest"
|
|
2160
|
+
compose_version="v2"
|
|
2161
|
+
|
|
2162
|
+
# Try to get from CI config
|
|
2163
|
+
if [ -f ".github/workflows/docker-build.yml" ]; then
|
|
2164
|
+
docker_version=$(grep -E 'docker.*version' .github/workflows/*.yml 2>/dev/null | head -1 | sed 's/.*: *//' || echo "latest")
|
|
2165
|
+
fi
|
|
2166
|
+
|
|
2167
|
+
# Build whole-line placeholders for setup section
|
|
2168
|
+
[ -n "$docker_version" ] && [ "$docker_version" != "latest" ] && \
|
|
2169
|
+
scope_vars[DOCKER_VERSION_LINE]="- Docker version: $docker_version"
|
|
2170
|
+
[ -n "$compose_version" ] && \
|
|
2171
|
+
scope_vars[COMPOSE_VERSION_LINE]="- Compose version: $compose_version"
|
|
2172
|
+
|
|
2173
|
+
# Detect registry from CI or compose files
|
|
2174
|
+
registry=""
|
|
2175
|
+
if [ -f "docker-compose.yml" ]; then
|
|
2176
|
+
registry=$(grep -E 'image:' docker-compose.yml 2>/dev/null | head -1 | sed 's/.*image: *//;s/:.*//;s|/.*||' || echo "")
|
|
2177
|
+
fi
|
|
2178
|
+
if [ -z "$registry" ] && [ -d ".github/workflows" ]; then
|
|
2179
|
+
registry=$(grep -E 'registry:|ghcr.io|docker.io|quay.io' .github/workflows/*.yml 2>/dev/null | head -1 | sed 's/.*: *//' || echo "")
|
|
2180
|
+
fi
|
|
2181
|
+
[ -n "$registry" ] && scope_vars[REGISTRY_LINE]="- Registry: $registry"
|
|
2182
|
+
|
|
2183
|
+
# Generate file map for Docker files
|
|
2184
|
+
docker_file_map=""
|
|
2185
|
+
while IFS= read -r df; do
|
|
2186
|
+
[ -z "$df" ] && continue
|
|
2187
|
+
filename=$(basename "$df")
|
|
2188
|
+
[ -z "$docker_file_map" ] && docker_file_map="| File | Purpose |\n|------|---------|"
|
|
2189
|
+
docker_file_map="$docker_file_map\n| \`$filename\` | (add description) |"
|
|
2190
|
+
done < <(find "$SCOPE_PATH" -maxdepth 1 -type f \( -name "Dockerfile*" -o -name "*.dockerfile" -o -name "docker-compose*.yml" -o -name "compose*.yml" -o -name ".dockerignore" \) 2>/dev/null | sort)
|
|
2191
|
+
scope_vars[SCOPE_FILE_MAP]=$(echo -e "$docker_file_map")
|
|
2192
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=""
|
|
2193
|
+
scope_vars[HOUSE_RULES]=""
|
|
2194
|
+
;;
|
|
2195
|
+
|
|
2196
|
+
"github-actions")
|
|
2197
|
+
# Count workflows
|
|
2198
|
+
workflow_count=$(find .github/workflows -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | wc -l)
|
|
2199
|
+
scope_vars[WORKFLOW_COUNT_LINE]="- Workflows: $workflow_count workflow file(s)"
|
|
2200
|
+
|
|
2201
|
+
# Check for reusable workflows
|
|
2202
|
+
reusable_count=$(grep -l "workflow_call:" .github/workflows/*.yml 2>/dev/null | wc -l || true)
|
|
2203
|
+
[ "$reusable_count" -gt 0 ] && \
|
|
2204
|
+
scope_vars[REUSABLE_WORKFLOWS_LINE]="- Reusable workflows: $reusable_count"
|
|
2205
|
+
|
|
2206
|
+
# Generate file map for workflows
|
|
2207
|
+
workflow_file_map=""
|
|
2208
|
+
while IFS= read -r wf; do
|
|
2209
|
+
[ -z "$wf" ] && continue
|
|
2210
|
+
filename=$(basename "$wf")
|
|
2211
|
+
# Try to extract workflow name from file
|
|
2212
|
+
wf_name=$(grep -m1 "^name:" "$wf" 2>/dev/null | sed 's/name: *//' || echo "(add description)")
|
|
2213
|
+
[ -z "$workflow_file_map" ] && workflow_file_map="| File | Purpose |\n|------|---------|"
|
|
2214
|
+
workflow_file_map="$workflow_file_map\n| \`$filename\` | $wf_name |"
|
|
2215
|
+
done < <(find .github/workflows -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null | sort)
|
|
2216
|
+
scope_vars[SCOPE_FILE_MAP]=$(echo -e "$workflow_file_map")
|
|
2217
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=""
|
|
2218
|
+
scope_vars[HOUSE_RULES]=""
|
|
2219
|
+
;;
|
|
2220
|
+
|
|
2221
|
+
"gitlab-ci")
|
|
2222
|
+
# Count pipeline stages/jobs
|
|
2223
|
+
job_count=$(grep -cE "^[a-zA-Z_-]+:$" .gitlab-ci.yml 2>/dev/null || echo "0")
|
|
2224
|
+
scope_vars[JOB_COUNT_LINE]="- Jobs defined: ~$job_count"
|
|
2225
|
+
|
|
2226
|
+
# Check for includes
|
|
2227
|
+
include_count=$(grep -cE "^ *- " .gitlab-ci.yml 2>/dev/null | head -1 || echo "0")
|
|
2228
|
+
[ "$include_count" -gt 0 ] && \
|
|
2229
|
+
scope_vars[INCLUDES_LINE]="- Includes external files/templates"
|
|
2230
|
+
|
|
2231
|
+
# File map - just the main file and any .gitlab/ directory contents
|
|
2232
|
+
gitlab_file_map="| File | Purpose |\n|------|---------|"
|
|
2233
|
+
gitlab_file_map="$gitlab_file_map\n| \`.gitlab-ci.yml\` | Main pipeline configuration |"
|
|
2234
|
+
if [ -d ".gitlab" ]; then
|
|
2235
|
+
while IFS= read -r gf; do
|
|
2236
|
+
[ -z "$gf" ] && continue
|
|
2237
|
+
filename="${gf#.gitlab/}"
|
|
2238
|
+
gitlab_file_map="$gitlab_file_map\n| \`.gitlab/$filename\` | (add description) |"
|
|
2239
|
+
done < <(find .gitlab -type f -name "*.yml" 2>/dev/null | sort)
|
|
2240
|
+
fi
|
|
2241
|
+
scope_vars[SCOPE_FILE_MAP]=$(echo -e "$gitlab_file_map")
|
|
2242
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=""
|
|
2243
|
+
scope_vars[HOUSE_RULES]=""
|
|
2244
|
+
;;
|
|
2245
|
+
|
|
2246
|
+
"concourse")
|
|
2247
|
+
# Count pipeline files
|
|
2248
|
+
pipeline_count=$(find . -maxdepth 2 -name "*pipeline*.yml" -o -name "*pipeline*.yaml" 2>/dev/null | wc -l)
|
|
2249
|
+
scope_vars[PIPELINE_COUNT_LINE]="- Pipeline files: $pipeline_count"
|
|
2250
|
+
|
|
2251
|
+
# Check for tasks directory
|
|
2252
|
+
[ -d "ci/tasks" ] || [ -d "tasks" ] && \
|
|
2253
|
+
scope_vars[TASKS_LINE]="- Tasks directory present"
|
|
2254
|
+
|
|
2255
|
+
# File map
|
|
2256
|
+
concourse_file_map="| File | Purpose |\n|------|---------|"
|
|
2257
|
+
while IFS= read -r pf; do
|
|
2258
|
+
[ -z "$pf" ] && continue
|
|
2259
|
+
filename=$(basename "$pf")
|
|
2260
|
+
concourse_file_map="$concourse_file_map\n| \`$filename\` | (add description) |"
|
|
2261
|
+
done < <(find . -maxdepth 2 \( -name "*pipeline*.yml" -o -name "*pipeline*.yaml" \) 2>/dev/null | sort)
|
|
2262
|
+
if [ -d "ci/tasks" ]; then
|
|
2263
|
+
task_count=$(find ci/tasks -name "*.yml" 2>/dev/null | wc -l)
|
|
2264
|
+
[ "$task_count" -gt 0 ] && \
|
|
2265
|
+
concourse_file_map="$concourse_file_map\n| \`ci/tasks/\` | $task_count task definition(s) |"
|
|
2266
|
+
fi
|
|
2267
|
+
scope_vars[SCOPE_FILE_MAP]=$(echo -e "$concourse_file_map")
|
|
2268
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=""
|
|
2269
|
+
scope_vars[HOUSE_RULES]=""
|
|
2270
|
+
;;
|
|
2271
|
+
|
|
2272
|
+
"python-modern")
|
|
2273
|
+
scope_vars[PYTHON_VERSION]="$VERSION"
|
|
2274
|
+
BUILD_TOOL=$(echo "$PROJECT_INFO" | jq -r '.build_tool')
|
|
2275
|
+
scope_vars[PACKAGE_MANAGER]="$BUILD_TOOL"
|
|
2276
|
+
scope_vars[ENV_VARS]="See .env or .env.example"
|
|
2277
|
+
|
|
2278
|
+
# Extract commands from detected commands
|
|
2279
|
+
scope_vars[INSTALL_CMD]=$(echo "$COMMANDS" | jq -r '.install // empty')
|
|
2280
|
+
scope_vars[LINT_CMD]=$(echo "$COMMANDS" | jq -r '.lint // empty')
|
|
2281
|
+
scope_vars[FORMAT_CMD]=$(echo "$COMMANDS" | jq -r '.format // empty')
|
|
2282
|
+
scope_vars[TEST_CMD]=$(echo "$COMMANDS" | jq -r '.test // empty')
|
|
2283
|
+
scope_vars[TYPECHECK_CMD]=$(echo "$COMMANDS" | jq -r '.typecheck // empty')
|
|
2284
|
+
scope_vars[BUILD_CMD]=$(echo "$COMMANDS" | jq -r '.build // empty')
|
|
2285
|
+
|
|
2286
|
+
# Set ruff/mypy-aware defaults
|
|
2287
|
+
case "$BUILD_TOOL" in
|
|
2288
|
+
"poetry")
|
|
2289
|
+
[ -z "${scope_vars[INSTALL_CMD]}" ] && scope_vars[INSTALL_CMD]="poetry install"
|
|
2290
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="poetry run ruff check ."
|
|
2291
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="poetry run ruff format ."
|
|
2292
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="poetry run pytest"
|
|
2293
|
+
[ -z "${scope_vars[TYPECHECK_CMD]}" ] && scope_vars[TYPECHECK_CMD]="poetry run mypy ."
|
|
2294
|
+
scope_vars[VENV_CMD]="poetry shell"
|
|
2295
|
+
;;
|
|
2296
|
+
"uv")
|
|
2297
|
+
[ -z "${scope_vars[INSTALL_CMD]}" ] && scope_vars[INSTALL_CMD]="uv sync"
|
|
2298
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="uv run ruff check ."
|
|
2299
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="uv run ruff format ."
|
|
2300
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="uv run pytest"
|
|
2301
|
+
[ -z "${scope_vars[TYPECHECK_CMD]}" ] && scope_vars[TYPECHECK_CMD]="uv run mypy ."
|
|
2302
|
+
scope_vars[VENV_CMD]="Managed by uv (auto-creates .venv)"
|
|
2303
|
+
;;
|
|
2304
|
+
"hatch")
|
|
2305
|
+
[ -z "${scope_vars[INSTALL_CMD]}" ] && scope_vars[INSTALL_CMD]="hatch env create"
|
|
2306
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="hatch run lint:check"
|
|
2307
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="hatch run lint:fmt"
|
|
2308
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="hatch run test"
|
|
2309
|
+
[ -z "${scope_vars[TYPECHECK_CMD]}" ] && scope_vars[TYPECHECK_CMD]="hatch run types:check"
|
|
2310
|
+
scope_vars[VENV_CMD]="Managed by hatch"
|
|
2311
|
+
;;
|
|
2312
|
+
*)
|
|
2313
|
+
[ -z "${scope_vars[LINT_CMD]}" ] && scope_vars[LINT_CMD]="ruff check ."
|
|
2314
|
+
[ -z "${scope_vars[FORMAT_CMD]}" ] && scope_vars[FORMAT_CMD]="ruff format ."
|
|
2315
|
+
[ -z "${scope_vars[TEST_CMD]}" ] && scope_vars[TEST_CMD]="pytest"
|
|
2316
|
+
[ -z "${scope_vars[TYPECHECK_CMD]}" ] && scope_vars[TYPECHECK_CMD]="mypy ."
|
|
2317
|
+
scope_vars[VENV_CMD]="python -m venv .venv && source .venv/bin/activate"
|
|
2318
|
+
;;
|
|
2319
|
+
esac
|
|
2320
|
+
|
|
2321
|
+
# Build whole-line placeholders for setup
|
|
2322
|
+
[ -n "${scope_vars[PYTHON_VERSION]:-}" ] && [ "${scope_vars[PYTHON_VERSION]}" != "unknown" ] && \
|
|
2323
|
+
scope_vars[PYTHON_VERSION_LINE]="- Python: ${scope_vars[PYTHON_VERSION]}"
|
|
2324
|
+
[ -n "${scope_vars[PACKAGE_MANAGER]:-}" ] && [ "${scope_vars[PACKAGE_MANAGER]}" != "unknown" ] && \
|
|
2325
|
+
scope_vars[PACKAGE_MANAGER_LINE]="- Package manager: ${scope_vars[PACKAGE_MANAGER]}"
|
|
2326
|
+
[ -n "${scope_vars[VENV_CMD]:-}" ] && \
|
|
2327
|
+
scope_vars[VENV_LINE]="- Virtual env: \`${scope_vars[VENV_CMD]}\`"
|
|
2328
|
+
scope_vars[ENV_VARS_LINE]="- Environment: see \`.env\` or \`.env.example\`"
|
|
2329
|
+
|
|
2330
|
+
# Build command table lines
|
|
2331
|
+
[ -n "${scope_vars[LINT_CMD]:-}" ] && \
|
|
2332
|
+
scope_vars[RUFF_CHECK_LINE]="| \`${scope_vars[LINT_CMD]}\` | Lint (ruff) | ~5s |"
|
|
2333
|
+
[ -n "${scope_vars[FORMAT_CMD]:-}" ] && \
|
|
2334
|
+
scope_vars[RUFF_FORMAT_LINE]="| \`${scope_vars[FORMAT_CMD]} --check\` | Format check | ~3s |"
|
|
2335
|
+
[ -n "${scope_vars[TYPECHECK_CMD]:-}" ] && \
|
|
2336
|
+
scope_vars[MYPY_LINE]="| \`${scope_vars[TYPECHECK_CMD]}\` | Type check (mypy) | ~15s |"
|
|
2337
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
2338
|
+
scope_vars[PYTEST_LINE]="| \`${scope_vars[TEST_CMD]}\` | Run tests | ~30s |"
|
|
2339
|
+
[ -n "${scope_vars[TEST_CMD]:-}" ] && \
|
|
2340
|
+
scope_vars[PYTEST_COV_LINE]="| \`${scope_vars[TEST_CMD]} --cov=src\` | Tests + coverage | ~45s |"
|
|
2341
|
+
[ -n "${scope_vars[BUILD_CMD]:-}" ] && \
|
|
2342
|
+
scope_vars[BUILD_LINE]="| \`${scope_vars[BUILD_CMD]}\` | Build | ~10s |"
|
|
2343
|
+
|
|
2344
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "py")
|
|
2345
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "py")
|
|
2346
|
+
scope_vars[HOUSE_RULES]=""
|
|
2347
|
+
;;
|
|
2348
|
+
|
|
2349
|
+
"skill-repo")
|
|
2350
|
+
# Extract plugin info (if plugin.json exists)
|
|
2351
|
+
plugin_name=$(jq -r '.name // "unknown"' .claude-plugin/plugin.json 2>/dev/null || echo "unknown")
|
|
2352
|
+
plugin_version=$(jq -r '.version // "unknown"' .claude-plugin/plugin.json 2>/dev/null || echo "unknown")
|
|
2353
|
+
skill_count=$(find skills -maxdepth 2 -name "SKILL.md" -type f 2>/dev/null | wc -l)
|
|
2354
|
+
|
|
2355
|
+
# Build whole-line placeholders
|
|
2356
|
+
if [ -f ".claude-plugin/plugin.json" ]; then
|
|
2357
|
+
scope_vars[PLUGIN_JSON_LINE]="- Plugin: $plugin_name v$plugin_version"
|
|
2358
|
+
fi
|
|
2359
|
+
scope_vars[SKILLS_LINE]="- Skills: $skill_count skill(s) in \`skills/\`"
|
|
2360
|
+
|
|
2361
|
+
# Build command lines if shell scripts exist
|
|
2362
|
+
sh_count=$(find . -maxdepth 5 -name "*.sh" -type f 2>/dev/null | wc -l)
|
|
2363
|
+
if [ "$sh_count" -gt 0 ]; then
|
|
2364
|
+
scope_vars[LINT_LINE]="- Lint scripts: \`shellcheck skills/*/scripts/*.sh\`"
|
|
2365
|
+
scope_vars[SHELLCHECK_LINE]="- ShellCheck: \`shellcheck skills/*/scripts/*.sh\`"
|
|
2366
|
+
fi
|
|
2367
|
+
scope_vars[VALIDATE_LINE]="- Validate plugin: \`jq . .claude-plugin/plugin.json\`"
|
|
2368
|
+
|
|
2369
|
+
scope_vars[SCOPE_FILE_MAP]=$(generate_scope_file_map "$SCOPE_PATH" "sh")
|
|
2370
|
+
scope_vars[SCOPE_GOLDEN_SAMPLES]=$(generate_scope_golden_samples "$SCOPE_PATH" "md")
|
|
2371
|
+
scope_vars[HOUSE_RULES]=""
|
|
2372
|
+
;;
|
|
2373
|
+
esac
|
|
2374
|
+
|
|
2375
|
+
# Render template (smart mode respects --update flag)
|
|
2376
|
+
render_template_smart "$SCOPE_TEMPLATE" "$SCOPE_FILE" scope_vars "$UPDATE_ONLY"
|
|
2377
|
+
|
|
2378
|
+
# Enforce byte budget for scoped files (use half of root budget)
|
|
2379
|
+
enforce_byte_budget "$SCOPE_FILE" "$((BYTE_BUDGET / 2))"
|
|
2380
|
+
|
|
2381
|
+
if [ "$UPDATE_ONLY" = true ]; then
|
|
2382
|
+
echo "✅ Updated: $SCOPE_FILE"
|
|
2383
|
+
else
|
|
2384
|
+
echo "✅ Created: $SCOPE_FILE"
|
|
2385
|
+
fi
|
|
2386
|
+
|
|
2387
|
+
# Create subdirectory symlinks for cross-agent compatibility
|
|
2388
|
+
if [ "$CREATE_SYMLINKS" = true ]; then
|
|
2389
|
+
for symlink_name in CLAUDE.md GEMINI.md; do
|
|
2390
|
+
SYMLINK_FILE="$PROJECT_DIR/$SCOPE_PATH/$symlink_name"
|
|
2391
|
+
if [ -L "$SYMLINK_FILE" ] || [ ! -e "$SYMLINK_FILE" ]; then
|
|
2392
|
+
if [ "$DRY_RUN" = true ]; then
|
|
2393
|
+
echo "[DRY-RUN] Would symlink: $SYMLINK_FILE → AGENTS.md"
|
|
2394
|
+
else
|
|
2395
|
+
ln -sf AGENTS.md "$SYMLINK_FILE"
|
|
2396
|
+
echo " ↳ Symlinked: $SCOPE_PATH/$symlink_name → AGENTS.md"
|
|
2397
|
+
fi
|
|
2398
|
+
elif [ "$FORCE" = true ]; then
|
|
2399
|
+
rm -f "$SYMLINK_FILE"
|
|
2400
|
+
ln -s AGENTS.md "$SYMLINK_FILE"
|
|
2401
|
+
echo " ↳ Replaced: $SCOPE_PATH/$symlink_name → AGENTS.md (--force)"
|
|
2402
|
+
fi
|
|
2403
|
+
done
|
|
2404
|
+
fi
|
|
2405
|
+
|
|
2406
|
+
done < <(echo "$SCOPES_INFO" | jq -c '.scopes[]')
|
|
2407
|
+
fi
|
|
2408
|
+
|
|
2409
|
+
if [ "$DRY_RUN" = true ]; then
|
|
2410
|
+
echo ""
|
|
2411
|
+
echo "[DRY-RUN] No files were modified. Remove --dry-run to apply changes."
|
|
2412
|
+
fi
|
|
2413
|
+
|
|
2414
|
+
# Print extraction summary
|
|
2415
|
+
if [ "$VERBOSE" = true ]; then
|
|
2416
|
+
print_summary "$PROJECT_INFO" "$SCOPES_INFO" "$COMMANDS" "$DOCS_INFO" "$PLATFORM_INFO" "$IDE_INFO" "$AGENT_INFO"
|
|
2417
|
+
else
|
|
2418
|
+
echo ""
|
|
2419
|
+
print_compact_summary "$PROJECT_INFO" "$SCOPES_INFO"
|
|
2420
|
+
fi
|
|
2421
|
+
|
|
2422
|
+
echo ""
|
|
2423
|
+
echo "✅ AGENTS.md generation complete!"
|
|
2424
|
+
[ "$SCOPE_COUNT" -gt 0 ] && echo " Generated: 1 root + $SCOPE_COUNT scoped files" || true
|