aidevops 2.52.1 → 2.53.1

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 (334) hide show
  1. package/README.md +1 -1
  2. package/VERSION +1 -1
  3. package/aidevops.sh +15 -9
  4. package/package.json +4 -4
  5. package/scripts/npm-postinstall.js +6 -7
  6. package/setup.sh +1 -1
  7. package/templates/deploy-templates.sh +144 -0
  8. package/templates/home/.agent/README.md +33 -0
  9. package/templates/home/AGENTS.md +96 -0
  10. package/templates/home/git/.agent/README.md +48 -0
  11. package/templates/home/git/AGENTS.md +97 -0
  12. package/templates/standard-functions.sh +179 -0
  13. package/templates/wordpress-performance-workflow.md +217 -0
  14. package/.agent/AGENTS.md +0 -614
  15. package/.agent/accounts.md +0 -65
  16. package/.agent/aidevops/add-new-mcp-to-aidevops.md +0 -456
  17. package/.agent/aidevops/api-integrations.md +0 -335
  18. package/.agent/aidevops/architecture.md +0 -510
  19. package/.agent/aidevops/configs.md +0 -274
  20. package/.agent/aidevops/docs.md +0 -244
  21. package/.agent/aidevops/extension.md +0 -311
  22. package/.agent/aidevops/mcp-integrations.md +0 -340
  23. package/.agent/aidevops/mcp-troubleshooting.md +0 -162
  24. package/.agent/aidevops/memory-patterns.md +0 -172
  25. package/.agent/aidevops/providers.md +0 -217
  26. package/.agent/aidevops/recommendations.md +0 -321
  27. package/.agent/aidevops/requirements.md +0 -301
  28. package/.agent/aidevops/resources.md +0 -214
  29. package/.agent/aidevops/security-requirements.md +0 -174
  30. package/.agent/aidevops/security.md +0 -350
  31. package/.agent/aidevops/service-links.md +0 -400
  32. package/.agent/aidevops/services.md +0 -357
  33. package/.agent/aidevops/setup.md +0 -153
  34. package/.agent/aidevops/troubleshooting.md +0 -389
  35. package/.agent/aidevops.md +0 -124
  36. package/.agent/build-plus.md +0 -244
  37. package/.agent/content/guidelines.md +0 -109
  38. package/.agent/content.md +0 -87
  39. package/.agent/health.md +0 -59
  40. package/.agent/legal.md +0 -59
  41. package/.agent/loop-state/full-loop.local.md +0 -16
  42. package/.agent/loop-state/ralph-loop.local.md +0 -10
  43. package/.agent/marketing.md +0 -440
  44. package/.agent/memory/README.md +0 -260
  45. package/.agent/onboarding.md +0 -796
  46. package/.agent/plan-plus.md +0 -245
  47. package/.agent/research.md +0 -100
  48. package/.agent/sales.md +0 -333
  49. package/.agent/scripts/101domains-helper.sh +0 -701
  50. package/.agent/scripts/add-missing-returns.sh +0 -140
  51. package/.agent/scripts/agent-browser-helper.sh +0 -311
  52. package/.agent/scripts/agno-setup.sh +0 -712
  53. package/.agent/scripts/ahrefs-mcp-wrapper.js +0 -168
  54. package/.agent/scripts/aidevops-update-check.sh +0 -71
  55. package/.agent/scripts/ampcode-cli.sh +0 -522
  56. package/.agent/scripts/auto-version-bump.sh +0 -156
  57. package/.agent/scripts/autogen-helper.sh +0 -512
  58. package/.agent/scripts/beads-sync-helper.sh +0 -596
  59. package/.agent/scripts/closte-helper.sh +0 -5
  60. package/.agent/scripts/cloudron-helper.sh +0 -321
  61. package/.agent/scripts/codacy-cli-chunked.sh +0 -581
  62. package/.agent/scripts/codacy-cli.sh +0 -442
  63. package/.agent/scripts/code-audit-helper.sh +0 -5
  64. package/.agent/scripts/coderabbit-cli.sh +0 -417
  65. package/.agent/scripts/coderabbit-pro-analysis.sh +0 -238
  66. package/.agent/scripts/commands/code-simplifier.md +0 -86
  67. package/.agent/scripts/commands/full-loop.md +0 -246
  68. package/.agent/scripts/commands/postflight-loop.md +0 -103
  69. package/.agent/scripts/commands/recall.md +0 -182
  70. package/.agent/scripts/commands/remember.md +0 -132
  71. package/.agent/scripts/commands/save-todo.md +0 -175
  72. package/.agent/scripts/commands/session-review.md +0 -154
  73. package/.agent/scripts/comprehensive-quality-fix.sh +0 -106
  74. package/.agent/scripts/context-builder-helper.sh +0 -522
  75. package/.agent/scripts/coolify-cli-helper.sh +0 -674
  76. package/.agent/scripts/coolify-helper.sh +0 -380
  77. package/.agent/scripts/crawl4ai-examples.sh +0 -401
  78. package/.agent/scripts/crawl4ai-helper.sh +0 -1078
  79. package/.agent/scripts/crewai-helper.sh +0 -681
  80. package/.agent/scripts/dev-browser-helper.sh +0 -513
  81. package/.agent/scripts/dns-helper.sh +0 -396
  82. package/.agent/scripts/domain-research-helper.sh +0 -917
  83. package/.agent/scripts/dspy-helper.sh +0 -285
  84. package/.agent/scripts/dspyground-helper.sh +0 -291
  85. package/.agent/scripts/eeat-score-helper.sh +0 -1242
  86. package/.agent/scripts/efficient-return-fix.sh +0 -92
  87. package/.agent/scripts/extract-opencode-prompts.sh +0 -128
  88. package/.agent/scripts/find-missing-returns.sh +0 -113
  89. package/.agent/scripts/fix-auth-headers.sh +0 -104
  90. package/.agent/scripts/fix-common-strings.sh +0 -254
  91. package/.agent/scripts/fix-content-type.sh +0 -100
  92. package/.agent/scripts/fix-error-messages.sh +0 -130
  93. package/.agent/scripts/fix-misplaced-returns.sh +0 -74
  94. package/.agent/scripts/fix-remaining-literals.sh +0 -152
  95. package/.agent/scripts/fix-return-statements.sh +0 -41
  96. package/.agent/scripts/fix-s131-default-cases.sh +0 -249
  97. package/.agent/scripts/fix-sc2155-simple.sh +0 -102
  98. package/.agent/scripts/fix-shellcheck-critical.sh +0 -187
  99. package/.agent/scripts/fix-string-literals.sh +0 -273
  100. package/.agent/scripts/full-loop-helper.sh +0 -773
  101. package/.agent/scripts/generate-opencode-agents.sh +0 -497
  102. package/.agent/scripts/generate-opencode-commands.sh +0 -1629
  103. package/.agent/scripts/generate-skills.sh +0 -366
  104. package/.agent/scripts/git-platforms-helper.sh +0 -640
  105. package/.agent/scripts/gitea-cli-helper.sh +0 -743
  106. package/.agent/scripts/github-cli-helper.sh +0 -702
  107. package/.agent/scripts/gitlab-cli-helper.sh +0 -682
  108. package/.agent/scripts/gsc-add-user-helper.sh +0 -325
  109. package/.agent/scripts/gsc-sitemap-helper.sh +0 -678
  110. package/.agent/scripts/hetzner-helper.sh +0 -485
  111. package/.agent/scripts/hostinger-helper.sh +0 -229
  112. package/.agent/scripts/keyword-research-helper.sh +0 -1815
  113. package/.agent/scripts/langflow-helper.sh +0 -544
  114. package/.agent/scripts/linkedin-automation.py +0 -241
  115. package/.agent/scripts/linter-manager.sh +0 -599
  116. package/.agent/scripts/linters-local.sh +0 -434
  117. package/.agent/scripts/list-keys-helper.sh +0 -488
  118. package/.agent/scripts/local-browser-automation.py +0 -339
  119. package/.agent/scripts/localhost-helper.sh +0 -744
  120. package/.agent/scripts/loop-common.sh +0 -806
  121. package/.agent/scripts/mainwp-helper.sh +0 -728
  122. package/.agent/scripts/markdown-formatter.sh +0 -338
  123. package/.agent/scripts/markdown-lint-fix.sh +0 -311
  124. package/.agent/scripts/mass-fix-returns.sh +0 -58
  125. package/.agent/scripts/mcp-diagnose.sh +0 -167
  126. package/.agent/scripts/mcp-inspector-helper.sh +0 -449
  127. package/.agent/scripts/memory-helper.sh +0 -650
  128. package/.agent/scripts/monitor-code-review.sh +0 -255
  129. package/.agent/scripts/onboarding-helper.sh +0 -706
  130. package/.agent/scripts/opencode-github-setup-helper.sh +0 -797
  131. package/.agent/scripts/opencode-test-helper.sh +0 -213
  132. package/.agent/scripts/pagespeed-helper.sh +0 -464
  133. package/.agent/scripts/pandoc-helper.sh +0 -362
  134. package/.agent/scripts/postflight-check.sh +0 -555
  135. package/.agent/scripts/pre-commit-hook.sh +0 -259
  136. package/.agent/scripts/pre-edit-check.sh +0 -169
  137. package/.agent/scripts/qlty-cli.sh +0 -356
  138. package/.agent/scripts/quality-cli-manager.sh +0 -525
  139. package/.agent/scripts/quality-feedback-helper.sh +0 -462
  140. package/.agent/scripts/quality-fix.sh +0 -263
  141. package/.agent/scripts/quality-loop-helper.sh +0 -1108
  142. package/.agent/scripts/ralph-loop-helper.sh +0 -836
  143. package/.agent/scripts/ralph-upstream-check.sh +0 -341
  144. package/.agent/scripts/secretlint-helper.sh +0 -847
  145. package/.agent/scripts/servers-helper.sh +0 -241
  146. package/.agent/scripts/ses-helper.sh +0 -619
  147. package/.agent/scripts/session-review-helper.sh +0 -404
  148. package/.agent/scripts/setup-linters-wizard.sh +0 -379
  149. package/.agent/scripts/setup-local-api-keys.sh +0 -330
  150. package/.agent/scripts/setup-mcp-integrations.sh +0 -472
  151. package/.agent/scripts/shared-constants.sh +0 -246
  152. package/.agent/scripts/site-crawler-helper.sh +0 -1487
  153. package/.agent/scripts/snyk-helper.sh +0 -940
  154. package/.agent/scripts/sonarcloud-autofix.sh +0 -193
  155. package/.agent/scripts/sonarcloud-cli.sh +0 -191
  156. package/.agent/scripts/sonarscanner-cli.sh +0 -455
  157. package/.agent/scripts/spaceship-helper.sh +0 -747
  158. package/.agent/scripts/stagehand-helper.sh +0 -321
  159. package/.agent/scripts/stagehand-python-helper.sh +0 -321
  160. package/.agent/scripts/stagehand-python-setup.sh +0 -441
  161. package/.agent/scripts/stagehand-setup.sh +0 -439
  162. package/.agent/scripts/system-cleanup.sh +0 -340
  163. package/.agent/scripts/terminal-title-helper.sh +0 -388
  164. package/.agent/scripts/terminal-title-setup.sh +0 -549
  165. package/.agent/scripts/test-stagehand-both-integration.sh +0 -317
  166. package/.agent/scripts/test-stagehand-integration.sh +0 -309
  167. package/.agent/scripts/test-stagehand-python-integration.sh +0 -341
  168. package/.agent/scripts/todo-ready.sh +0 -263
  169. package/.agent/scripts/tool-version-check.sh +0 -362
  170. package/.agent/scripts/toon-helper.sh +0 -469
  171. package/.agent/scripts/twilio-helper.sh +0 -917
  172. package/.agent/scripts/updown-helper.sh +0 -279
  173. package/.agent/scripts/validate-mcp-integrations.sh +0 -250
  174. package/.agent/scripts/validate-version-consistency.sh +0 -131
  175. package/.agent/scripts/vaultwarden-helper.sh +0 -597
  176. package/.agent/scripts/vercel-cli-helper.sh +0 -816
  177. package/.agent/scripts/verify-mirrors.sh +0 -169
  178. package/.agent/scripts/version-manager.sh +0 -831
  179. package/.agent/scripts/webhosting-helper.sh +0 -471
  180. package/.agent/scripts/webhosting-verify.sh +0 -238
  181. package/.agent/scripts/wordpress-mcp-helper.sh +0 -508
  182. package/.agent/scripts/worktree-helper.sh +0 -595
  183. package/.agent/scripts/worktree-sessions.sh +0 -577
  184. package/.agent/seo/dataforseo.md +0 -215
  185. package/.agent/seo/domain-research.md +0 -532
  186. package/.agent/seo/eeat-score.md +0 -659
  187. package/.agent/seo/google-search-console.md +0 -366
  188. package/.agent/seo/gsc-sitemaps.md +0 -282
  189. package/.agent/seo/keyword-research.md +0 -521
  190. package/.agent/seo/serper.md +0 -278
  191. package/.agent/seo/site-crawler.md +0 -387
  192. package/.agent/seo.md +0 -236
  193. package/.agent/services/accounting/quickfile.md +0 -159
  194. package/.agent/services/communications/telfon.md +0 -470
  195. package/.agent/services/communications/twilio.md +0 -569
  196. package/.agent/services/crm/fluentcrm.md +0 -449
  197. package/.agent/services/email/ses.md +0 -399
  198. package/.agent/services/hosting/101domains.md +0 -378
  199. package/.agent/services/hosting/closte.md +0 -177
  200. package/.agent/services/hosting/cloudflare.md +0 -251
  201. package/.agent/services/hosting/cloudron.md +0 -478
  202. package/.agent/services/hosting/dns-providers.md +0 -335
  203. package/.agent/services/hosting/domain-purchasing.md +0 -344
  204. package/.agent/services/hosting/hetzner.md +0 -327
  205. package/.agent/services/hosting/hostinger.md +0 -287
  206. package/.agent/services/hosting/localhost.md +0 -419
  207. package/.agent/services/hosting/spaceship.md +0 -353
  208. package/.agent/services/hosting/webhosting.md +0 -330
  209. package/.agent/social-media.md +0 -69
  210. package/.agent/templates/plans-template.md +0 -114
  211. package/.agent/templates/prd-template.md +0 -129
  212. package/.agent/templates/tasks-template.md +0 -108
  213. package/.agent/templates/todo-template.md +0 -89
  214. package/.agent/tools/ai-assistants/agno.md +0 -471
  215. package/.agent/tools/ai-assistants/capsolver.md +0 -326
  216. package/.agent/tools/ai-assistants/configuration.md +0 -221
  217. package/.agent/tools/ai-assistants/overview.md +0 -209
  218. package/.agent/tools/ai-assistants/status.md +0 -171
  219. package/.agent/tools/ai-assistants/windsurf.md +0 -193
  220. package/.agent/tools/ai-orchestration/autogen.md +0 -406
  221. package/.agent/tools/ai-orchestration/crewai.md +0 -445
  222. package/.agent/tools/ai-orchestration/langflow.md +0 -405
  223. package/.agent/tools/ai-orchestration/openprose.md +0 -487
  224. package/.agent/tools/ai-orchestration/overview.md +0 -362
  225. package/.agent/tools/ai-orchestration/packaging.md +0 -647
  226. package/.agent/tools/browser/agent-browser.md +0 -464
  227. package/.agent/tools/browser/browser-automation.md +0 -400
  228. package/.agent/tools/browser/chrome-devtools.md +0 -282
  229. package/.agent/tools/browser/crawl4ai-integration.md +0 -422
  230. package/.agent/tools/browser/crawl4ai-resources.md +0 -277
  231. package/.agent/tools/browser/crawl4ai-usage.md +0 -416
  232. package/.agent/tools/browser/crawl4ai.md +0 -585
  233. package/.agent/tools/browser/dev-browser.md +0 -341
  234. package/.agent/tools/browser/pagespeed.md +0 -260
  235. package/.agent/tools/browser/playwright.md +0 -266
  236. package/.agent/tools/browser/playwriter.md +0 -310
  237. package/.agent/tools/browser/stagehand-examples.md +0 -456
  238. package/.agent/tools/browser/stagehand-python.md +0 -483
  239. package/.agent/tools/browser/stagehand.md +0 -421
  240. package/.agent/tools/build-agent/agent-review.md +0 -224
  241. package/.agent/tools/build-agent/build-agent.md +0 -784
  242. package/.agent/tools/build-mcp/aidevops-plugin.md +0 -476
  243. package/.agent/tools/build-mcp/api-wrapper.md +0 -445
  244. package/.agent/tools/build-mcp/build-mcp.md +0 -240
  245. package/.agent/tools/build-mcp/deployment.md +0 -401
  246. package/.agent/tools/build-mcp/server-patterns.md +0 -632
  247. package/.agent/tools/build-mcp/transports.md +0 -366
  248. package/.agent/tools/code-review/auditing.md +0 -383
  249. package/.agent/tools/code-review/automation.md +0 -219
  250. package/.agent/tools/code-review/best-practices.md +0 -203
  251. package/.agent/tools/code-review/codacy.md +0 -151
  252. package/.agent/tools/code-review/code-simplifier.md +0 -174
  253. package/.agent/tools/code-review/code-standards.md +0 -309
  254. package/.agent/tools/code-review/coderabbit.md +0 -101
  255. package/.agent/tools/code-review/management.md +0 -155
  256. package/.agent/tools/code-review/qlty.md +0 -248
  257. package/.agent/tools/code-review/secretlint.md +0 -565
  258. package/.agent/tools/code-review/setup.md +0 -250
  259. package/.agent/tools/code-review/snyk.md +0 -563
  260. package/.agent/tools/code-review/tools.md +0 -230
  261. package/.agent/tools/content/summarize.md +0 -353
  262. package/.agent/tools/context/augment-context-engine.md +0 -468
  263. package/.agent/tools/context/context-builder-agent.md +0 -76
  264. package/.agent/tools/context/context-builder.md +0 -375
  265. package/.agent/tools/context/context7.md +0 -371
  266. package/.agent/tools/context/dspy.md +0 -302
  267. package/.agent/tools/context/dspyground.md +0 -374
  268. package/.agent/tools/context/llm-tldr.md +0 -219
  269. package/.agent/tools/context/osgrep.md +0 -488
  270. package/.agent/tools/context/prompt-optimization.md +0 -338
  271. package/.agent/tools/context/toon.md +0 -292
  272. package/.agent/tools/conversion/pandoc.md +0 -304
  273. package/.agent/tools/credentials/api-key-management.md +0 -154
  274. package/.agent/tools/credentials/api-key-setup.md +0 -224
  275. package/.agent/tools/credentials/environment-variables.md +0 -180
  276. package/.agent/tools/credentials/vaultwarden.md +0 -382
  277. package/.agent/tools/data-extraction/outscraper.md +0 -974
  278. package/.agent/tools/deployment/coolify-cli.md +0 -388
  279. package/.agent/tools/deployment/coolify-setup.md +0 -353
  280. package/.agent/tools/deployment/coolify.md +0 -345
  281. package/.agent/tools/deployment/vercel.md +0 -390
  282. package/.agent/tools/git/authentication.md +0 -132
  283. package/.agent/tools/git/gitea-cli.md +0 -193
  284. package/.agent/tools/git/github-actions.md +0 -207
  285. package/.agent/tools/git/github-cli.md +0 -223
  286. package/.agent/tools/git/gitlab-cli.md +0 -190
  287. package/.agent/tools/git/opencode-github-security.md +0 -350
  288. package/.agent/tools/git/opencode-github.md +0 -328
  289. package/.agent/tools/git/opencode-gitlab.md +0 -252
  290. package/.agent/tools/git/security.md +0 -196
  291. package/.agent/tools/git.md +0 -207
  292. package/.agent/tools/opencode/oh-my-opencode.md +0 -375
  293. package/.agent/tools/opencode/opencode-anthropic-auth.md +0 -446
  294. package/.agent/tools/opencode/opencode.md +0 -651
  295. package/.agent/tools/social-media/bird.md +0 -437
  296. package/.agent/tools/task-management/beads.md +0 -336
  297. package/.agent/tools/terminal/terminal-title.md +0 -251
  298. package/.agent/tools/ui/shadcn.md +0 -196
  299. package/.agent/tools/ui/ui-skills.md +0 -115
  300. package/.agent/tools/wordpress/localwp.md +0 -311
  301. package/.agent/tools/wordpress/mainwp.md +0 -391
  302. package/.agent/tools/wordpress/scf.md +0 -527
  303. package/.agent/tools/wordpress/wp-admin.md +0 -729
  304. package/.agent/tools/wordpress/wp-dev.md +0 -940
  305. package/.agent/tools/wordpress/wp-preferred.md +0 -398
  306. package/.agent/tools/wordpress.md +0 -95
  307. package/.agent/workflows/branch/bugfix.md +0 -63
  308. package/.agent/workflows/branch/chore.md +0 -95
  309. package/.agent/workflows/branch/experiment.md +0 -115
  310. package/.agent/workflows/branch/feature.md +0 -59
  311. package/.agent/workflows/branch/hotfix.md +0 -98
  312. package/.agent/workflows/branch/refactor.md +0 -92
  313. package/.agent/workflows/branch/release.md +0 -96
  314. package/.agent/workflows/branch.md +0 -347
  315. package/.agent/workflows/bug-fixing.md +0 -267
  316. package/.agent/workflows/changelog.md +0 -129
  317. package/.agent/workflows/code-audit-remote.md +0 -279
  318. package/.agent/workflows/conversation-starter.md +0 -69
  319. package/.agent/workflows/error-feedback.md +0 -578
  320. package/.agent/workflows/feature-development.md +0 -355
  321. package/.agent/workflows/git-workflow.md +0 -702
  322. package/.agent/workflows/multi-repo-workspace.md +0 -268
  323. package/.agent/workflows/plans.md +0 -709
  324. package/.agent/workflows/postflight.md +0 -604
  325. package/.agent/workflows/pr.md +0 -571
  326. package/.agent/workflows/preflight.md +0 -278
  327. package/.agent/workflows/ralph-loop.md +0 -773
  328. package/.agent/workflows/release.md +0 -498
  329. package/.agent/workflows/session-manager.md +0 -254
  330. package/.agent/workflows/session-review.md +0 -311
  331. package/.agent/workflows/sql-migrations.md +0 -631
  332. package/.agent/workflows/version-bump.md +0 -283
  333. package/.agent/workflows/wiki-update.md +0 -333
  334. package/.agent/workflows/worktree.md +0 -477
