@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.3

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 (407) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -2
  3. package/bin/doflow.js +44 -0
  4. package/core/.antigravity-plugin/hooks.json +39 -0
  5. package/core/.antigravity-plugin/mcp_config.json +18 -0
  6. package/core/.antigravity-plugin/plugin.json +31 -0
  7. package/core/.claude-plugin/marketplace.json +20 -0
  8. package/core/.claude-plugin/plugin.json +21 -0
  9. package/core/.codex-plugin/marketplace.json +20 -0
  10. package/core/.codex-plugin/plugin.json +35 -0
  11. package/core/.plugin/plugin.json +19 -0
  12. package/core/harnesses/antigravity/hooks/pre-implementation-gate.sh +9 -0
  13. package/core/harnesses/antigravity/hooks/stop-check.sh +10 -0
  14. package/core/harnesses/claude/hooks/lib.sh +304 -0
  15. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +20 -0
  16. package/core/harnesses/claude/hooks/permission-audit.sh +47 -0
  17. package/core/harnesses/claude/hooks/post-compact.sh +74 -0
  18. package/core/harnesses/claude/hooks/post-edit-lint.sh +36 -0
  19. package/core/harnesses/claude/hooks/post-tool-failure.sh +47 -0
  20. package/core/harnesses/claude/hooks/pre-bash-guard.sh +20 -0
  21. package/core/harnesses/claude/hooks/pre-compact.sh +43 -0
  22. package/core/harnesses/claude/hooks/pre-implement-gate.sh +21 -0
  23. package/core/harnesses/claude/hooks/session-end.sh +89 -0
  24. package/core/harnesses/claude/hooks/session-start.sh +5 -0
  25. package/core/harnesses/claude/hooks/skill-config-audit.sh +39 -0
  26. package/core/harnesses/claude/hooks/stop-check.sh +5 -0
  27. package/core/harnesses/claude/hooks/subagent-audit.sh +7 -0
  28. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -0
  29. package/core/harnesses/claude/settings/keybindings.json +20 -0
  30. package/core/harnesses/claude/settings/settings.json +201 -0
  31. package/core/harnesses/claude/statusline/doflow-statusline.sh +49 -0
  32. package/core/harnesses/codex/agents/core-implementer.toml +7 -0
  33. package/core/harnesses/codex/agents/quality-guardian.toml +8 -0
  34. package/core/harnesses/codex/agents/research-writer.toml +8 -0
  35. package/core/harnesses/codex/agents/spec-analyst.toml +8 -0
  36. package/core/harnesses/codex/agents/system-architect.toml +9 -0
  37. package/core/harnesses/codex/config/config.toml +10 -0
  38. package/core/harnesses/codex/hooks/hooks.json +122 -0
  39. package/core/harnesses/codex/hooks/lib.sh +335 -0
  40. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +18 -0
  41. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +52 -0
  42. package/core/harnesses/codex/hooks/post-edit-lint.sh +6 -0
  43. package/core/harnesses/codex/hooks/pre-bash-guard.sh +18 -0
  44. package/core/harnesses/codex/hooks/pre-compact.impl.sh +46 -0
  45. package/core/harnesses/codex/hooks/pre-compact.sh +9 -0
  46. package/core/harnesses/codex/hooks/pre-implement-gate.sh +19 -0
  47. package/core/harnesses/codex/hooks/session-end.impl.sh +92 -0
  48. package/core/harnesses/codex/hooks/session-end.sh +5 -0
  49. package/core/harnesses/codex/hooks/session-start.sh +5 -0
  50. package/core/harnesses/codex/hooks/stop-check.sh +8 -0
  51. package/core/harnesses/codex/hooks/subagent-audit.sh +8 -0
  52. package/core/harnesses/codex/hooks/user-prompt-submit.sh +6 -0
  53. package/core/harnesses/gemini/hooks/hooks.json +77 -0
  54. package/core/harnesses/gemini/hooks/lib.sh +302 -0
  55. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +21 -0
  56. package/core/harnesses/gemini/hooks/post-edit-lint.sh +39 -0
  57. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +21 -0
  58. package/core/harnesses/gemini/hooks/pre-compact.sh +38 -0
  59. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +22 -0
  60. package/core/harnesses/gemini/hooks/session-end.impl.sh +89 -0
  61. package/core/harnesses/gemini/hooks/session-end.sh +5 -0
  62. package/core/harnesses/gemini/hooks/session-start.sh +5 -0
  63. package/core/harnesses/gemini/settings/adapter-defaults.json +4 -0
  64. package/core/harnesses/kiro/agents/doflow.json +58 -0
  65. package/core/harnesses/kiro/hooks/doflow.json +53 -0
  66. package/core/harnesses/kiro/hooks/lib.sh +324 -0
  67. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +9 -0
  68. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +9 -0
  69. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +9 -0
  70. package/core/harnesses/kiro/hooks/session-start.sh +9 -0
  71. package/core/harnesses/kiro/hooks/stop-check.sh +43 -0
  72. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +39 -0
  73. package/core/harnesses/shared/hooks/adapters/antigravity.js +111 -0
  74. package/core/harnesses/shared/hooks/adapters/arg-fields.js +19 -0
  75. package/core/harnesses/shared/hooks/adapters/gemini.js +114 -0
  76. package/core/harnesses/shared/hooks/hooks.json +47 -0
  77. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +91 -0
  78. package/core/harnesses/shared/hooks/policies/capture-failure.sh +97 -0
  79. package/core/harnesses/shared/hooks/policies/deny-json.sh +29 -0
  80. package/core/harnesses/shared/hooks/policies/lib.sh +501 -0
  81. package/core/harnesses/shared/hooks/policies/mcp-policy.conf +21 -0
  82. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +91 -0
  83. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +328 -0
  84. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +496 -0
  85. package/core/harnesses/shared/hooks/policies/prompt-nudge.jq +131 -0
  86. package/core/harnesses/shared/hooks/policies/session-context.sh +141 -0
  87. package/core/harnesses/shared/hooks/policies/stop-check.sh +264 -0
  88. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +46 -0
  89. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +235 -0
  90. package/core/harnesses/shared/hooks/stream-hook-runner.js +590 -0
  91. package/core/harnesses/shared/locator/doflow-run +87 -0
  92. package/core/registry/advice.json +101 -0
  93. package/core/registry/assets.json +792 -0
  94. package/core/registry/capabilities.json +120 -0
  95. package/core/registry/contracts.json +189 -0
  96. package/core/registry/external-tools.json +191 -0
  97. package/core/registry/harnesses.json +2062 -0
  98. package/core/registry/lifecycle.json +231 -0
  99. package/core/registry/mcp.json +18 -0
  100. package/core/registry/models.json +79 -0
  101. package/core/registry/readiness-templates.json +118 -0
  102. package/core/registry/routes.json +47 -0
  103. package/core/registry/storage.json +1057 -0
  104. package/core/registry/verification.json +258 -0
  105. package/core/registry/workflows.json +790 -0
  106. package/core/shared/agent-specs/core-implementer.md +73 -0
  107. package/core/shared/agent-specs/quality-guardian.md +28 -0
  108. package/core/shared/agent-specs/research-writer.md +31 -0
  109. package/core/shared/agent-specs/spec-analyst.md +33 -0
  110. package/core/shared/agent-specs/system-architect.md +29 -0
  111. package/core/shared/guidance/DOFLOW_CORE.md +8 -0
  112. package/core/shared/guidance/FLAGS.md +25 -0
  113. package/core/shared/guidance/PRINCIPLES.md +61 -0
  114. package/core/shared/guidance/VERSION +1 -0
  115. package/core/shared/guidance/mcp/MCP_Context7.md +18 -0
  116. package/core/shared/guidance/mcp/MCP_Sequential.md +17 -0
  117. package/core/shared/guidance/modes/MODE_Brainstorming.md +46 -0
  118. package/core/shared/guidance/modes/MODE_DeepResearch.md +69 -0
  119. package/core/shared/guidance/modes/MODE_Introspection.md +33 -0
  120. package/core/shared/guidance/modes/MODE_Orchestration.md +57 -0
  121. package/core/shared/guidance/modes/MODE_Task_Management.md +74 -0
  122. package/core/shared/guidance/pointers/claude-gemini.md +2 -0
  123. package/core/shared/guidance/pointers/codex.md +6 -0
  124. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +373 -0
  125. package/core/shared/guidance/references/ARTIFACT_VOICE.md +104 -0
  126. package/core/shared/guidance/references/CONSTITUTION_BASE.md +47 -0
  127. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +161 -0
  128. package/core/shared/guidance/references/DOFLOW_CHAIN.md +87 -0
  129. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +93 -0
  130. package/core/shared/guidance/references/MODEL_SELECTION.md +74 -0
  131. package/core/shared/guidance/references/ORCHESTRATION.md +36 -0
  132. package/core/shared/guidance/references/RESEARCH_CONFIG.md +21 -0
  133. package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +81 -0
  134. package/core/shared/guidance/rules/RULE_01_SAFETY.md +25 -0
  135. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +23 -0
  136. package/core/shared/guidance/rules/RULE_03_QUALITY.md +14 -0
  137. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +46 -0
  138. package/core/shared/scripts/doflow/assets/mermaid-theme.json +46 -0
  139. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +167 -0
  140. package/core/shared/scripts/doflow/bash/do-git-state.sh +546 -0
  141. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +208 -0
  142. package/core/shared/scripts/doflow/bash/do-paths.sh +347 -0
  143. package/core/shared/scripts/doflow/bash/do-prereqs.sh +66 -0
  144. package/core/shared/scripts/doflow/bash/do-review-package.sh +127 -0
  145. package/core/shared/scripts/doflow/bash/do-task-brief.sh +698 -0
  146. package/core/shared/scripts/doflow/bash/render-audit.sh +224 -0
  147. package/core/shared/scripts/doflow/bash/render-diagrams.sh +177 -0
  148. package/core/shared/scripts/doflow/bash/render-puml.sh +223 -0
  149. package/core/shared/scripts/doflow/bash/sync-context.sh +66 -0
  150. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +526 -0
  151. package/core/shared/scripts/doflow/bin/doflow-run +592 -0
  152. package/core/shared/skills/do/SKILL.md +128 -0
  153. package/core/shared/skills/do/references/estimation.md +13 -0
  154. package/core/shared/skills/do/references/pm_routing.md +25 -0
  155. package/core/shared/skills/do/references/tool_matrix.md +52 -0
  156. package/core/shared/skills/do-brainstorm/SKILL.md +252 -0
  157. package/core/shared/skills/do-code-review/README.md +49 -0
  158. package/core/shared/skills/do-code-review/SKILL.md +333 -0
  159. package/core/shared/skills/do-code-review/assets/sample_c_clean.c +71 -0
  160. package/core/shared/skills/do-code-review/assets/sample_c_smells.c +66 -0
  161. package/core/shared/skills/do-code-review/assets/sample_cpp_shift.cpp +19 -0
  162. package/core/shared/skills/do-code-review/assets/sample_csharp_clean.cs +100 -0
  163. package/core/shared/skills/do-code-review/assets/sample_csharp_smells.cs +78 -0
  164. package/core/shared/skills/do-code-review/assets/sample_java_clean.java +55 -0
  165. package/core/shared/skills/do-code-review/assets/sample_java_smells.java +56 -0
  166. package/core/shared/skills/do-code-review/assets/sample_json_clean.json +7 -0
  167. package/core/shared/skills/do-code-review/assets/sample_markdown_clean.md +30 -0
  168. package/core/shared/skills/do-code-review/assets/sample_markdown_smells.md +29 -0
  169. package/core/shared/skills/do-code-review/assets/sample_shell_heredoc.sh +38 -0
  170. package/core/shared/skills/do-code-review/assets/sample_shell_smells.sh +15 -0
  171. package/core/shared/skills/do-code-review/assets/sample_typescript_strings.ts +36 -0
  172. package/core/shared/skills/do-code-review/assets/sample_yaml_smells.yaml +6 -0
  173. package/core/shared/skills/do-code-review/content-types/config.md +83 -0
  174. package/core/shared/skills/do-code-review/content-types/markdown.md +132 -0
  175. package/core/shared/skills/do-code-review/expected_outputs/sample_c_clean_quality.json +79 -0
  176. package/core/shared/skills/do-code-review/expected_outputs/sample_c_smells_quality.json +169 -0
  177. package/core/shared/skills/do-code-review/expected_outputs/sample_cpp_shift_quality.json +49 -0
  178. package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_clean_quality.json +149 -0
  179. package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_smells_quality.json +157 -0
  180. package/core/shared/skills/do-code-review/expected_outputs/sample_java_clean_quality.json +67 -0
  181. package/core/shared/skills/do-code-review/expected_outputs/sample_java_smells_quality.json +97 -0
  182. package/core/shared/skills/do-code-review/expected_outputs/sample_json_clean_quality.json +33 -0
  183. package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_clean_quality.json +1 -0
  184. package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_smells_quality.json +16 -0
  185. package/core/shared/skills/do-code-review/expected_outputs/sample_shell_heredoc_quality.json +54 -0
  186. package/core/shared/skills/do-code-review/expected_outputs/sample_shell_smells_quality.json +91 -0
  187. package/core/shared/skills/do-code-review/expected_outputs/sample_typescript_strings_quality.json +60 -0
  188. package/core/shared/skills/do-code-review/expected_outputs/sample_yaml_smells_quality.json +46 -0
  189. package/core/shared/skills/do-code-review/languages/c.md +103 -0
  190. package/core/shared/skills/do-code-review/languages/cpp.md +103 -0
  191. package/core/shared/skills/do-code-review/languages/csharp.md +97 -0
  192. package/core/shared/skills/do-code-review/languages/dart.md +108 -0
  193. package/core/shared/skills/do-code-review/languages/go.md +92 -0
  194. package/core/shared/skills/do-code-review/languages/java.md +103 -0
  195. package/core/shared/skills/do-code-review/languages/kotlin.md +88 -0
  196. package/core/shared/skills/do-code-review/languages/php.md +109 -0
  197. package/core/shared/skills/do-code-review/languages/python.md +94 -0
  198. package/core/shared/skills/do-code-review/languages/ruby.md +105 -0
  199. package/core/shared/skills/do-code-review/languages/rust.md +99 -0
  200. package/core/shared/skills/do-code-review/languages/shell.md +90 -0
  201. package/core/shared/skills/do-code-review/languages/swift.md +88 -0
  202. package/core/shared/skills/do-code-review/languages/typescript.md +97 -0
  203. package/core/shared/skills/do-code-review/review-policy.json +47 -0
  204. package/core/shared/skills/do-code-review/rules/universal.md +49 -0
  205. package/core/shared/skills/do-code-review/scripts/code_quality_checker.py +1696 -0
  206. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +532 -0
  207. package/core/shared/skills/do-code-review/scripts/pr_analyzer.py +620 -0
  208. package/core/shared/skills/do-code-review/scripts/review_report_generator.py +505 -0
  209. package/core/shared/skills/do-constitution/SKILL.md +72 -0
  210. package/core/shared/skills/do-design/SKILL.md +212 -0
  211. package/core/shared/skills/do-diagnose/SKILL.md +156 -0
  212. package/core/shared/skills/do-diagnose/references/code_audit.md +15 -0
  213. package/core/shared/skills/do-diagnose/references/refactoring.md +12 -0
  214. package/core/shared/skills/do-diagnose/references/root_cause.md +17 -0
  215. package/core/shared/skills/do-document/SKILL.md +81 -0
  216. package/core/shared/skills/do-document/references/api-reference.md +42 -0
  217. package/core/shared/skills/do-document/references/deep_research.md +18 -0
  218. package/core/shared/skills/do-document/references/embedded-research.md +11 -0
  219. package/core/shared/skills/do-document/references/implementation-flow.md +105 -0
  220. package/core/shared/skills/do-document/references/index_generator.md +9 -0
  221. package/core/shared/skills/do-document/references/user-guide.md +22 -0
  222. package/core/shared/skills/do-execute-plan/SKILL.md +261 -0
  223. package/core/shared/skills/do-execute-plan/references/handoff_record.md +57 -0
  224. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +92 -0
  225. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +155 -0
  226. package/core/shared/skills/do-execute-plan/references/run_position.md +63 -0
  227. package/core/shared/skills/do-execute-plan/references/scaffold.md +441 -0
  228. package/core/shared/skills/do-execute-plan/references/task_attempts.md +41 -0
  229. package/core/shared/skills/do-flow/SKILL.md +186 -0
  230. package/core/shared/skills/do-flow/references/task_classes.md +134 -0
  231. package/core/shared/skills/do-git/SKILL.md +115 -0
  232. package/core/shared/skills/do-git/references/hotfix-ritual.md +44 -0
  233. package/core/shared/skills/do-git/references/lifecycle-policy.md +54 -0
  234. package/core/shared/skills/do-git/references/release-ritual.md +64 -0
  235. package/core/shared/skills/do-implement/SKILL.md +222 -0
  236. package/core/shared/skills/do-plan/SKILL.md +219 -0
  237. package/core/shared/skills/do-test/SKILL.md +113 -0
  238. package/core/shared/templates/doflow/constitution-template.md +28 -0
  239. package/core/shared/templates/doflow/data-model-template.md +169 -0
  240. package/core/shared/templates/doflow/design-template.md +193 -0
  241. package/core/shared/templates/doflow/external-contract-template.md +35 -0
  242. package/core/shared/templates/doflow/intent-template.md +45 -0
  243. package/core/shared/templates/doflow/plan-template.md +151 -0
  244. package/core/shared/templates/doflow/question-log-template.md +51 -0
  245. package/core/shared/templates/doflow/requirement-template.md +126 -0
  246. package/core/shared/templates/doflow/specs-template.md +55 -0
  247. package/core/shared/templates/doflow/state-template.md +59 -0
  248. package/core/shared/workflows/do-flow-chain.md +17 -0
  249. package/package.json +46 -4
  250. package/src/adapters/antigravity/index.ts +631 -0
  251. package/src/adapters/claude/index.ts +548 -0
  252. package/src/adapters/claude/mcp.ts +94 -0
  253. package/src/adapters/codex/agents.ts +208 -0
  254. package/src/adapters/codex/config.ts +272 -0
  255. package/src/adapters/codex/hooks.ts +203 -0
  256. package/src/adapters/codex/index.ts +707 -0
  257. package/src/adapters/codex/mcp.ts +271 -0
  258. package/src/adapters/codex/native-projection.ts +34 -0
  259. package/src/adapters/copilot/index.ts +413 -0
  260. package/src/adapters/copy-tree.ts +620 -0
  261. package/src/adapters/gemini/hooks.ts +210 -0
  262. package/src/adapters/gemini/index.ts +390 -0
  263. package/src/adapters/hook-commands.ts +60 -0
  264. package/src/adapters/index.ts +106 -0
  265. package/src/adapters/kiro/index.ts +310 -0
  266. package/src/adapters/mcp-entries.ts +331 -0
  267. package/src/adapters/opencode/index.ts +466 -0
  268. package/src/adapters/pi/index.ts +279 -0
  269. package/src/adapters/pi/json-members.ts +185 -0
  270. package/src/adapters/pi/mcp.ts +373 -0
  271. package/src/adapters/resolve-source.ts +19 -0
  272. package/src/cli/commands/install.ts +218 -0
  273. package/src/cli/commands/list-backups.ts +21 -0
  274. package/src/cli/commands/reconcile.ts +120 -0
  275. package/src/cli/commands/remove.ts +82 -0
  276. package/src/cli/commands/rollback.ts +172 -0
  277. package/src/cli/commands/self-update.ts +33 -0
  278. package/src/cli/commands/status.ts +138 -0
  279. package/src/cli/commands/tools.ts +92 -0
  280. package/src/cli/commands/update.ts +162 -0
  281. package/src/cli/index.ts +568 -0
  282. package/src/cli/runtime-commands.ts +281 -0
  283. package/src/cli/shared.ts +218 -0
  284. package/src/helper/bash-helper.ts +34 -0
  285. package/src/helper/git.ts +24 -0
  286. package/src/helper/guardrails.ts +49 -0
  287. package/src/helper/harness-paths.ts +140 -0
  288. package/src/helper/marker-merge.ts +116 -0
  289. package/src/helper/prompt.ts +61 -0
  290. package/src/helper/repo-root.ts +29 -0
  291. package/src/helper/settings-merge.ts +151 -0
  292. package/src/helper/settings-scope.ts +33 -0
  293. package/src/helper/toml.ts +244 -0
  294. package/src/install/backup-set.ts +149 -0
  295. package/src/install/backup.ts +724 -0
  296. package/src/install/context.ts +58 -0
  297. package/src/install/manifest.ts +145 -0
  298. package/src/install/mcp.ts +254 -0
  299. package/src/install/paths.ts +84 -0
  300. package/src/install/targets.ts +90 -0
  301. package/src/install/tool-lifecycle.ts +151 -0
  302. package/src/lifecycle/bash-availability.ts +25 -0
  303. package/src/lifecycle/index.ts +812 -0
  304. package/src/lifecycle/mcp-index.ts +28 -0
  305. package/src/lifecycle/policies.ts +73 -0
  306. package/src/lifecycle/view.ts +175 -0
  307. package/src/registry/index.ts +611 -0
  308. package/src/release/sync-plugin-versions.ts +66 -0
  309. package/src/runtime/advice/cli.ts +283 -0
  310. package/src/runtime/advice/client.ts +324 -0
  311. package/src/runtime/advice/config.ts +189 -0
  312. package/src/runtime/advice/doctor.ts +169 -0
  313. package/src/runtime/advice/endpoint-guard.ts +134 -0
  314. package/src/runtime/advice/kinds.ts +154 -0
  315. package/src/runtime/advice/providers/ollama-systemone.ts +209 -0
  316. package/src/runtime/advice/record.ts +166 -0
  317. package/src/runtime/advice/registry.ts +170 -0
  318. package/src/runtime/advice/strength.ts +66 -0
  319. package/src/runtime/brief-tools.ts +342 -0
  320. package/src/runtime/c4-project.ts +371 -0
  321. package/src/runtime/capability-router.ts +484 -0
  322. package/src/runtime/checkouts.ts +170 -0
  323. package/src/runtime/claims.ts +468 -0
  324. package/src/runtime/cli-result.ts +55 -0
  325. package/src/runtime/cli.ts +678 -0
  326. package/src/runtime/command-detect.ts +516 -0
  327. package/src/runtime/context-pack.ts +492 -0
  328. package/src/runtime/decision-register.ts +493 -0
  329. package/src/runtime/evidence-ledger.ts +338 -0
  330. package/src/runtime/failure/capture.ts +272 -0
  331. package/src/runtime/failure/classifier.ts +68 -0
  332. package/src/runtime/failure/cli.ts +178 -0
  333. package/src/runtime/failure/home.ts +64 -0
  334. package/src/runtime/failure/store.ts +226 -0
  335. package/src/runtime/feature-resolve.ts +79 -0
  336. package/src/runtime/freshness.ts +177 -0
  337. package/src/runtime/health.ts +863 -0
  338. package/src/runtime/history-compactor.ts +237 -0
  339. package/src/runtime/implementation-gate.ts +321 -0
  340. package/src/runtime/inventory/coverage.ts +218 -0
  341. package/src/runtime/inventory/identity.ts +65 -0
  342. package/src/runtime/inventory/index.ts +705 -0
  343. package/src/runtime/inventory/precedence.ts +134 -0
  344. package/src/runtime/inventory/read-scopes.ts +131 -0
  345. package/src/runtime/inventory/siblings.ts +192 -0
  346. package/src/runtime/knowledge/chunker.ts +124 -0
  347. package/src/runtime/knowledge/hybrid.ts +149 -0
  348. package/src/runtime/knowledge/index-store.ts +145 -0
  349. package/src/runtime/knowledge/retrieval.ts +194 -0
  350. package/src/runtime/leak-scan.ts +185 -0
  351. package/src/runtime/lifecycle/cli.ts +276 -0
  352. package/src/runtime/lifecycle/event-store.ts +340 -0
  353. package/src/runtime/lifecycle/fold.ts +353 -0
  354. package/src/runtime/lifecycle/followup.ts +432 -0
  355. package/src/runtime/lifecycle/goal.ts +249 -0
  356. package/src/runtime/lifecycle/intent-writer.ts +86 -0
  357. package/src/runtime/lifecycle/overview.ts +292 -0
  358. package/src/runtime/lifecycle/release.ts +241 -0
  359. package/src/runtime/lifecycle/report-store.ts +221 -0
  360. package/src/runtime/lifecycle/retention.ts +212 -0
  361. package/src/runtime/lifecycle/root.ts +71 -0
  362. package/src/runtime/lifecycle/status.ts +353 -0
  363. package/src/runtime/lifecycle/store-upkeep.ts +393 -0
  364. package/src/runtime/locator-resolve.ts +158 -0
  365. package/src/runtime/mask.ts +210 -0
  366. package/src/runtime/mcp-consultations.ts +412 -0
  367. package/src/runtime/mcp-reach.ts +157 -0
  368. package/src/runtime/model-router.ts +153 -0
  369. package/src/runtime/outcome.ts +467 -0
  370. package/src/runtime/reach.ts +148 -0
  371. package/src/runtime/readiness-record.ts +150 -0
  372. package/src/runtime/readiness.ts +435 -0
  373. package/src/runtime/recovery.ts +282 -0
  374. package/src/runtime/research-request.ts +231 -0
  375. package/src/runtime/retrieval-plan.ts +578 -0
  376. package/src/runtime/scaffold/artifacts.ts +217 -0
  377. package/src/runtime/scaffold/fingerprint.ts +56 -0
  378. package/src/runtime/scaffold/generate.ts +911 -0
  379. package/src/runtime/scaffold/languages.ts +245 -0
  380. package/src/runtime/storage/cli.ts +64 -0
  381. package/src/runtime/storage/engine.ts +249 -0
  382. package/src/runtime/storage/registry.ts +130 -0
  383. package/src/runtime/storage/roots.ts +63 -0
  384. package/src/runtime/storage/rules.ts +282 -0
  385. package/src/runtime/storage/upkeep.ts +243 -0
  386. package/src/runtime/storage/work-status.ts +326 -0
  387. package/src/runtime/task-attempts.ts +415 -0
  388. package/src/runtime/task-classifier.ts +550 -0
  389. package/src/runtime/task-scope.ts +130 -0
  390. package/src/runtime/task-state.ts +176 -0
  391. package/src/runtime/trace/indicators.ts +235 -0
  392. package/src/runtime/trace/ledger.ts +626 -0
  393. package/src/runtime/trace/render.ts +76 -0
  394. package/src/runtime/trace/views.ts +459 -0
  395. package/src/runtime/verification/contract-runner.ts +341 -0
  396. package/src/runtime/verification/engine.ts +1461 -0
  397. package/src/runtime/verification/registry.ts +130 -0
  398. package/src/runtime/verification/scope-bound.ts +418 -0
  399. package/src/runtime/verification/test-selection.ts +172 -0
  400. package/src/runtime/workflow-engine.ts +588 -0
  401. package/src/runtime/workflow-orchestrator.ts +953 -0
  402. package/src/runtime/worktree.ts +340 -0
  403. package/src/state/index.ts +193 -0
  404. package/src/state/lockfile.ts +136 -0
  405. package/src/state/run-lock.ts +408 -0
  406. package/types/node-commonjs.d.ts +68 -0
  407. package/types/runtime.d.ts +91 -0
