@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.
Files changed (354) hide show
  1. package/dist/index.js +11 -4
  2. package/package.json +3 -4
  3. package/skills/ATTRIBUTION.md +80 -0
  4. package/skills/accessibility-audit/SKILL.md +325 -0
  5. package/skills/accessibility-audit/reference/wcag-checklist.md +103 -0
  6. package/skills/apns-notifier/SKILL.md +86 -0
  7. package/skills/approval-policy-enforcer/SKILL.md +66 -0
  8. package/skills/apps-sdk-builder/LICENSE.txt +201 -0
  9. package/skills/apps-sdk-builder/SKILL.md +328 -0
  10. package/skills/apps-sdk-builder/agents/openai.yaml +13 -0
  11. package/skills/apps-sdk-builder/references/app-archetypes.md +132 -0
  12. package/skills/apps-sdk-builder/references/apps-sdk-docs-workflow.md +135 -0
  13. package/skills/apps-sdk-builder/references/interactive-state-sync-patterns.md +113 -0
  14. package/skills/apps-sdk-builder/references/repo-contract-and-validation.md +93 -0
  15. package/skills/apps-sdk-builder/references/search-fetch-standard.md +67 -0
  16. package/skills/apps-sdk-builder/references/upstream-example-workflow.md +79 -0
  17. package/skills/apps-sdk-builder/references/window-openai-patterns.md +79 -0
  18. package/skills/apps-sdk-builder/scripts/scaffold_node_ext_apps.mjs +606 -0
  19. package/skills/architecture-selector/SKILL.md +64 -0
  20. package/skills/backlog-planner/SKILL.md +68 -0
  21. package/skills/carplay-entitlement-checker/SKILL.md +82 -0
  22. package/skills/concept-deepener/SKILL.md +86 -0
  23. package/skills/concept-discovery/SKILL.md +517 -0
  24. package/skills/concept-discovery/assets/sample-analysis.json +81 -0
  25. package/skills/concept-discovery/expected_outputs/sample-enum-dictionary.md +25 -0
  26. package/skills/concept-discovery/expected_outputs/sample-page-user-list.md +83 -0
  27. package/skills/concept-discovery/expected_outputs/sample-prd-readme.md +43 -0
  28. package/skills/concept-discovery/references/framework-patterns.md +228 -0
  29. package/skills/concept-discovery/references/prd-quality-checklist.md +65 -0
  30. package/skills/concept-discovery/scripts/codebase_analyzer.py +732 -0
  31. package/skills/concept-discovery/scripts/prd_scaffolder.py +435 -0
  32. package/skills/dast-zap/SKILL.md +453 -0
  33. package/skills/dast-zap/assets/.gitkeep +9 -0
  34. package/skills/dast-zap/assets/github_action.yml +207 -0
  35. package/skills/dast-zap/assets/gitlab_ci.yml +226 -0
  36. package/skills/dast-zap/assets/zap_automation.yaml +196 -0
  37. package/skills/dast-zap/assets/zap_context.xml +192 -0
  38. package/skills/dast-zap/references/EXAMPLE.md +40 -0
  39. package/skills/dast-zap/references/api_testing_guide.md +475 -0
  40. package/skills/dast-zap/references/authentication_guide.md +431 -0
  41. package/skills/dast-zap/references/false_positive_handling.md +427 -0
  42. package/skills/dast-zap/references/owasp_mapping.md +255 -0
  43. package/skills/dep-sbom-scan/SKILL.md +466 -0
  44. package/skills/deploy-cloudflare/SKILL.md +930 -0
  45. package/skills/deploy-docker/SKILL.md +55 -0
  46. package/skills/deploy-fly/SKILL.md +228 -0
  47. package/skills/deploy-k8s/SKILL.md +108 -0
  48. package/skills/deploy-k8s/assets/logo.png +0 -0
  49. package/skills/deploy-k8s/docs/README.md +29 -0
  50. package/skills/deploy-k8s/docs/SUMMARY.md +56 -0
  51. package/skills/deploy-k8s/docs/advanced/token-efficiency.md +61 -0
  52. package/skills/deploy-k8s/docs/architecture/multi-tenancy.md +96 -0
  53. package/skills/deploy-k8s/docs/architecture/storage-and-state.md +102 -0
  54. package/skills/deploy-k8s/docs/architecture/workload-patterns.md +87 -0
  55. package/skills/deploy-k8s/docs/book.json +16 -0
  56. package/skills/deploy-k8s/docs/community/changelog.md +34 -0
  57. package/skills/deploy-k8s/docs/community/contributing.md +67 -0
  58. package/skills/deploy-k8s/docs/core-concepts/failure-modes.md +153 -0
  59. package/skills/deploy-k8s/docs/core-concepts/philosophy.md +83 -0
  60. package/skills/deploy-k8s/docs/core-concepts/workflow.md +124 -0
  61. package/skills/deploy-k8s/docs/examples/bad-patterns.md +47 -0
  62. package/skills/deploy-k8s/docs/examples/do-dont-checklist.md +37 -0
  63. package/skills/deploy-k8s/docs/examples/good-patterns.md +49 -0
  64. package/skills/deploy-k8s/docs/failure-modes/api-drift.md +104 -0
  65. package/skills/deploy-k8s/docs/failure-modes/fragile-rollouts.md +99 -0
  66. package/skills/deploy-k8s/docs/failure-modes/insecure-workload-defaults.md +80 -0
  67. package/skills/deploy-k8s/docs/failure-modes/network-exposure.md +98 -0
  68. package/skills/deploy-k8s/docs/failure-modes/privilege-sprawl.md +91 -0
  69. package/skills/deploy-k8s/docs/failure-modes/resource-starvation.md +85 -0
  70. package/skills/deploy-k8s/docs/getting-started/installation.md +152 -0
  71. package/skills/deploy-k8s/docs/getting-started/quick-start.md +115 -0
  72. package/skills/deploy-k8s/docs/guides/helm-patterns.md +71 -0
  73. package/skills/deploy-k8s/docs/guides/kustomize-patterns.md +65 -0
  74. package/skills/deploy-k8s/docs/guides/observability.md +67 -0
  75. package/skills/deploy-k8s/docs/guides/security-hardening.md +59 -0
  76. package/skills/deploy-k8s/docs/guides/validation-and-policy.md +66 -0
  77. package/skills/deploy-k8s/docs/integrations/mcp-integration.md +52 -0
  78. package/skills/deploy-k8s/docs/package-lock.json +2892 -0
  79. package/skills/deploy-k8s/docs/package.json +13 -0
  80. package/skills/deploy-k8s/references/api-drift.md +298 -0
  81. package/skills/deploy-k8s/references/conditional/aks-patterns.md +70 -0
  82. package/skills/deploy-k8s/references/conditional/eks-patterns.md +79 -0
  83. package/skills/deploy-k8s/references/conditional/gitops-controllers.md +71 -0
  84. package/skills/deploy-k8s/references/conditional/gke-patterns.md +74 -0
  85. package/skills/deploy-k8s/references/conditional/observability-stacks.md +80 -0
  86. package/skills/deploy-k8s/references/conditional/openshift-patterns.md +67 -0
  87. package/skills/deploy-k8s/references/daemonset-operator-patterns.md +155 -0
  88. package/skills/deploy-k8s/references/deployment-patterns.md +146 -0
  89. package/skills/deploy-k8s/references/do-dont-patterns.md +87 -0
  90. package/skills/deploy-k8s/references/examples-bad.md +282 -0
  91. package/skills/deploy-k8s/references/examples-good.md +440 -0
  92. package/skills/deploy-k8s/references/fragile-rollouts.md +303 -0
  93. package/skills/deploy-k8s/references/helm-patterns.md +203 -0
  94. package/skills/deploy-k8s/references/insecure-workload-defaults.md +300 -0
  95. package/skills/deploy-k8s/references/job-patterns.md +120 -0
  96. package/skills/deploy-k8s/references/kustomize-patterns.md +239 -0
  97. package/skills/deploy-k8s/references/multi-tenancy.md +343 -0
  98. package/skills/deploy-k8s/references/network-exposure.md +481 -0
  99. package/skills/deploy-k8s/references/observability.md +302 -0
  100. package/skills/deploy-k8s/references/privilege-sprawl.md +273 -0
  101. package/skills/deploy-k8s/references/resource-starvation.md +374 -0
  102. package/skills/deploy-k8s/references/security-hardening.md +209 -0
  103. package/skills/deploy-k8s/references/stateful-patterns.md +130 -0
  104. package/skills/deploy-k8s/references/storage-and-state.md +330 -0
  105. package/skills/deploy-k8s/references/validation-and-policy.md +242 -0
  106. package/skills/deploy-railway/SKILL.md +235 -0
  107. package/skills/deploy-railway/references/analyze-db-mongo.md +84 -0
  108. package/skills/deploy-railway/references/analyze-db-mysql.md +254 -0
  109. package/skills/deploy-railway/references/analyze-db-postgres.md +479 -0
  110. package/skills/deploy-railway/references/analyze-db-redis.md +208 -0
  111. package/skills/deploy-railway/references/analyze-db.md +344 -0
  112. package/skills/deploy-railway/references/configure.md +309 -0
  113. package/skills/deploy-railway/references/deploy.md +195 -0
  114. package/skills/deploy-railway/references/operate.md +214 -0
  115. package/skills/deploy-railway/references/request.md +248 -0
  116. package/skills/deploy-railway/references/setup.md +312 -0
  117. package/skills/deploy-railway/scripts/analyze-mongo.py +1549 -0
  118. package/skills/deploy-railway/scripts/analyze-mysql.py +1195 -0
  119. package/skills/deploy-railway/scripts/analyze-postgres.py +3058 -0
  120. package/skills/deploy-railway/scripts/analyze-redis.py +1090 -0
  121. package/skills/deploy-railway/scripts/dal.py +671 -0
  122. package/skills/deploy-railway/scripts/enable-pg-stats.py +170 -0
  123. package/skills/deploy-railway/scripts/pg-extensions.py +370 -0
  124. package/skills/deploy-railway/scripts/railway-api.sh +52 -0
  125. package/skills/deploy-ssh/SKILL.md +91 -0
  126. package/skills/deploy-vercel/SKILL.md +304 -0
  127. package/skills/deploy-vercel/resources/deploy-codex.sh +301 -0
  128. package/skills/deploy-vercel/resources/deploy.sh +301 -0
  129. package/skills/docs-runbooks/SKILL.md +399 -0
  130. package/skills/drive-status-renderer/SKILL.md +62 -0
  131. package/skills/iac-scan/SKILL.md +680 -0
  132. package/skills/iac-scan/assets/.gitkeep +9 -0
  133. package/skills/iac-scan/assets/checkov_config.yaml +94 -0
  134. package/skills/iac-scan/assets/github_actions.yml +199 -0
  135. package/skills/iac-scan/assets/gitlab_ci.yml +218 -0
  136. package/skills/iac-scan/assets/pre_commit_config.yaml +92 -0
  137. package/skills/iac-scan/references/EXAMPLE.md +40 -0
  138. package/skills/iac-scan/references/compliance_mapping.md +237 -0
  139. package/skills/iac-scan/references/custom_policies.md +460 -0
  140. package/skills/iac-scan/references/suppression_guide.md +431 -0
  141. package/skills/incident-briefing/SKILL.md +66 -0
  142. package/skills/incident-triage/SKILL.md +481 -0
  143. package/{LICENSE → skills/mcp-builder/LICENSE.txt} +15 -14
  144. package/skills/mcp-builder/SKILL.md +244 -0
  145. package/skills/mcp-builder/reference/evaluation.md +602 -0
  146. package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  147. package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  148. package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  149. package/skills/mcp-builder/scripts/connections.py +151 -0
  150. package/skills/mcp-builder/scripts/evaluation.py +373 -0
  151. package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  152. package/skills/mcp-builder/scripts/requirements.txt +2 -0
  153. package/skills/mobile-pairing/SKILL.md +52 -0
  154. package/skills/ops-sre/SKILL.md +297 -0
  155. package/skills/playwright-qa/LICENSE.txt +201 -0
  156. package/skills/playwright-qa/NOTICE.txt +14 -0
  157. package/skills/playwright-qa/SKILL.md +156 -0
  158. package/skills/playwright-qa/agents/openai.yaml +6 -0
  159. package/skills/playwright-qa/assets/playwright-small.svg +3 -0
  160. package/skills/playwright-qa/assets/playwright.png +0 -0
  161. package/skills/playwright-qa/references/cli.md +116 -0
  162. package/skills/playwright-qa/references/workflows.md +95 -0
  163. package/skills/playwright-qa/scripts/playwright_cli.sh +25 -0
  164. package/skills/release-publish/SKILL.md +85 -0
  165. package/skills/repo-bootstrap/SKILL.md +92 -0
  166. package/skills/repo-bootstrap/assets/example-workflows/validate-agents.yml +89 -0
  167. package/skills/repo-bootstrap/assets/root-thin.md +141 -0
  168. package/skills/repo-bootstrap/assets/root-verbose.md +149 -0
  169. package/skills/repo-bootstrap/assets/scoped/backend-go.md +107 -0
  170. package/skills/repo-bootstrap/assets/scoped/backend-php.md +94 -0
  171. package/skills/repo-bootstrap/assets/scoped/backend-python.md +84 -0
  172. package/skills/repo-bootstrap/assets/scoped/backend-typescript.md +89 -0
  173. package/skills/repo-bootstrap/assets/scoped/claude-code-skill.md +101 -0
  174. package/skills/repo-bootstrap/assets/scoped/cli.md +83 -0
  175. package/skills/repo-bootstrap/assets/scoped/concourse.md +196 -0
  176. package/skills/repo-bootstrap/assets/scoped/ddev.md +68 -0
  177. package/skills/repo-bootstrap/assets/scoped/docker.md +160 -0
  178. package/skills/repo-bootstrap/assets/scoped/documentation.md +98 -0
  179. package/skills/repo-bootstrap/assets/scoped/examples.md +96 -0
  180. package/skills/repo-bootstrap/assets/scoped/frontend-typescript.md +88 -0
  181. package/skills/repo-bootstrap/assets/scoped/github-actions.md +174 -0
  182. package/skills/repo-bootstrap/assets/scoped/gitlab-ci.md +174 -0
  183. package/skills/repo-bootstrap/assets/scoped/oro-bundle.md +209 -0
  184. package/skills/repo-bootstrap/assets/scoped/oro-project.md +170 -0
  185. package/skills/repo-bootstrap/assets/scoped/python-modern.md +170 -0
  186. package/skills/repo-bootstrap/assets/scoped/resources.md +96 -0
  187. package/skills/repo-bootstrap/assets/scoped/skill-repo.md +139 -0
  188. package/skills/repo-bootstrap/assets/scoped/symfony.md +168 -0
  189. package/skills/repo-bootstrap/assets/scoped/testing.md +87 -0
  190. package/skills/repo-bootstrap/assets/scoped/typo3-docs.md +103 -0
  191. package/skills/repo-bootstrap/assets/scoped/typo3-extension.md +133 -0
  192. package/skills/repo-bootstrap/assets/scoped/typo3-project.md +137 -0
  193. package/skills/repo-bootstrap/assets/scoped/typo3-testing.md +80 -0
  194. package/skills/repo-bootstrap/checkpoints.yaml +279 -0
  195. package/skills/repo-bootstrap/evals/evals.json +385 -0
  196. package/skills/repo-bootstrap/references/ai-contribution-guidelines.md +63 -0
  197. package/skills/repo-bootstrap/references/ai-tool-compatibility.md +223 -0
  198. package/skills/repo-bootstrap/references/directory-coverage.md +82 -0
  199. package/skills/repo-bootstrap/references/examples/coding-agent-cli/AGENTS.md +70 -0
  200. package/skills/repo-bootstrap/references/examples/coding-agent-cli/go.mod +3 -0
  201. package/skills/repo-bootstrap/references/examples/coding-agent-cli/scripts-AGENTS.md +389 -0
  202. package/skills/repo-bootstrap/references/examples/express-api-ts/.env.example +13 -0
  203. package/skills/repo-bootstrap/references/examples/express-api-ts/AGENTS.md +91 -0
  204. package/skills/repo-bootstrap/references/examples/express-api-ts/package.json +33 -0
  205. package/skills/repo-bootstrap/references/examples/express-api-ts/pnpm-lock.yaml +3 -0
  206. package/skills/repo-bootstrap/references/examples/express-api-ts/src/AGENTS.md +91 -0
  207. package/skills/repo-bootstrap/references/examples/express-api-ts/src/config.ts +28 -0
  208. package/skills/repo-bootstrap/references/examples/express-api-ts/src/controllers/userController.ts +74 -0
  209. package/skills/repo-bootstrap/references/examples/express-api-ts/src/index.ts +26 -0
  210. package/skills/repo-bootstrap/references/examples/express-api-ts/src/middleware/errorHandler.ts +45 -0
  211. package/skills/repo-bootstrap/references/examples/express-api-ts/src/middleware/requestLogger.ts +18 -0
  212. package/skills/repo-bootstrap/references/examples/express-api-ts/src/routes/health.ts +18 -0
  213. package/skills/repo-bootstrap/references/examples/express-api-ts/src/routes/users.ts +13 -0
  214. package/skills/repo-bootstrap/references/examples/express-api-ts/src/utils/errors.ts +40 -0
  215. package/skills/repo-bootstrap/references/examples/express-api-ts/src/utils/logger.ts +14 -0
  216. package/skills/repo-bootstrap/references/examples/express-api-ts/tsconfig.json +24 -0
  217. package/skills/repo-bootstrap/references/examples/fastapi-app/.env.example +19 -0
  218. package/skills/repo-bootstrap/references/examples/fastapi-app/AGENTS.md +92 -0
  219. package/skills/repo-bootstrap/references/examples/fastapi-app/pyproject.toml +88 -0
  220. package/skills/repo-bootstrap/references/examples/fastapi-app/src/AGENTS.md +85 -0
  221. package/skills/repo-bootstrap/references/examples/fastapi-app/src/__init__.py +3 -0
  222. package/skills/repo-bootstrap/references/examples/fastapi-app/src/config.py +49 -0
  223. package/skills/repo-bootstrap/references/examples/fastapi-app/src/main.py +66 -0
  224. package/skills/repo-bootstrap/references/examples/fastapi-app/src/models/__init__.py +13 -0
  225. package/skills/repo-bootstrap/references/examples/fastapi-app/src/models/item.py +43 -0
  226. package/skills/repo-bootstrap/references/examples/fastapi-app/src/models/user.py +40 -0
  227. package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/__init__.py +5 -0
  228. package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/health.py +20 -0
  229. package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/items.py +61 -0
  230. package/skills/repo-bootstrap/references/examples/fastapi-app/src/routes/users.py +55 -0
  231. package/skills/repo-bootstrap/references/examples/fastapi-app/src/services/__init__.py +6 -0
  232. package/skills/repo-bootstrap/references/examples/fastapi-app/src/services/item_service.py +77 -0
  233. package/skills/repo-bootstrap/references/examples/fastapi-app/src/services/user_service.py +69 -0
  234. package/skills/repo-bootstrap/references/examples/fastapi-app/uv.lock +4 -0
  235. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/.scopes +3 -0
  236. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/AGENTS.md +86 -0
  237. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/admin/package.json +20 -0
  238. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/admin/src/App.tsx +5 -0
  239. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/cmd/api/main.go +7 -0
  240. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/go.mod +2 -0
  241. package/skills/repo-bootstrap/references/examples/go-api-with-react-admin/main.go +7 -0
  242. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/.scopes +3 -0
  243. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/AGENTS.md +89 -0
  244. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/go.mod +2 -0
  245. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/AGENTS.md +90 -0
  246. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/package.json +17 -0
  247. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/App.tsx +1 -0
  248. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Button.tsx +1 -0
  249. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Footer.tsx +1 -0
  250. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Header.tsx +1 -0
  251. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/internal/web/src/Sidebar.tsx +1 -0
  252. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/main.go +7 -0
  253. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/package-lock.json +0 -0
  254. package/skills/repo-bootstrap/references/examples/go-with-internal-web-tsx/package.json +12 -0
  255. package/skills/repo-bootstrap/references/examples/ldap-selfservice/AGENTS.md +70 -0
  256. package/skills/repo-bootstrap/references/examples/ldap-selfservice/go.mod +3 -0
  257. package/skills/repo-bootstrap/references/examples/ldap-selfservice/internal-AGENTS.md +371 -0
  258. package/skills/repo-bootstrap/references/examples/ldap-selfservice/internal-web-AGENTS.md +448 -0
  259. package/skills/repo-bootstrap/references/examples/php-with-frontend/.scopes +3 -0
  260. package/skills/repo-bootstrap/references/examples/php-with-frontend/AGENTS.md +91 -0
  261. package/skills/repo-bootstrap/references/examples/php-with-frontend/composer.json +8 -0
  262. package/skills/repo-bootstrap/references/examples/php-with-frontend/package.json +15 -0
  263. package/skills/repo-bootstrap/references/examples/php-with-frontend/pnpm-lock.yaml +0 -0
  264. package/skills/repo-bootstrap/references/examples/php-with-frontend/src/Controller.php +3 -0
  265. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/AGENTS.md +92 -0
  266. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/package.json +26 -0
  267. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/App.tsx +3 -0
  268. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/Button.tsx +10 -0
  269. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/Footer.tsx +9 -0
  270. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/Header.tsx +9 -0
  271. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/src/main.tsx +3 -0
  272. package/skills/repo-bootstrap/references/examples/php-with-frontend/web/tsconfig.json +13 -0
  273. package/skills/repo-bootstrap/references/examples/pnpm-workspace/AGENTS.md +75 -0
  274. package/skills/repo-bootstrap/references/examples/pnpm-workspace/package.json +7 -0
  275. package/skills/repo-bootstrap/references/examples/pnpm-workspace/packages/web/package.json +11 -0
  276. package/skills/repo-bootstrap/references/examples/pnpm-workspace/packages/web/src/index.ts +11 -0
  277. package/skills/repo-bootstrap/references/examples/pnpm-workspace/pnpm-lock.yaml +42 -0
  278. package/skills/repo-bootstrap/references/examples/pnpm-workspace/pnpm-workspace.yaml +2 -0
  279. package/skills/repo-bootstrap/references/examples/simple-ldap-go/AGENTS.md +70 -0
  280. package/skills/repo-bootstrap/references/examples/simple-ldap-go/examples-AGENTS.md +45 -0
  281. package/skills/repo-bootstrap/references/examples/simple-ldap-go/go.mod +3 -0
  282. package/skills/repo-bootstrap/references/examples/t3x-rte-ckeditor-image/AGENTS.md +70 -0
  283. package/skills/repo-bootstrap/references/examples/t3x-rte-ckeditor-image/Classes-AGENTS.md +392 -0
  284. package/skills/repo-bootstrap/references/examples/t3x-rte-ckeditor-image/composer.json +8 -0
  285. package/skills/repo-bootstrap/references/feedback-memory-schema.md +135 -0
  286. package/skills/repo-bootstrap/references/git-hooks-setup.md +79 -0
  287. package/skills/repo-bootstrap/references/output-structure.md +124 -0
  288. package/skills/repo-bootstrap/references/scripts-guide.md +175 -0
  289. package/skills/repo-bootstrap/references/verification-guide.md +137 -0
  290. package/skills/repo-bootstrap/scripts/analyze-git-history.sh +315 -0
  291. package/skills/repo-bootstrap/scripts/check-freshness.sh +230 -0
  292. package/skills/repo-bootstrap/scripts/detect-golden-samples.sh +161 -0
  293. package/skills/repo-bootstrap/scripts/detect-heuristics.sh +93 -0
  294. package/skills/repo-bootstrap/scripts/detect-project.sh +486 -0
  295. package/skills/repo-bootstrap/scripts/detect-scopes.sh +330 -0
  296. package/skills/repo-bootstrap/scripts/detect-utilities.sh +133 -0
  297. package/skills/repo-bootstrap/scripts/extract-adrs.sh +194 -0
  298. package/skills/repo-bootstrap/scripts/extract-agent-configs.sh +331 -0
  299. package/skills/repo-bootstrap/scripts/extract-architecture-rules.sh +522 -0
  300. package/skills/repo-bootstrap/scripts/extract-ci-commands.sh +385 -0
  301. package/skills/repo-bootstrap/scripts/extract-ci-rules.sh +384 -0
  302. package/skills/repo-bootstrap/scripts/extract-commands.sh +358 -0
  303. package/skills/repo-bootstrap/scripts/extract-documentation.sh +308 -0
  304. package/skills/repo-bootstrap/scripts/extract-github-rulesets.sh +96 -0
  305. package/skills/repo-bootstrap/scripts/extract-github-settings.sh +88 -0
  306. package/skills/repo-bootstrap/scripts/extract-ide-settings.sh +228 -0
  307. package/skills/repo-bootstrap/scripts/extract-platform-files.sh +290 -0
  308. package/skills/repo-bootstrap/scripts/extract-quality-configs.sh +442 -0
  309. package/skills/repo-bootstrap/scripts/generate-agents.sh +2424 -0
  310. package/skills/repo-bootstrap/scripts/generate-file-map.sh +153 -0
  311. package/skills/repo-bootstrap/scripts/lib/config-root.sh +211 -0
  312. package/skills/repo-bootstrap/scripts/lib/summary.sh +244 -0
  313. package/skills/repo-bootstrap/scripts/lib/template.sh +397 -0
  314. package/skills/repo-bootstrap/scripts/validate-structure.sh +324 -0
  315. package/skills/repo-bootstrap/scripts/verify-commands.sh +615 -0
  316. package/skills/repo-bootstrap/scripts/verify-content.sh +302 -0
  317. package/skills/schema-api-contracts/SKILL.md +56 -0
  318. package/skills/secret-hygiene/SKILL.md +511 -0
  319. package/skills/secret-hygiene/assets/.gitkeep +9 -0
  320. package/skills/secret-hygiene/assets/config-balanced.toml +81 -0
  321. package/skills/secret-hygiene/assets/config-custom.toml +178 -0
  322. package/skills/secret-hygiene/assets/config-strict.toml +48 -0
  323. package/skills/secret-hygiene/assets/github-action.yml +181 -0
  324. package/skills/secret-hygiene/assets/gitlab-ci.yml +257 -0
  325. package/skills/secret-hygiene/assets/precommit-config.yaml +70 -0
  326. package/skills/secret-hygiene/references/EXAMPLE.md +40 -0
  327. package/skills/secret-hygiene/references/compliance_mapping.md +538 -0
  328. package/skills/secret-hygiene/references/detection_rules.md +276 -0
  329. package/skills/secret-hygiene/references/false_positives.md +598 -0
  330. package/skills/secret-hygiene/references/remediation_guide.md +530 -0
  331. package/skills/stack-selector/SKILL.md +56 -0
  332. package/skills/telegram-control/SKILL.md +110 -0
  333. package/skills/telegram-control/references/architecture.md +184 -0
  334. package/skills/telegram-control/references/convex.md +173 -0
  335. package/skills/telegram-control/references/error_handling.md +212 -0
  336. package/skills/telegram-control/references/initial_setup.md +165 -0
  337. package/skills/telegram-control/references/telegram_api.md +156 -0
  338. package/skills/telegram-control/scripts/cancel_message.ts +53 -0
  339. package/skills/telegram-control/scripts/list_scheduled.ts +103 -0
  340. package/skills/telegram-control/scripts/logger.ts +121 -0
  341. package/skills/telegram-control/scripts/proxy-util.ts +11 -0
  342. package/skills/telegram-control/scripts/schedule_message.ts +216 -0
  343. package/skills/telegram-control/scripts/send_message.ts +115 -0
  344. package/skills/telegram-control/scripts/setup.ts +185 -0
  345. package/skills/telegram-control/scripts/types.ts +75 -0
  346. package/skills/telegram-control/scripts/view_history.ts +74 -0
  347. package/skills/test-strategy/SKILL.md +352 -0
  348. package/skills/threat-model/SKILL.md +303 -0
  349. package/skills/threat-model/examples/example-output.md +196 -0
  350. package/skills/threat-model/template.md +96 -0
  351. package/skills/ts-lint/SKILL.md +80 -0
  352. package/skills/ui-flow/SKILL.md +668 -0
  353. package/skills/voice-command-router/SKILL.md +51 -0
  354. 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