@@ -1,1108 +0,0 @@
1
- #!/bin/bash
2
- # =============================================================================
3
- # Quality Loop Helper - Iterative Quality Workflows
4
- # =============================================================================
5
- # Applies Ralph Wiggum technique to preflight, PR review, and postflight.
6
- # Loops until quality checks pass or max iterations reached.
7
- #
8
- # Usage:
9
- # quality-loop-helper.sh preflight [--auto-fix] [--max-iterations N]
10
- # quality-loop-helper.sh pr-review [--pr N] [--wait-for-ci] [--max-iterations N] [--no-auto-trigger]
11
- # quality-loop-helper.sh postflight [--monitor-duration Nm]
12
- # quality-loop-helper.sh status
13
- # quality-loop-helper.sh cancel
14
- #
15
- # PR Review Options:
16
- # --pr N PR number (auto-detects from current branch if omitted)
17
- # --wait-for-ci Wait for CI checks to complete before checking review status
18
- # --max-iterations N Maximum check iterations (default: 10)
19
- # --no-auto-trigger Disable automatic re-review trigger for stale reviews
20
- # --auto-trigger-review Enable auto re-review (default, triggers @coderabbitai review
21
- # if no review received within 5 minutes of last push)
22
- #
23
- # Author: AI DevOps Framework
24
- # =============================================================================
25
-
26
- set -euo pipefail
27
-
28
- # =============================================================================
29
- # Constants
30
- # =============================================================================
31
-
32
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" || exit
33
- readonly SCRIPT_DIR
34
- readonly STATE_DIR=".agent/loop-state"
35
- readonly STATE_FILE="${STATE_DIR}/quality-loop.local.md"
36
-
37
- # Legacy state directory (for backward compatibility during migration)
38
- # shellcheck disable=SC2034 # Defined for documentation
39
- readonly LEGACY_STATE_DIR=".claude"
40
-
41
- # Default settings
42
- readonly DEFAULT_MAX_ITERATIONS=10
43
- readonly DEFAULT_MONITOR_DURATION=300 # 5 minutes in seconds
44
- readonly DEFAULT_REVIEW_STALE_THRESHOLD=300 # 5 minutes - trigger re-review if no activity
45
-
46
- # =============================================================================
47
- # CI/CD Service Timing Constants (Evidence-Based from PR #19 Analysis)
48
- # =============================================================================
49
- # These timings are based on observed completion times:
50
- # - Fast (CodeFactor, Version): ~1-5s → wait 10s, poll every 5s
51
- # - Medium (SonarCloud, Codacy, Qlty): ~43-62s → wait 60s, poll every 15s
52
- # - Slow (CodeRabbit): ~120-180s → wait 120s, poll every 30s
53
-
54
- # Service categories for intelligent polling
55
- readonly FAST_SERVICES="codefactor|version|framework"
56
- readonly MEDIUM_SERVICES="sonarcloud|codacy|qlty|code-review-monitoring"
57
- readonly SLOW_SERVICES="coderabbit|coderabbitai"
58
-
59
- # Timing constants (seconds)
60
- readonly WAIT_FAST=10
61
- readonly WAIT_MEDIUM=60
62
- readonly WAIT_SLOW=120
63
- readonly POLL_FAST=5
64
- readonly POLL_MEDIUM=15
65
- readonly POLL_SLOW=30
66
-
67
- # Exponential backoff settings
68
- readonly BACKOFF_BASE=15
69
- readonly BACKOFF_MAX=120
70
- readonly BACKOFF_MULTIPLIER=2
71
-
72
- # Colors
73
- readonly RED='\033[0;31m'
74
- readonly GREEN='\033[0;32m'
75
- readonly YELLOW='\033[1;33m'
76
- readonly BLUE='\033[0;34m'
77
- readonly CYAN='\033[0;36m'
78
- readonly NC='\033[0m'
79
-
80
- # =============================================================================
81
- # Helper Functions
82
- # =============================================================================
83
-
84
- # Print error message to stderr with prefix
85
- # Arguments: $1 - Error message
86
- # Returns: 0
87
- print_error() {
88
- local message="$1"
89
- echo -e "${RED}[quality-loop] Error:${NC} ${message}" >&2
90
- return 0
91
- }
92
-
93
- # Print success message in green with prefix
94
- # Arguments: $1 - Success message
95
- # Returns: 0
96
- print_success() {
97
- local message="$1"
98
- echo -e "${GREEN}[quality-loop]${NC} ${message}"
99
- return 0
100
- }
101
-
102
- # Print warning message in yellow with prefix
103
- # Arguments: $1 - Warning message
104
- # Returns: 0
105
- print_warning() {
106
- local message="$1"
107
- echo -e "${YELLOW}[quality-loop]${NC} ${message}"
108
- return 0
109
- }
110
-
111
- # Print info message in blue with prefix
112
- # Arguments: $1 - Info message
113
- # Returns: 0
114
- print_info() {
115
- local message="$1"
116
- echo -e "${BLUE}[quality-loop]${NC} ${message}"
117
- return 0
118
- }
119
-
120
- # Print step message in cyan with prefix
121
- # Arguments: $1 - Step message
122
- # Returns: 0
123
- print_step() {
124
- local message="$1"
125
- echo -e "${CYAN}[quality-loop]${NC} ${message}"
126
- return 0
127
- }
128
-
129
- # =============================================================================
130
- # Adaptive Wait Time Functions
131
- # =============================================================================
132
-
133
- # Calculate wait time based on pending services
134
- # Arguments:
135
- # $1 - Comma-separated list of pending service names (e.g., "coderabbit,sonarcloud")
136
- # Returns: 0
137
- # Output: Recommended wait time in seconds
138
- calculate_adaptive_wait() {
139
- local pending_services="$1"
140
- local max_wait=0
141
-
142
- # Check for slow services first (they dominate wait time)
143
- if echo "$pending_services" | grep -qiE "$SLOW_SERVICES"; then
144
- max_wait=$WAIT_SLOW
145
- elif echo "$pending_services" | grep -qiE "$MEDIUM_SERVICES"; then
146
- max_wait=$WAIT_MEDIUM
147
- elif echo "$pending_services" | grep -qiE "$FAST_SERVICES"; then
148
- max_wait=$WAIT_FAST
149
- else
150
- # Unknown service, use medium as default
151
- max_wait=$WAIT_MEDIUM
152
- fi
153
-
154
- echo "$max_wait"
155
- return 0
156
- }
157
-
158
- # Calculate poll interval based on pending services
159
- # Arguments:
160
- # $1 - Comma-separated list of pending service names
161
- # Returns: 0
162
- # Output: Recommended poll interval in seconds
163
- calculate_poll_interval() {
164
- local pending_services="$1"
165
- local poll_interval=$POLL_MEDIUM
166
-
167
- if echo "$pending_services" | grep -qiE "$SLOW_SERVICES"; then
168
- poll_interval=$POLL_SLOW
169
- elif echo "$pending_services" | grep -qiE "$MEDIUM_SERVICES"; then
170
- poll_interval=$POLL_MEDIUM
171
- elif echo "$pending_services" | grep -qiE "$FAST_SERVICES"; then
172
- poll_interval=$POLL_FAST
173
- fi
174
-
175
- echo "$poll_interval"
176
- return 0
177
- }
178
-
179
- # Calculate exponential backoff wait time
180
- # Arguments:
181
- # $1 - Current iteration number (1-based)
182
- # Returns: 0
183
- # Output: Wait time in seconds (capped at BACKOFF_MAX)
184
- calculate_backoff_wait() {
185
- local iteration="$1"
186
- local wait_time=$BACKOFF_BASE
187
-
188
- # Calculate: base * multiplier^(iteration-1), capped at max
189
- local i=1
190
- while [[ $i -lt $iteration ]]; do
191
- wait_time=$((wait_time * BACKOFF_MULTIPLIER))
192
- if [[ $wait_time -ge $BACKOFF_MAX ]]; then
193
- wait_time=$BACKOFF_MAX
194
- break
195
- fi
196
- ((i++))
197
- done
198
-
199
- echo "$wait_time"
200
- return 0
201
- }
202
-
203
- # Get list of pending CI check names from PR
204
- # Arguments:
205
- # $1 - PR number
206
- # Returns: 0
207
- # Output: Comma-separated list of pending check names (lowercase)
208
- get_pending_checks() {
209
- local pr_number="$1"
210
-
211
- local pr_info
212
- pr_info=$(gh pr view "$pr_number" --json statusCheckRollup 2>/dev/null || echo '{"statusCheckRollup":[]}')
213
-
214
- local pending
215
- pending=$(echo "$pr_info" | jq -r '[.statusCheckRollup[] | select(.status == "PENDING" or .status == "IN_PROGRESS") | .name] | join(",")' 2>/dev/null | tr '[:upper:]' '[:lower:]')
216
-
217
- echo "$pending"
218
- return 0
219
- }
220
-
221
- # =============================================================================
222
- # State Management
223
- # =============================================================================
224
-
225
- # Create state file for a new quality loop
226
- # Arguments:
227
- # $1 - Loop type (preflight, pr-review, postflight)
228
- # $2 - Max iterations
229
- # $3 - Options string
230
- # Returns: 0
231
- # Side effects: Creates .claude/quality-loop.local.md
232
- create_state() {
233
- local loop_type="$1"
234
- local max_iterations="$2"
235
- local options="$3"
236
-
237
- mkdir -p "$STATE_DIR"
238
-
239
- cat > "$STATE_FILE" << EOF
240
- ---
241
- type: $loop_type
242
- iteration: 1
243
- max_iterations: $max_iterations
244
- status: running
245
- started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
246
- options: "$options"
247
- checks_passed: []
248
- checks_failed: []
249
- fixes_applied: 0
250
- ---
251
- EOF
252
- return 0
253
- }
254
-
255
- # Update a field in the state file
256
- # Arguments:
257
- # $1 - Field name
258
- # $2 - New value
259
- # Returns: 0 on success, 1 if no state file
260
- update_state() {
261
- local field="$1"
262
- local value="$2"
263
-
264
- if [[ ! -f "$STATE_FILE" ]]; then
265
- return 1
266
- fi
267
-
268
- local temp_file="${STATE_FILE}.tmp.$$"
269
- sed "s/^${field}: .*/${field}: ${value}/" "$STATE_FILE" > "$temp_file"
270
- mv "$temp_file" "$STATE_FILE"
271
- return 0
272
- }
273
-
274
- # Get a field value from the state file
275
- # Arguments: $1 - Field name
276
- # Returns: 0
277
- # Output: Field value to stdout (empty if not found)
278
- get_state_field() {
279
- local field="$1"
280
-
281
- if [[ ! -f "$STATE_FILE" ]]; then
282
- echo ""
283
- return 0
284
- fi
285
-
286
- local frontmatter
287
- frontmatter=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$STATE_FILE")
288
- echo "$frontmatter" | grep "^${field}:" | sed "s/${field}: *//" | sed 's/^"\(.*\)"$/\1/'
289
- return 0
290
- }
291
-
292
- # Increment iteration counter in state file
293
- # Arguments: none
294
- # Returns: 0
295
- # Output: New iteration number to stdout
296
- increment_iteration() {
297
- local current
298
- current=$(get_state_field "iteration")
299
-
300
- if [[ ! "$current" =~ ^[0-9]+$ ]]; then
301
- current=0
302
- fi
303
-
304
- local next=$((current + 1))
305
- update_state "iteration" "$next"
306
- echo "$next"
307
- return 0
308
- }
309
-
310
- # Increment fixes applied counter in state file
311
- # Arguments: none
312
- # Returns: 0
313
- # Output: New fixes count to stdout
314
- increment_fixes() {
315
- local current
316
- current=$(get_state_field "fixes_applied")
317
-
318
- if [[ ! "$current" =~ ^[0-9]+$ ]]; then
319
- current=0
320
- fi
321
-
322
- local next=$((current + 1))
323
- update_state "fixes_applied" "$next"
324
- echo "$next"
325
- return 0
326
- }
327
-
328
- # Cancel the active quality loop
329
- # Arguments: none
330
- # Returns: 0
331
- # Side effects: Removes state file if exists
332
- cancel_loop() {
333
- if [[ ! -f "$STATE_FILE" ]]; then
334
- print_warning "No active quality loop found."
335
- return 0
336
- fi
337
-
338
- local loop_type
339
- local iteration
340
- loop_type=$(get_state_field "type")
341
- iteration=$(get_state_field "iteration")
342
-
343
- rm -f "$STATE_FILE"
344
- print_success "Cancelled ${loop_type} loop (was at iteration ${iteration})"
345
- return 0
346
- }
347
-
348
- # Display current quality loop status
349
- # Arguments: none
350
- # Returns: 0
351
- # Output: Status information to stdout
352
- show_status() {
353
- if [[ ! -f "$STATE_FILE" ]]; then
354
- echo "No active quality loop."
355
- return 0
356
- fi
357
-
358
- echo "Quality Loop Status"
359
- echo "==================="
360
- echo ""
361
-
362
- local loop_type iteration max_iterations status started_at fixes_applied
363
- loop_type=$(get_state_field "type")
364
- iteration=$(get_state_field "iteration")
365
- max_iterations=$(get_state_field "max_iterations")
366
- status=$(get_state_field "status")
367
- started_at=$(get_state_field "started_at")
368
- fixes_applied=$(get_state_field "fixes_applied")
369
-
370
- echo "Type: $loop_type"
371
- echo "Status: $status"
372
- echo "Iteration: $iteration / $max_iterations"
373
- echo "Fixes applied: $fixes_applied"
374
- echo "Started: $started_at"
375
- echo ""
376
- echo "State file: $STATE_FILE"
377
- return 0
378
- }
379
-
380
- # =============================================================================
381
- # Preflight Loop
382
- # =============================================================================
383
-
384
- # Run all preflight checks and optionally auto-fix issues
385
- # Arguments: $1 - "true" to enable auto-fix, "false" otherwise
386
- # Returns: 0
387
- # Output: "PASS" or "FAIL" to stdout
388
- run_preflight_checks() {
389
- local auto_fix="$1"
390
- local results=""
391
- local all_passed=true
392
-
393
- print_step "Running preflight checks..."
394
-
395
- # Check 1: ShellCheck
396
- print_info " Checking ShellCheck..."
397
- if find .agent/scripts -name "*.sh" -exec shellcheck {} \; >/dev/null 2>&1; then
398
- results="${results}shellcheck:pass\n"
399
- print_success " ShellCheck: PASS"
400
- else
401
- results="${results}shellcheck:fail\n"
402
- print_warning " ShellCheck: FAIL"
403
- all_passed=false
404
-
405
- if [[ "$auto_fix" == "true" ]]; then
406
- print_info " Auto-fix not available for ShellCheck (manual fixes required)"
407
- fi
408
- fi
409
-
410
- # Check 2: Secretlint
411
- print_info " Checking secrets..."
412
- if command -v secretlint &>/dev/null; then
413
- if secretlint "**/*" --no-terminalLink 2>/dev/null; then
414
- results="${results}secretlint:pass\n"
415
- print_success " Secretlint: PASS"
416
- else
417
- results="${results}secretlint:fail\n"
418
- print_warning " Secretlint: FAIL"
419
- all_passed=false
420
- fi
421
- else
422
- results="${results}secretlint:skip\n"
423
- print_info " Secretlint: SKIPPED (not installed)"
424
- fi
425
-
426
- # Check 3: Markdown formatting
427
- print_info " Checking markdown..."
428
- if command -v markdownlint &>/dev/null || command -v markdownlint-cli2 &>/dev/null; then
429
- local md_cmd="markdownlint"
430
- command -v markdownlint-cli2 &>/dev/null && md_cmd="markdownlint-cli2"
431
-
432
- if $md_cmd "**/*.md" --ignore node_modules 2>/dev/null; then
433
- results="${results}markdown:pass\n"
434
- print_success " Markdown: PASS"
435
- else
436
- results="${results}markdown:fail\n"
437
- print_warning " Markdown: FAIL"
438
- all_passed=false
439
-
440
- if [[ "$auto_fix" == "true" ]]; then
441
- print_info " Attempting auto-fix..."
442
- $md_cmd "**/*.md" --fix --ignore node_modules 2>/dev/null || true
443
- increment_fixes > /dev/null
444
- fi
445
- fi
446
- else
447
- results="${results}markdown:skip\n"
448
- print_info " Markdown: SKIPPED (markdownlint not installed)"
449
- fi
450
-
451
- # Check 4: Version consistency
452
- print_info " Checking version consistency..."
453
- if [[ -x "${SCRIPT_DIR}/version-manager.sh" ]]; then
454
- if "${SCRIPT_DIR}/version-manager.sh" validate &>/dev/null; then
455
- results="${results}version:pass\n"
456
- print_success " Version: PASS"
457
- else
458
- results="${results}version:fail\n"
459
- print_warning " Version: FAIL"
460
- all_passed=false
461
- fi
462
- else
463
- results="${results}version:skip\n"
464
- print_info " Version: SKIPPED (version-manager.sh not found)"
465
- fi
466
-
467
- # Return results
468
- if [[ "$all_passed" == "true" ]]; then
469
- echo "PASS"
470
- else
471
- echo "FAIL"
472
- fi
473
- return 0
474
- }
475
-
476
- # Run preflight checks in a loop until all pass or max iterations
477
- # Arguments: --auto-fix (optional), --max-iterations N (optional)
478
- # Returns: 0 on success, 1 if max iterations reached
479
- # Output: <promise>PREFLIGHT_PASS</promise> on success
480
- preflight_loop() {
481
- local auto_fix=false
482
- local max_iterations=$DEFAULT_MAX_ITERATIONS
483
-
484
- # Parse arguments
485
- while [[ $# -gt 0 ]]; do
486
- case $1 in
487
- --auto-fix)
488
- auto_fix=true
489
- shift
490
- ;;
491
- --max-iterations)
492
- max_iterations="$2"
493
- shift 2
494
- ;;
495
- *)
496
- shift
497
- ;;
498
- esac
499
- done
500
-
501
- print_info "Starting preflight loop (max iterations: $max_iterations, auto-fix: $auto_fix)"
502
-
503
- create_state "preflight" "$max_iterations" "auto_fix=$auto_fix"
504
-
505
- local iteration=1
506
- while [[ $iteration -le $max_iterations ]]; do
507
- echo ""
508
- print_info "=== Preflight Iteration $iteration / $max_iterations ==="
509
-
510
- local result
511
- result=$(run_preflight_checks "$auto_fix")
512
-
513
- if [[ "$result" == "PASS" ]]; then
514
- echo ""
515
- print_success "All preflight checks passed!"
516
- update_state "status" "completed"
517
- rm -f "$STATE_FILE"
518
-
519
- # Output completion promise for Ralph integration
520
- echo ""
521
- echo "<promise>PREFLIGHT_PASS</promise>"
522
- return 0
523
- fi
524
-
525
- if [[ $iteration -ge $max_iterations ]]; then
526
- echo ""
527
- print_warning "Max iterations ($max_iterations) reached. Some checks still failing."
528
- update_state "status" "max_iterations_reached"
529
- return 1
530
- fi
531
-
532
- iteration=$(increment_iteration)
533
-
534
- if [[ "$auto_fix" == "true" ]]; then
535
- print_info "Fixes applied, re-running checks..."
536
- sleep 1
537
- else
538
- print_warning "Checks failed. Enable --auto-fix or fix manually."
539
- return 1
540
- fi
541
- done
542
-
543
- return 1
544
- }
545
-
546
- # =============================================================================
547
- # PR Review Loop
548
- # =============================================================================
549
-
550
- # Check PR status including CI, reviews, and mergeability
551
- # Arguments:
552
- # $1 - PR number
553
- # $2 - "true" to wait for CI, "false" otherwise
554
- # Returns: 0 on success, 1 on error
555
- # Output: Status string (MERGED, READY, PENDING, CI_FAILED, CHANGES_REQUESTED, WAITING)
556
- check_pr_status() {
557
- local pr_number="$1"
558
- local wait_for_ci="$2"
559
-
560
- print_step "Checking PR #${pr_number} status..."
561
-
562
- # Get PR info
563
- local pr_info
564
- if ! pr_info=$(gh pr view "$pr_number" --json state,mergeable,reviewDecision,statusCheckRollup 2>/dev/null); then
565
- print_error "Failed to get PR info"
566
- return 1
567
- fi
568
-
569
- local state mergeable review_decision
570
- state=$(echo "$pr_info" | jq -r '.state')
571
- mergeable=$(echo "$pr_info" | jq -r '.mergeable')
572
- review_decision=$(echo "$pr_info" | jq -r '.reviewDecision // "NONE"')
573
-
574
- print_info " State: $state"
575
- print_info " Mergeable: $mergeable"
576
- print_info " Review: $review_decision"
577
-
578
- # Check CI status
579
- local checks_pending=false
580
- local checks_failed=false
581
-
582
- local check_count
583
- check_count=$(echo "$pr_info" | jq '.statusCheckRollup | length')
584
-
585
- if [[ "$check_count" -gt 0 ]]; then
586
- local pending_count failed_count
587
- pending_count=$(echo "$pr_info" | jq '[.statusCheckRollup[] | select(.status == "PENDING" or .status == "IN_PROGRESS")] | length')
588
- failed_count=$(echo "$pr_info" | jq '[.statusCheckRollup[] | select(.conclusion == "FAILURE")] | length')
589
-
590
- print_info " CI Checks: $check_count total, $pending_count pending, $failed_count failed"
591
-
592
- [[ "$pending_count" -gt 0 ]] && checks_pending=true
593
- [[ "$failed_count" -gt 0 ]] && checks_failed=true
594
- fi
595
-
596
- # Determine overall status
597
- if [[ "$state" == "MERGED" ]]; then
598
- echo "MERGED"
599
- elif [[ "$review_decision" == "APPROVED" ]] && [[ "$checks_failed" == "false" ]] && [[ "$checks_pending" == "false" ]]; then
600
- echo "READY"
601
- elif [[ "$checks_pending" == "true" ]] && [[ "$wait_for_ci" == "true" ]]; then
602
- echo "PENDING"
603
- elif [[ "$checks_failed" == "true" ]]; then
604
- echo "CI_FAILED"
605
- elif [[ "$review_decision" == "CHANGES_REQUESTED" ]]; then
606
- echo "CHANGES_REQUESTED"
607
- else
608
- echo "WAITING"
609
- fi
610
- return 0
611
- }
612
-
613
- # Get feedback from PR reviews and CI annotations
614
- # Arguments: $1 - PR number
615
- # Returns: 0
616
- # Output: Feedback text to stdout
617
- get_pr_feedback() {
618
- local pr_number="$1"
619
-
620
- print_step "Getting PR feedback..."
621
-
622
- # Get CodeRabbit comments
623
- local coderabbit_comments
624
- coderabbit_comments=$(gh api "repos/{owner}/{repo}/pulls/${pr_number}/comments" --jq '.[] | select(.user.login | contains("coderabbit")) | .body' 2>/dev/null | head -10 || echo "")
625
-
626
- if [[ -n "$coderabbit_comments" ]]; then
627
- print_info "CodeRabbit feedback found"
628
- echo "$coderabbit_comments"
629
- fi
630
-
631
- # Get check run annotations
632
- local head_sha
633
- head_sha=$(gh pr view "$pr_number" --json headRefOid -q .headRefOid 2>/dev/null || echo "")
634
-
635
- if [[ -n "$head_sha" ]]; then
636
- local annotations
637
- annotations=$(gh api "repos/{owner}/{repo}/commits/${head_sha}/check-runs" --jq '.check_runs[].output.annotations[]? | "\(.path):\(.start_line) - \(.message)"' 2>/dev/null | head -20 || echo "")
638
-
639
- if [[ -n "$annotations" ]]; then
640
- print_info "CI annotations found:"
641
- echo "$annotations"
642
- fi
643
- fi
644
-
645
- return 0
646
- }
647
-
648
- # Check if review is stale and trigger re-review if needed
649
- # Arguments:
650
- # $1 - PR number
651
- # $2 - Stale threshold in seconds (default: 300)
652
- # Returns: 0 if re-review triggered, 1 if not needed
653
- # Side effects: Posts @coderabbitai review comment if stale
654
- check_and_trigger_review() {
655
- local pr_number="$1"
656
- local stale_threshold="${2:-$DEFAULT_REVIEW_STALE_THRESHOLD}"
657
-
658
- # Get last push time
659
- local last_push_time
660
- last_push_time=$(gh pr view "$pr_number" --json commits --jq '.commits[-1].committedDate' 2>/dev/null || echo "")
661
-
662
- if [[ -z "$last_push_time" ]]; then
663
- print_warning "Could not determine last push time"
664
- return 1
665
- fi
666
-
667
- # Get last CodeRabbit review time
668
- local last_review_time
669
- last_review_time=$(gh api "repos/{owner}/{repo}/pulls/${pr_number}/reviews" \
670
- --jq '[.[] | select(.user.login | contains("coderabbit"))] | sort_by(.submitted_at) | last | .submitted_at // ""' 2>/dev/null || echo "")
671
-
672
- # Convert times to epoch for comparison
673
- local now_epoch last_push_epoch last_review_epoch
674
- now_epoch=$(date +%s)
675
-
676
- # Parse ISO 8601 dates (works on macOS and Linux)
677
- if [[ -n "$last_push_time" ]]; then
678
- last_push_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$last_push_time" +%s 2>/dev/null || \
679
- date -d "$last_push_time" +%s 2>/dev/null || echo "0")
680
- else
681
- last_push_epoch=0
682
- fi
683
-
684
- if [[ -n "$last_review_time" ]]; then
685
- last_review_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$last_review_time" +%s 2>/dev/null || \
686
- date -d "$last_review_time" +%s 2>/dev/null || echo "0")
687
- else
688
- last_review_epoch=0
689
- fi
690
-
691
- # Check if review is stale (push happened after last review, and threshold exceeded)
692
- local time_since_push=$((now_epoch - last_push_epoch))
693
-
694
- if [[ $last_push_epoch -gt $last_review_epoch ]] && [[ $time_since_push -ge $stale_threshold ]]; then
695
- print_info "Review appears stale (${time_since_push}s since push, no review since)"
696
- print_info "Triggering CodeRabbit re-review..."
697
-
698
- if gh pr comment "$pr_number" --body "@coderabbitai review" 2>/dev/null; then
699
- print_success "Re-review triggered for PR #${pr_number}"
700
- return 0
701
- else
702
- print_warning "Failed to trigger re-review"
703
- return 1
704
- fi
705
- fi
706
-
707
- return 1
708
- }
709
-
710
- # Monitor PR until approved or merged
711
- # Arguments: --pr NUMBER, --wait-for-ci, --max-iterations N, --auto-trigger-review
712
- # Returns: 0 on approval/merge, 1 if max iterations reached
713
- # Output: <promise>PR_APPROVED</promise> or <promise>PR_MERGED</promise>
714
- pr_review_loop() {
715
- local wait_for_ci=false
716
- local auto_trigger_review=true
717
- local max_iterations=$DEFAULT_MAX_ITERATIONS
718
- local pr_number=""
719
-
720
- # Parse arguments
721
- while [[ $# -gt 0 ]]; do
722
- case $1 in
723
- --wait-for-ci)
724
- wait_for_ci=true
725
- shift
726
- ;;
727
- --max-iterations)
728
- max_iterations="$2"
729
- shift 2
730
- ;;
731
- --pr)
732
- pr_number="$2"
733
- shift 2
734
- ;;
735
- --no-auto-trigger)
736
- auto_trigger_review=false
737
- shift
738
- ;;
739
- --auto-trigger-review)
740
- auto_trigger_review=true
741
- shift
742
- ;;
743
- *)
744
- # Assume it's the PR number
745
- if [[ "$1" =~ ^[0-9]+$ ]]; then
746
- pr_number="$1"
747
- fi
748
- shift
749
- ;;
750
- esac
751
- done
752
-
753
- # Auto-detect PR number if not provided
754
- if [[ -z "$pr_number" ]]; then
755
- pr_number=$(gh pr view --json number -q .number 2>/dev/null || echo "")
756
-
757
- if [[ -z "$pr_number" ]]; then
758
- print_error "No PR number provided and no PR found for current branch"
759
- echo "Usage: quality-loop-helper.sh pr-review [--pr NUMBER] [--wait-for-ci] [--max-iterations N] [--no-auto-trigger]"
760
- return 1
761
- fi
762
- fi
763
-
764
- print_info "Starting PR review loop for PR #${pr_number} (max iterations: $max_iterations, auto-trigger: $auto_trigger_review)"
765
-
766
- create_state "pr-review" "$max_iterations" "pr=$pr_number,wait_for_ci=$wait_for_ci,auto_trigger=$auto_trigger_review"
767
-
768
- local iteration=1
769
- while [[ $iteration -le $max_iterations ]]; do
770
- echo ""
771
- print_info "=== PR Review Iteration $iteration / $max_iterations ==="
772
-
773
- local status
774
- status=$(check_pr_status "$pr_number" "$wait_for_ci")
775
-
776
- case "$status" in
777
- MERGED)
778
- print_success "PR has been merged!"
779
- rm -f "$STATE_FILE"
780
- echo "<promise>PR_MERGED</promise>"
781
- return 0
782
- ;;
783
- READY)
784
- print_success "PR is approved and ready to merge!"
785
- rm -f "$STATE_FILE"
786
- echo "<promise>PR_APPROVED</promise>"
787
- return 0
788
- ;;
789
- PENDING)
790
- # Get pending checks and calculate adaptive wait
791
- local pending_checks
792
- pending_checks=$(get_pending_checks "$pr_number")
793
- local wait_time
794
- wait_time=$(calculate_adaptive_wait "$pending_checks")
795
-
796
- if [[ -n "$pending_checks" ]]; then
797
- print_info "CI checks still running: $pending_checks"
798
- print_info "Waiting ${wait_time}s (adaptive based on slowest pending check)..."
799
- else
800
- print_info "CI checks still running, waiting ${wait_time}s..."
801
- fi
802
- sleep "$wait_time"
803
- ;;
804
- CI_FAILED)
805
- print_warning "CI checks failed. Getting feedback..."
806
- get_pr_feedback "$pr_number"
807
- print_info "Fix the issues and push updates."
808
- ;;
809
- CHANGES_REQUESTED)
810
- print_warning "Changes requested. Getting feedback..."
811
- get_pr_feedback "$pr_number"
812
- print_info "Address the feedback and push updates."
813
- ;;
814
- WAITING)
815
- print_info "Waiting for review..."
816
- # Check if review is stale and trigger re-review if enabled
817
- if [[ "$auto_trigger_review" == "true" ]]; then
818
- if check_and_trigger_review "$pr_number"; then
819
- print_info "Re-review triggered, waiting for response..."
820
- fi
821
- fi
822
- ;;
823
- *)
824
- print_warning "Unknown PR status: $status"
825
- ;;
826
- esac
827
-
828
- iteration=$(increment_iteration)
829
-
830
- if [[ $iteration -le $max_iterations ]]; then
831
- # Use exponential backoff for general waiting
832
- local backoff_wait
833
- backoff_wait=$(calculate_backoff_wait "$iteration")
834
-
835
- # But also consider pending checks for smarter waiting
836
- local pending_checks
837
- pending_checks=$(get_pending_checks "$pr_number")
838
- local adaptive_wait
839
- adaptive_wait=$(calculate_adaptive_wait "$pending_checks")
840
-
841
- # Use the larger of backoff or adaptive wait
842
- local final_wait=$backoff_wait
843
- if [[ $adaptive_wait -gt $backoff_wait ]]; then
844
- final_wait=$adaptive_wait
845
- fi
846
-
847
- print_info "Waiting ${final_wait}s before next check (iteration $iteration, backoff: ${backoff_wait}s, adaptive: ${adaptive_wait}s)..."
848
- sleep "$final_wait"
849
- fi
850
- done
851
-
852
- print_warning "Max iterations reached. PR not yet approved."
853
- update_state "status" "max_iterations_reached"
854
- return 1
855
- }
856
-
857
- # =============================================================================
858
- # Postflight Loop
859
- # =============================================================================
860
-
861
- # Check release health (CI status, release exists, version consistency)
862
- # Arguments: none
863
- # Returns: 0
864
- # Output: "HEALTHY" or "UNHEALTHY" to stdout
865
- check_release_health() {
866
- print_step "Checking release health..."
867
-
868
- local all_healthy=true
869
-
870
- # Check 1: Latest workflow run status
871
- print_info " Checking CI status..."
872
- local latest_run
873
- latest_run=$(gh run list --limit 1 --json conclusion,status -q '.[0]' 2>/dev/null || echo '{}')
874
-
875
- local run_status run_conclusion
876
- run_status=$(echo "$latest_run" | jq -r '.status // "unknown"')
877
- run_conclusion=$(echo "$latest_run" | jq -r '.conclusion // "unknown"')
878
-
879
- if [[ "$run_status" == "completed" ]] && [[ "$run_conclusion" == "success" ]]; then
880
- print_success " CI: PASS (latest run succeeded)"
881
- elif [[ "$run_status" == "in_progress" ]]; then
882
- print_info " CI: PENDING (run in progress)"
883
- all_healthy=false
884
- else
885
- print_warning " CI: FAIL (conclusion: $run_conclusion)"
886
- all_healthy=false
887
- fi
888
-
889
- # Check 2: Latest release exists
890
- print_info " Checking latest release..."
891
- local latest_release
892
- latest_release=$(gh release view --json tagName,publishedAt -q '.tagName' 2>/dev/null || echo "")
893
-
894
- if [[ -n "$latest_release" ]]; then
895
- print_success " Release: $latest_release exists"
896
- else
897
- print_warning " Release: No releases found"
898
- fi
899
-
900
- # Check 3: Tag matches VERSION
901
- print_info " Checking version consistency..."
902
- local current_version
903
- current_version=$(cat VERSION 2>/dev/null || echo "unknown")
904
-
905
- if [[ "$latest_release" == "v${current_version}" ]] || [[ "$latest_release" == "$current_version" ]]; then
906
- print_success " Version: Matches ($current_version)"
907
- else
908
- print_warning " Version: Mismatch (VERSION=$current_version, release=$latest_release)"
909
- all_healthy=false
910
- fi
911
-
912
- if [[ "$all_healthy" == "true" ]]; then
913
- echo "HEALTHY"
914
- else
915
- echo "UNHEALTHY"
916
- fi
917
- return 0
918
- }
919
-
920
- # Monitor release health for a specified duration
921
- # Arguments: --monitor-duration Nm/Nh/Ns, --max-iterations N
922
- # Returns: 0 on healthy, 0 on timeout (with warning)
923
- # Output: <promise>RELEASE_HEALTHY</promise> on success
924
- postflight_loop() {
925
- local monitor_duration=$DEFAULT_MONITOR_DURATION
926
- local max_iterations=5
927
-
928
- # Parse arguments
929
- while [[ $# -gt 0 ]]; do
930
- case $1 in
931
- --monitor-duration)
932
- # Parse duration (e.g., 5m, 10m, 1h, or raw seconds)
933
- local duration_str="$2"
934
- if [[ "$duration_str" =~ ^([0-9]+)m$ ]]; then
935
- monitor_duration=$((BASH_REMATCH[1] * 60))
936
- elif [[ "$duration_str" =~ ^([0-9]+)h$ ]]; then
937
- monitor_duration=$((BASH_REMATCH[1] * 3600))
938
- elif [[ "$duration_str" =~ ^([0-9]+)s$ ]]; then
939
- monitor_duration="${BASH_REMATCH[1]}"
940
- elif [[ "$duration_str" =~ ^([0-9]+)$ ]]; then
941
- monitor_duration="$duration_str"
942
- else
943
- print_warning "Unrecognized duration format: '$duration_str'. Expected: Nm (minutes), Nh (hours), Ns (seconds), or N (seconds). Using default: ${DEFAULT_MONITOR_DURATION}s"
944
- fi
945
- shift 2
946
- ;;
947
- --max-iterations)
948
- max_iterations="$2"
949
- shift 2
950
- ;;
951
- *)
952
- shift
953
- ;;
954
- esac
955
- done
956
-
957
- print_info "Starting postflight monitoring (duration: ${monitor_duration}s, max iterations: $max_iterations)"
958
-
959
- create_state "postflight" "$max_iterations" "monitor_duration=$monitor_duration"
960
-
961
- local start_time
962
- start_time=$(date +%s)
963
- local iteration=1
964
-
965
- while [[ $iteration -le $max_iterations ]]; do
966
- local current_time
967
- current_time=$(date +%s)
968
- local elapsed=$((current_time - start_time))
969
-
970
- if [[ $elapsed -ge $monitor_duration ]]; then
971
- print_info "Monitor duration reached."
972
- break
973
- fi
974
-
975
- echo ""
976
- print_info "=== Postflight Check $iteration / $max_iterations (${elapsed}s / ${monitor_duration}s) ==="
977
-
978
- local status
979
- status=$(check_release_health)
980
-
981
- if [[ "$status" == "HEALTHY" ]]; then
982
- print_success "Release is healthy!"
983
- rm -f "$STATE_FILE"
984
- echo "<promise>RELEASE_HEALTHY</promise>"
985
- return 0
986
- fi
987
-
988
- iteration=$(increment_iteration)
989
-
990
- if [[ $iteration -le $max_iterations ]]; then
991
- local wait_time=$((monitor_duration / max_iterations))
992
- print_info "Waiting ${wait_time}s before next check..."
993
- sleep "$wait_time"
994
- fi
995
- done
996
-
997
- print_warning "Postflight monitoring complete. Some issues may remain."
998
- update_state "status" "monitoring_complete"
999
- rm -f "$STATE_FILE"
1000
- return 0
1001
- }
1002
-
1003
- # =============================================================================
1004
- # Help
1005
- # =============================================================================
1006
-
1007
- show_help() {
1008
- cat << 'EOF'
1009
- Quality Loop Helper - Iterative Quality Workflows
1010
-
1011
- USAGE:
1012
- quality-loop-helper.sh <command> [options]
1013
-
1014
- COMMANDS:
1015
- preflight Run preflight checks in a loop until all pass
1016
- pr-review Monitor PR until approved or merged
1017
- postflight Monitor release health after deployment
1018
- status Show current loop status
1019
- cancel Cancel active loop
1020
- help Show this help
1021
-
1022
- PREFLIGHT OPTIONS:
1023
- --auto-fix Attempt to auto-fix issues
1024
- --max-iterations <n> Max iterations (default: 10)
1025
-
1026
- PR-REVIEW OPTIONS:
1027
- --pr <number> PR number (auto-detected if not provided)
1028
- --wait-for-ci Wait for CI checks to complete
1029
- --max-iterations <n> Max iterations (default: 10)
1030
-
1031
- POSTFLIGHT OPTIONS:
1032
- --monitor-duration <t> How long to monitor (e.g., 5m, 10m, 1h)
1033
- --max-iterations <n> Max checks during monitoring (default: 5)
1034
-
1035
- EXAMPLES:
1036
- # Run preflight with auto-fix
1037
- quality-loop-helper.sh preflight --auto-fix --max-iterations 5
1038
-
1039
- # Monitor PR until approved
1040
- quality-loop-helper.sh pr-review --pr 123 --wait-for-ci
1041
-
1042
- # Monitor release for 10 minutes
1043
- quality-loop-helper.sh postflight --monitor-duration 10m
1044
-
1045
- COMPLETION PROMISES:
1046
- preflight: <promise>PREFLIGHT_PASS</promise>
1047
- pr-review: <promise>PR_APPROVED</promise> or <promise>PR_MERGED</promise>
1048
- postflight: <promise>RELEASE_HEALTHY</promise>
1049
-
1050
- These can be used with Ralph loops for fully autonomous workflows.
1051
-
1052
- ADAPTIVE TIMING (PR Review):
1053
- The pr-review command uses intelligent timing based on pending CI checks:
1054
-
1055
- Service Category Typical Time Initial Wait Poll Interval
1056
- ─────────────────────────────────────────────────────────────────
1057
- Fast (CodeFactor) 1-5s 10s 5s
1058
- Medium (SonarCloud) 43-62s 60s 15s
1059
- Slow (CodeRabbit) 120-180s 120s 30s
1060
-
1061
- Additionally, exponential backoff is applied:
1062
- - Base wait: 15s, doubles each iteration, max 120s
1063
- - Final wait = max(backoff_wait, adaptive_wait)
1064
-
1065
- This prevents:
1066
- - Waiting too long for fast checks
1067
- - Polling too frequently for slow checks (wastes API calls)
1068
- EOF
1069
- return 0
1070
- }
1071
-
1072
- # =============================================================================
1073
- # Main
1074
- # =============================================================================
1075
-
1076
- main() {
1077
- local command="${1:-help}"
1078
- shift || true
1079
-
1080
- case "$command" in
1081
- preflight)
1082
- preflight_loop "$@"
1083
- ;;
1084
- pr-review|pr)
1085
- pr_review_loop "$@"
1086
- ;;
1087
- postflight)
1088
- postflight_loop "$@"
1089
- ;;
1090
- status)
1091
- show_status
1092
- ;;
1093
- cancel)
1094
- cancel_loop
1095
- ;;
1096
- help|--help|-h)
1097
- show_help
1098
- ;;
1099
- *)
1100
- print_error "Unknown command: $command"
1101
- echo ""
1102
- show_help
1103
- return 1
1104
- ;;
1105
- esac
1106
- }
1107
-
1108
- main "$@"