@@ -0,0 +1,620 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ PR Analyzer
4
+
5
+ Analyzes pull request changes for review complexity, risk assessment,
6
+ and generates review priorities.
7
+
8
+ Usage:
9
+ python pr_analyzer.py /path/to/repo
10
+ python pr_analyzer.py . --base main --head feature-branch
11
+ python pr_analyzer.py /path/to/repo --json
12
+ """
13
+
14
+ import argparse
15
+ import json
16
+ import os
17
+ import re
18
+ import subprocess
19
+ import sys
20
+ from pathlib import Path
21
+ from typing import Dict, List, Optional, Tuple
22
+
23
+
24
+ # File categories for review prioritization
25
+ FILE_CATEGORIES = {
26
+ "critical": {
27
+ "patterns": [
28
+ r"auth", r"security", r"password", r"token", r"secret",
29
+ r"payment", r"billing", r"crypto", r"encrypt"
30
+ ],
31
+ "weight": 5,
32
+ "description": "Security-sensitive files requiring careful review"
33
+ },
34
+ "high": {
35
+ "patterns": [
36
+ r"api", r"database", r"migration", r"schema", r"model",
37
+ r"config", r"env", r"middleware"
38
+ ],
39
+ "weight": 4,
40
+ "description": "Core infrastructure files"
41
+ },
42
+ "medium": {
43
+ "patterns": [
44
+ r"service", r"controller", r"handler", r"util", r"helper"
45
+ ],
46
+ "weight": 3,
47
+ "description": "Business logic files"
48
+ },
49
+ "low": {
50
+ "patterns": [
51
+ r"test", r"spec", r"mock", r"fixture", r"story",
52
+ r"readme", r"docs", r"\.md$"
53
+ ],
54
+ "weight": 1,
55
+ "description": "Tests and documentation"
56
+ }
57
+ }
58
+
59
+ # Risky patterns to flag
60
+ RISK_PATTERNS = [
61
+ {
62
+ "name": "hardcoded_secrets",
63
+ # comments: a committed secret is a secret wherever it sits, including commented-out code.
64
+ "comments": True,
65
+ # pattern is written lowercase and must catch Password:, API_KEY, Token=.
66
+ "ignorecase": True,
67
+ "pattern": r"(password|secret|api_key|token|connection_?string)\s*[=:]\s*['\"][^'\"]+['\"]",
68
+ "severity": "critical",
69
+ "message": "Potential hardcoded secret or connection string detected"
70
+ },
71
+ {
72
+ "name": "todo_fixme",
73
+ # comments: TODO/FIXME/HACK markers are written as comments; skipping comments would disable this rule entirely.
74
+ "comments": True,
75
+ # pattern is written uppercase and must catch todo:, Fixme:.
76
+ "ignorecase": True,
77
+ "pattern": r"(TODO|FIXME|HACK|XXX):",
78
+ "severity": "low",
79
+ "message": "TODO/FIXME comment found"
80
+ },
81
+ {
82
+ "name": "console_log",
83
+ # comments: a commented-out console.log is not debug output left in production.
84
+ "comments": False,
85
+ # console.log, Debug.WriteLine, System.out.println are exact-case API names.
86
+ "ignorecase": False,
87
+ "pattern": (
88
+ r"console\.(log|debug|info|warn|error)\(|\bDebug\.WriteLine\(|"
89
+ r"\bSystem\.out\.print(?:ln)?\(|\.printStackTrace\("
90
+ ),
91
+ "severity": "medium",
92
+ "message": (
93
+ "Debug output statement found "
94
+ "(console.* / Debug.WriteLine / System.out / printStackTrace)"
95
+ )
96
+ },
97
+ {
98
+ "name": "debugger",
99
+ # comments: a commented-out debugger statement is inert.
100
+ "comments": False,
101
+ # the JavaScript `debugger` keyword is lowercase.
102
+ "ignorecase": False,
103
+ "pattern": r"\bdebugger\b",
104
+ "severity": "high",
105
+ "message": "Debugger statement found"
106
+ },
107
+ {
108
+ "name": "analyzer_disable",
109
+ # comments: eslint-disable and @SuppressWarnings are comment syntax.
110
+ "comments": True,
111
+ # eslint-disable, [SuppressMessage, @SuppressWarnings are exact-case tokens.
112
+ "ignorecase": False,
113
+ "pattern": (
114
+ r"eslint-disable|#pragma\s+warning\s+disable|\[SuppressMessage|"
115
+ r"@SuppressWarnings"
116
+ ),
117
+ "severity": "medium",
118
+ "message": (
119
+ "Static-analyzer rule disabled "
120
+ "(ESLint / Roslyn / SuppressMessage / @SuppressWarnings)"
121
+ )
122
+ },
123
+ {
124
+ "name": "loose_type",
125
+ # comments: a type annotation inside a comment declares nothing.
126
+ "comments": False,
127
+ # the TypeScript `any` and C# `dynamic` keywords are lowercase.
128
+ "ignorecase": False,
129
+ "pattern": r":\s*any\b|\bdynamic\s+\w+\s*[=;]",
130
+ "severity": "medium",
131
+ "message": "Loose type used (TypeScript 'any' or C# 'dynamic')"
132
+ },
133
+ {
134
+ "name": "sql_concatenation",
135
+ # comments: commented-out SQL executes nothing.
136
+ "comments": False,
137
+ # SQL is written either case; the pattern spells the keywords uppercase.
138
+ "ignorecase": True,
139
+ "pattern": r"(SELECT|INSERT|UPDATE|DELETE).*\+.*['\"]|(?:FromSql|ExecuteSql)\w*\([^)]*\$\"",
140
+ "severity": "critical",
141
+ "message": "Potential SQL injection (string concatenation or interpolation in query)"
142
+ },
143
+ {
144
+ "name": "csharp_unsafe_block",
145
+ # comments: commented-out code compiles nothing.
146
+ "comments": False,
147
+ # C# keywords are lowercase and exact.
148
+ "ignorecase": False,
149
+ "pattern": (
150
+ r"\bunsafe\s+(?:\{|public|private|protected|internal|static|sealed|"
151
+ r"partial|class|struct|void|int|string|long|short|byte|double|float|"
152
+ r"bool|char|ref|out|fixed)\b"
153
+ ),
154
+ "severity": "high",
155
+ "message": "C# 'unsafe' code — requires memory-safety review"
156
+ },
157
+ {
158
+ "name": "csharp_null_forgiving",
159
+ # comments: commented-out code compiles nothing.
160
+ "comments": False,
161
+ # punctuation only, so case cannot apply.
162
+ "ignorecase": False,
163
+ "pattern": r"(?:\)\s*!\.|\w+!\.\w+)",
164
+ "severity": "medium",
165
+ "message": "Null-forgiving operator (!) used — verify the value is truly non-null"
166
+ },
167
+ {
168
+ "name": "csharp_async_void",
169
+ # comments: commented-out code compiles nothing.
170
+ "comments": False,
171
+ # C# keywords are lowercase and exact.
172
+ "ignorecase": False,
173
+ "pattern": r"\basync\s+void\s+\w+\s*\(",
174
+ "severity": "high",
175
+ "message": "'async void' method — use only for event handlers"
176
+ },
177
+ {
178
+ "name": "csharp_blocking_async",
179
+ # comments: commented-out code runs nothing — and the comments documenting this rule name .Result, which is how the rule flagged its own documentation.
180
+ "comments": False,
181
+ # `.Result`/`.Wait()`/`.GetAwaiter()` are PascalCase C# members; matching them case-insensitively made JavaScript's `...result` spread a high-severity ASP.NET deadlock finding.
182
+ "ignorecase": False,
183
+ "pattern": r"\.(?:Result\b|Wait\(\)|GetAwaiter\(\)\.GetResult\(\))",
184
+ "severity": "high",
185
+ "message": "Blocking call on async operation — can deadlock in ASP.NET contexts"
186
+ }
187
+ ]
188
+
189
+
190
+ def run_git_command(cmd: List[str], cwd: Path) -> Tuple[bool, str]:
191
+ """Run a git command and return success status and output."""
192
+ try:
193
+ result = subprocess.run(
194
+ cmd,
195
+ cwd=cwd,
196
+ capture_output=True,
197
+ text=True,
198
+ timeout=30
199
+ )
200
+ return result.returncode == 0, result.stdout.strip()
201
+ except subprocess.TimeoutExpired:
202
+ return False, "Command timed out"
203
+ except Exception as e:
204
+ return False, str(e)
205
+
206
+
207
+ def get_changed_files(repo_path: Path, base: str, head: str) -> List[Dict]:
208
+ """Get list of changed files between two refs."""
209
+ success, output = run_git_command(
210
+ ["git", "diff", "--name-status", f"{base}...{head}"],
211
+ repo_path
212
+ )
213
+
214
+ if not success:
215
+ # Try without the triple dot (for uncommitted changes)
216
+ success, output = run_git_command(
217
+ ["git", "diff", "--name-status", base, head],
218
+ repo_path
219
+ )
220
+
221
+ if not success or not output:
222
+ # Fall back to staged changes
223
+ success, output = run_git_command(
224
+ ["git", "diff", "--name-status", "--cached"],
225
+ repo_path
226
+ )
227
+
228
+ files = []
229
+ for line in output.split("\n"):
230
+ if not line.strip():
231
+ continue
232
+ parts = line.split("\t")
233
+ if len(parts) >= 2:
234
+ status = parts[0][0] # First character of status
235
+ filepath = parts[-1] # Handle renames (R100\told\tnew)
236
+ status_map = {
237
+ "A": "added",
238
+ "M": "modified",
239
+ "D": "deleted",
240
+ "R": "renamed",
241
+ "C": "copied"
242
+ }
243
+ files.append({
244
+ "path": filepath,
245
+ "status": status_map.get(status, "modified")
246
+ })
247
+
248
+ return files
249
+
250
+
251
+ def get_file_diff(repo_path: Path, filepath: str, base: str, head: str) -> str:
252
+ """Get diff content for a specific file."""
253
+ success, output = run_git_command(
254
+ ["git", "diff", f"{base}...{head}", "--", filepath],
255
+ repo_path
256
+ )
257
+ if not success:
258
+ success, output = run_git_command(
259
+ ["git", "diff", "--cached", "--", filepath],
260
+ repo_path
261
+ )
262
+ return output if success else ""
263
+
264
+
265
+ def categorize_file(filepath: str) -> Tuple[str, int]:
266
+ """Categorize a file based on its path and name."""
267
+ filepath_lower = filepath.lower()
268
+
269
+ for category, info in FILE_CATEGORIES.items():
270
+ for pattern in info["patterns"]:
271
+ if re.search(pattern, filepath_lower):
272
+ return category, info["weight"]
273
+
274
+ return "medium", 2 # Default category
275
+
276
+
277
+ # Case sensitivity is per pattern and has no default: a language-specific pattern matched
278
+ # case-insensitively reports findings it has not earned, and a prose-shaped one matched
279
+ # case-sensitively misses findings it should have. Declaring it once per pattern is the only way
280
+ # both stay right, so an undeclared pattern is refused here rather than silently taking a default.
281
+ def strip_comments(content: str) -> str:
282
+ """
283
+ Blank comment text, preserving newlines so line-based reasoning still lines up.
284
+
285
+ Most rules here describe things a compiler or runtime acts on, and a commented-out one acts on
286
+ nothing. Scanning comments made this analyser report its own documentation: each comment
287
+ explaining why a rule exists contained the very token that rule matches, so the explanations
288
+ were reported as findings against the file that held them.
289
+
290
+ This docstring deliberately names no rule's literal token. A docstring is a string, not a
291
+ comment, so nothing below strips it — and quoting the tokens here reintroduced two high-severity
292
+ self-matches after the comment fix had removed them. Describing the tokens is enough; the rule
293
+ table below is where they belong.
294
+
295
+ Which rules skip comments is declared per rule below, not decided here. Three of them must read
296
+ comments to work at all — TODO markers, eslint-disable and @SuppressWarnings are comment syntax,
297
+ and a secret is a secret wherever it sits. Stripping globally would have silently disabled them.
298
+ """
299
+ content = re.sub(r"/\*[\s\S]*?\*/", lambda m: "\n" * m.group(0).count("\n"), content)
300
+ out = []
301
+ for line in content.split("\n"):
302
+ stripped = line.lstrip()
303
+ out.append("" if stripped.startswith(("//", "#", "*")) else re.sub(r"\s(?://|#).*$", "", line))
304
+ return "\n".join(out)
305
+
306
+
307
+ _undeclared = [r["name"] for r in RISK_PATTERNS if "ignorecase" not in r or "comments" not in r]
308
+ if _undeclared:
309
+ raise ValueError(
310
+ "every RISK_PATTERNS entry must declare both 'ignorecase' and 'comments'; missing on: " + ", ".join(_undeclared)
311
+ )
312
+
313
+
314
+ def analyze_diff_for_risks(diff_content: str, filepath: str) -> List[Dict]:
315
+ """Analyze diff content for risky patterns."""
316
+ risks = []
317
+
318
+ # Only analyze added lines (starting with +)
319
+ added_lines = [
320
+ line[1:] for line in diff_content.split("\n")
321
+ if line.startswith("+") and not line.startswith("+++")
322
+ ]
323
+
324
+ content = "\n".join(added_lines)
325
+
326
+ code_only = strip_comments(content)
327
+
328
+ for risk in RISK_PATTERNS:
329
+ flags = re.IGNORECASE if risk["ignorecase"] else 0
330
+ haystack = content if risk["comments"] else code_only
331
+ matches = re.findall(risk["pattern"], haystack, flags)
332
+ if matches:
333
+ risks.append({
334
+ "name": risk["name"],
335
+ "severity": risk["severity"],
336
+ "message": risk["message"],
337
+ "file": filepath,
338
+ "count": len(matches)
339
+ })
340
+
341
+ return risks
342
+
343
+
344
+ def count_changes(diff_content: str) -> Dict[str, int]:
345
+ """Count additions and deletions in diff."""
346
+ additions = 0
347
+ deletions = 0
348
+
349
+ for line in diff_content.split("\n"):
350
+ if line.startswith("+") and not line.startswith("+++"):
351
+ additions += 1
352
+ elif line.startswith("-") and not line.startswith("---"):
353
+ deletions += 1
354
+
355
+ return {"additions": additions, "deletions": deletions}
356
+
357
+
358
+ def calculate_complexity_score(files: List[Dict], all_risks: List[Dict]) -> int:
359
+ """Calculate overall PR complexity score (1-10)."""
360
+ score = 0
361
+
362
+ # File count contribution (max 3 points)
363
+ file_count = len(files)
364
+ if file_count > 20:
365
+ score += 3
366
+ elif file_count > 10:
367
+ score += 2
368
+ elif file_count > 5:
369
+ score += 1
370
+
371
+ # Total changes contribution (max 3 points)
372
+ total_changes = sum(f.get("additions", 0) + f.get("deletions", 0) for f in files)
373
+ if total_changes > 500:
374
+ score += 3
375
+ elif total_changes > 200:
376
+ score += 2
377
+ elif total_changes > 50:
378
+ score += 1
379
+
380
+ # Risk severity contribution (max 4 points)
381
+ critical_risks = sum(1 for r in all_risks if r["severity"] == "critical")
382
+ high_risks = sum(1 for r in all_risks if r["severity"] == "high")
383
+
384
+ score += min(2, critical_risks)
385
+ score += min(2, high_risks)
386
+
387
+ return min(10, max(1, score))
388
+
389
+
390
+ def analyze_commit_messages(repo_path: Path, base: str, head: str) -> Dict:
391
+ """Analyze commit messages in the PR."""
392
+ success, output = run_git_command(
393
+ ["git", "log", "--oneline", f"{base}...{head}"],
394
+ repo_path
395
+ )
396
+
397
+ if not success or not output:
398
+ return {"commits": 0, "issues": []}
399
+
400
+ commits = output.strip().split("\n")
401
+ issues = []
402
+
403
+ for commit in commits:
404
+ if len(commit) < 10:
405
+ continue
406
+
407
+ # Check for conventional commit format
408
+ message = commit[8:] if len(commit) > 8 else commit # Skip hash
409
+
410
+ if not re.match(r"^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?:", message):
411
+ issues.append({
412
+ "commit": commit[:7],
413
+ "issue": "Does not follow conventional commit format"
414
+ })
415
+
416
+ if len(message) > 72:
417
+ issues.append({
418
+ "commit": commit[:7],
419
+ "issue": "Commit message exceeds 72 characters"
420
+ })
421
+
422
+ return {
423
+ "commits": len(commits),
424
+ "issues": issues
425
+ }
426
+
427
+
428
+ def analyze_pr(
429
+ repo_path: Path,
430
+ base: str = "main",
431
+ head: str = "HEAD"
432
+ ) -> Dict:
433
+ """Perform complete PR analysis."""
434
+ # Get changed files
435
+ changed_files = get_changed_files(repo_path, base, head)
436
+
437
+ if not changed_files:
438
+ return {
439
+ "status": "no_changes",
440
+ "message": "No changes detected between branches"
441
+ }
442
+
443
+ # Analyze each file
444
+ all_risks = []
445
+ file_analyses = []
446
+
447
+ for file_info in changed_files:
448
+ filepath = file_info["path"]
449
+ category, weight = categorize_file(filepath)
450
+
451
+ # Get diff for the file
452
+ diff = get_file_diff(repo_path, filepath, base, head)
453
+ changes = count_changes(diff)
454
+ risks = analyze_diff_for_risks(diff, filepath)
455
+
456
+ all_risks.extend(risks)
457
+
458
+ file_analyses.append({
459
+ "path": filepath,
460
+ "status": file_info["status"],
461
+ "category": category,
462
+ "priority_weight": weight,
463
+ "additions": changes["additions"],
464
+ "deletions": changes["deletions"],
465
+ "risks": risks
466
+ })
467
+
468
+ # Sort by priority (highest first)
469
+ file_analyses.sort(key=lambda x: (-x["priority_weight"], x["path"]))
470
+
471
+ # Analyze commits
472
+ commit_analysis = analyze_commit_messages(repo_path, base, head)
473
+
474
+ # Calculate metrics
475
+ complexity = calculate_complexity_score(file_analyses, all_risks)
476
+
477
+ total_additions = sum(f["additions"] for f in file_analyses)
478
+ total_deletions = sum(f["deletions"] for f in file_analyses)
479
+
480
+ return {
481
+ "status": "analyzed",
482
+ "summary": {
483
+ "files_changed": len(file_analyses),
484
+ "total_additions": total_additions,
485
+ "total_deletions": total_deletions,
486
+ "complexity_score": complexity,
487
+ "complexity_label": get_complexity_label(complexity),
488
+ "commits": commit_analysis["commits"]
489
+ },
490
+ "risks": {
491
+ "critical": [r for r in all_risks if r["severity"] == "critical"],
492
+ "high": [r for r in all_risks if r["severity"] == "high"],
493
+ "medium": [r for r in all_risks if r["severity"] == "medium"],
494
+ "low": [r for r in all_risks if r["severity"] == "low"]
495
+ },
496
+ "files": file_analyses,
497
+ "commit_issues": commit_analysis["issues"],
498
+ "review_order": [f["path"] for f in file_analyses[:10]] # Top 10 priority files
499
+ }
500
+
501
+
502
+ def get_complexity_label(score: int) -> str:
503
+ """Get human-readable complexity label."""
504
+ if score <= 2:
505
+ return "Simple"
506
+ elif score <= 4:
507
+ return "Moderate"
508
+ elif score <= 6:
509
+ return "Complex"
510
+ elif score <= 8:
511
+ return "Very Complex"
512
+ else:
513
+ return "Critical"
514
+
515
+
516
+ def print_report(analysis: Dict) -> None:
517
+ """Print human-readable analysis report."""
518
+ if analysis["status"] == "no_changes":
519
+ print("No changes detected.")
520
+ return
521
+
522
+ summary = analysis["summary"]
523
+ risks = analysis["risks"]
524
+
525
+ print("=" * 60)
526
+ print("PR ANALYSIS REPORT")
527
+ print("=" * 60)
528
+
529
+ print(f"\nComplexity: {summary['complexity_score']}/10 ({summary['complexity_label']})")
530
+ print(f"Files Changed: {summary['files_changed']}")
531
+ print(f"Lines: +{summary['total_additions']} / -{summary['total_deletions']}")
532
+ print(f"Commits: {summary['commits']}")
533
+
534
+ # Risk summary
535
+ print("\n--- RISK SUMMARY ---")
536
+ print(f"Critical: {len(risks['critical'])}")
537
+ print(f"High: {len(risks['high'])}")
538
+ print(f"Medium: {len(risks['medium'])}")
539
+ print(f"Low: {len(risks['low'])}")
540
+
541
+ # Critical and high risks details
542
+ if risks["critical"]:
543
+ print("\n--- CRITICAL RISKS ---")
544
+ for risk in risks["critical"]:
545
+ print(f" [{risk['file']}] {risk['message']} (x{risk['count']})")
546
+
547
+ if risks["high"]:
548
+ print("\n--- HIGH RISKS ---")
549
+ for risk in risks["high"]:
550
+ print(f" [{risk['file']}] {risk['message']} (x{risk['count']})")
551
+
552
+ # Commit message issues
553
+ if analysis["commit_issues"]:
554
+ print("\n--- COMMIT MESSAGE ISSUES ---")
555
+ for issue in analysis["commit_issues"][:5]:
556
+ print(f" {issue['commit']}: {issue['issue']}")
557
+
558
+ # Review order
559
+ print("\n--- SUGGESTED REVIEW ORDER ---")
560
+ for i, filepath in enumerate(analysis["review_order"], 1):
561
+ file_info = next(f for f in analysis["files"] if f["path"] == filepath)
562
+ print(f" {i}. [{file_info['category'].upper()}] {filepath}")
563
+
564
+ print("\n" + "=" * 60)
565
+
566
+
567
+ def main():
568
+ parser = argparse.ArgumentParser(
569
+ description="Analyze pull request for review complexity and risks"
570
+ )
571
+ parser.add_argument(
572
+ "repo_path",
573
+ nargs="?",
574
+ default=".",
575
+ help="Path to git repository (default: current directory)"
576
+ )
577
+ parser.add_argument(
578
+ "--base", "-b",
579
+ default="main",
580
+ help="Base branch for comparison (default: main)"
581
+ )
582
+ parser.add_argument(
583
+ "--head",
584
+ default="HEAD",
585
+ help="Head branch/commit for comparison (default: HEAD)"
586
+ )
587
+ parser.add_argument(
588
+ "--json",
589
+ action="store_true",
590
+ help="Output in JSON format"
591
+ )
592
+ parser.add_argument(
593
+ "--output", "-o",
594
+ help="Write output to file"
595
+ )
596
+
597
+ args = parser.parse_args()
598
+
599
+ repo_path = Path(args.repo_path).resolve()
600
+
601
+ if not (repo_path / ".git").exists():
602
+ print(f"Error: {repo_path} is not a git repository", file=sys.stderr)
603
+ sys.exit(1)
604
+
605
+ analysis = analyze_pr(repo_path, args.base, args.head)
606
+
607
+ if args.json:
608
+ output = json.dumps(analysis, indent=2)
609
+ if args.output:
610
+ with open(args.output, "w") as f:
611
+ f.write(output)
612
+ print(f"Results written to {args.output}")
613
+ else:
614
+ print(output)
615
+ else:
616
+ print_report(analysis)
617
+
618
+
619
+ if __name__ == "__main__":
620
+ main()