pactkit 2.9.2__tar.gz → 2.9.5__tar.gz

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 (339) hide show
  1. {pactkit-2.9.2 → pactkit-2.9.5}/.github/workflows/ci.yml +2 -2
  2. {pactkit-2.9.2 → pactkit-2.9.5}/.github/workflows/pactkit.yml +1 -1
  3. {pactkit-2.9.2 → pactkit-2.9.5}/.gitignore +6 -0
  4. {pactkit-2.9.2 → pactkit-2.9.5}/.opencode/pactkit.yaml +7 -4
  5. {pactkit-2.9.2 → pactkit-2.9.5}/CHANGELOG.md +21 -0
  6. {pactkit-2.9.2 → pactkit-2.9.5}/PKG-INFO +5 -6
  7. {pactkit-2.9.2 → pactkit-2.9.5}/pyproject.toml +5 -9
  8. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/__init__.py +1 -1
  9. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/cli.py +60 -5
  10. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/config.py +74 -5
  11. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/context_gen.py +103 -1
  12. pactkit-2.9.5/src/pactkit/garden.py +387 -0
  13. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/deployer.py +2 -0
  14. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/lazy_visualize.py +25 -11
  15. pactkit-2.9.5/src/pactkit/lessons.py +160 -0
  16. pactkit-2.9.5/src/pactkit/observe.py +193 -0
  17. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/profiles.py +22 -0
  18. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/__init__.py +1 -0
  19. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/agents.py +8 -0
  20. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/commands.py +48 -51
  21. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/rules.py +12 -0
  22. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/skills.py +40 -1
  23. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/workflows.py +5 -5
  24. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/schemas.py +4 -0
  25. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/visualize.py +392 -30
  26. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/validators.py +13 -0
  27. {pactkit-2.9.2 → pactkit-2.9.5}/tests/e2e/cli/test_cli_e2e.py +105 -0
  28. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug002_plugin_paths.py +7 -9
  29. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug017_project_init_playbook.py +4 -4
  30. pactkit-2.9.5/tests/unit/test_bug_slim001_env_detection.py +50 -0
  31. pactkit-2.9.5/tests/unit/test_check_extensions_072_073.py +292 -0
  32. pactkit-2.9.5/tests/unit/test_check_playbook_072_073.py +51 -0
  33. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_config.py +2 -1
  34. pactkit-2.9.5/tests/unit/test_context_gen.py +285 -0
  35. pactkit-2.9.5/tests/unit/test_garden.py +336 -0
  36. pactkit-2.9.5/tests/unit/test_init_playbook_074.py +204 -0
  37. pactkit-2.9.5/tests/unit/test_lessons_rotation_075.py +200 -0
  38. pactkit-2.9.5/tests/unit/test_observe.py +186 -0
  39. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_pdca_slim.py +6 -4
  40. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_prompt_cli_refs.py +1 -1
  41. pactkit-2.9.5/tests/unit/test_prompt_quality_075.py +157 -0
  42. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_prompt_structural_invariants.py +2 -2
  43. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_selective_deploy.py +6 -5
  44. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story017_init_claude_md.py +7 -4
  45. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story063_prompt_slimming.py +2 -1
  46. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_validators.py +3 -0
  47. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim063.py +2 -2
  48. pactkit-2.9.5/tests/unit/test_visualize_call_nested.py +217 -0
  49. pactkit-2.9.5/tests/unit/test_visualize_chain_fix.py +229 -0
  50. pactkit-2.9.5/tests/unit/test_visualize_multilang_chain.py +230 -0
  51. pactkit-2.9.2/src/pactkit/lessons.py +0 -80
  52. pactkit-2.9.2/tests/unit/test_bug_slim001_env_detection.py +0 -70
  53. {pactkit-2.9.2 → pactkit-2.9.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  54. {pactkit-2.9.2 → pactkit-2.9.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  55. {pactkit-2.9.2 → pactkit-2.9.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  56. {pactkit-2.9.2 → pactkit-2.9.5}/.github/dependabot.yml +0 -0
  57. {pactkit-2.9.2 → pactkit-2.9.5}/.github/workflows/publish.yml +0 -0
  58. {pactkit-2.9.2 → pactkit-2.9.5}/AGENTS.md +0 -0
  59. {pactkit-2.9.2 → pactkit-2.9.5}/CODE_OF_CONDUCT.md +0 -0
  60. {pactkit-2.9.2 → pactkit-2.9.5}/CONTRIBUTING.md +0 -0
  61. {pactkit-2.9.2 → pactkit-2.9.5}/LICENSE +0 -0
  62. {pactkit-2.9.2 → pactkit-2.9.5}/README.md +0 -0
  63. {pactkit-2.9.2 → pactkit-2.9.5}/SECURITY.md +0 -0
  64. {pactkit-2.9.2 → pactkit-2.9.5}/docs/assets/logo.png +0 -0
  65. {pactkit-2.9.2 → pactkit-2.9.5}/docs/guides/codex-integration-preresearch.md +0 -0
  66. {pactkit-2.9.2 → pactkit-2.9.5}/docs/guides/tool-integration-checklist.md +0 -0
  67. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-001.md +0 -0
  68. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-002.md +0 -0
  69. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-003.md +0 -0
  70. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-004.md +0 -0
  71. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-005.md +0 -0
  72. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-006.md +0 -0
  73. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-007.md +0 -0
  74. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-008.md +0 -0
  75. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-009.md +0 -0
  76. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-010.md +0 -0
  77. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-011.md +0 -0
  78. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-012.md +0 -0
  79. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-013.md +0 -0
  80. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-014.md +0 -0
  81. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-015.md +0 -0
  82. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-016.md +0 -0
  83. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-017.md +0 -0
  84. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-018.md +0 -0
  85. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-019.md +0 -0
  86. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-020.md +0 -0
  87. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-021.md +0 -0
  88. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-022.md +0 -0
  89. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-023.md +0 -0
  90. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-024.md +0 -0
  91. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-025.md +0 -0
  92. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-026.md +0 -0
  93. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-027.md +0 -0
  94. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-028.md +0 -0
  95. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-029.md +0 -0
  96. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-030.md +0 -0
  97. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-031.md +0 -0
  98. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-032.md +0 -0
  99. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-033.md +0 -0
  100. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-034.md +0 -0
  101. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-035.md +0 -0
  102. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-001.md +0 -0
  103. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-002.md +0 -0
  104. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-003.md +0 -0
  105. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-004.md +0 -0
  106. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-005.md +0 -0
  107. {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-006.md +0 -0
  108. {pactkit-2.9.2 → pactkit-2.9.5}/docs/test_cases/BUG-001_case.md +0 -0
  109. {pactkit-2.9.2 → pactkit-2.9.5}/docs/test_cases/BUG-002_case.md +0 -0
  110. {pactkit-2.9.2 → pactkit-2.9.5}/opencode.json +0 -0
  111. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/__main__.py +0 -0
  112. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/backfill.py +0 -0
  113. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/cleaners.py +0 -0
  114. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/coverage_gate.py +0 -0
  115. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/doctor.py +0 -0
  116. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/__init__.py +0 -0
  117. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/adapter.py +0 -0
  118. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/deploy_base.py +0 -0
  119. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/guards.py +0 -0
  120. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/id_generator.py +0 -0
  121. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/invariants.py +0 -0
  122. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/issue_sync.py +0 -0
  123. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/lint_runner.py +0 -0
  124. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/references.py +0 -0
  125. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/regression.py +0 -0
  126. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/scripts.py +0 -0
  127. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/sec_scope.py +0 -0
  128. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/__init__.py +0 -0
  129. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/board.py +0 -0
  130. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/scaffold.py +0 -0
  131. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/spec_linter.py +0 -0
  132. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/spec_status.py +0 -0
  133. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/test_mapper.py +0 -0
  134. {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/utils.py +0 -0
  135. {pactkit-2.9.2 → pactkit-2.9.5}/tests/conftest.py +0 -0
  136. {pactkit-2.9.2 → pactkit-2.9.5}/tests/e2e/__init__.py +0 -0
  137. {pactkit-2.9.2 → pactkit-2.9.5}/tests/e2e/cli/__init__.py +0 -0
  138. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/agents_dir/researcher.yaml +0 -0
  139. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/agents_dir/writer.yaml +0 -0
  140. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/langgraph_app.py +0 -0
  141. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/mcp_settings.json +0 -0
  142. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/api_call_parser/axios_style.tsx +0 -0
  143. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/api_call_parser/dynamic.tsx +0 -0
  144. {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/api_call_parser/page.tsx +0 -0
  145. {pactkit-2.9.2 → pactkit-2.9.5}/tests/integration/__init__.py +0 -0
  146. {pactkit-2.9.2 → pactkit-2.9.5}/tests/integration/test_deploy_classic.py +0 -0
  147. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_agent_features.py +0 -0
  148. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_agent_frontmatter.py +0 -0
  149. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_agents_enrichment.py +0 -0
  150. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_board_bug027.py +0 -0
  151. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_board_sections.py +0 -0
  152. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug001_skill_path.py +0 -0
  153. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug003_multi_import.py +0 -0
  154. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug004_dead_set.py +0 -0
  155. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug005_archive_taskless.py +0 -0
  156. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug006_scan_excludes.py +0 -0
  157. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug007_stale_trace_refs.py +0 -0
  158. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug008_stale_command_refs.py +0 -0
  159. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug009_project_config_backfill.py +0 -0
  160. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug010_rewrite_yaml.py +0 -0
  161. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug011_stale_refs.py +0 -0
  162. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug012_call_graph_filter.py +0 -0
  163. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug013_config_single_source.py +0 -0
  164. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug014_version_hygiene.py +0 -0
  165. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug018_issue_tracker_verification.py +0 -0
  166. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug019_venv_deployment.py +0 -0
  167. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug020_claude_md_backup.py +0 -0
  168. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug025_release_delegation.py +0 -0
  169. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug026_version_sync.py +0 -0
  170. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug028_ghost_refs.py +0 -0
  171. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug029_stack_detection_fallback.py +0 -0
  172. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug030_spec_lint_cli.py +0 -0
  173. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug031_docstring_accuracy.py +0 -0
  174. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug034_plan_metadata_template.py +0 -0
  175. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_021.py +0 -0
  176. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_022.py +0 -0
  177. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_023.py +0 -0
  178. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_024.py +0 -0
  179. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim002_instruction_collision.py +0 -0
  180. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim003.py +0 -0
  181. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim004.py +0 -0
  182. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim005.py +0 -0
  183. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim006.py +0 -0
  184. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_check_command.py +0 -0
  185. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_command_frontmatter.py +0 -0
  186. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_command_visualize_modes.py +0 -0
  187. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_config_auto_merge.py +0 -0
  188. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_constitution_sharpening.py +0 -0
  189. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_create_skill.py +0 -0
  190. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_cross_flow_matrix.py +0 -0
  191. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_deploy_base.py +0 -0
  192. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_deployer_cleanup.py +0 -0
  193. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_deployer_plugin.py +0 -0
  194. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_design_command.py +0 -0
  195. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_done_gates.py +0 -0
  196. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_draw_prompt.py +0 -0
  197. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_draw_references.py +0 -0
  198. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_drawio_mcp.py +0 -0
  199. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_home_path_fix.py +0 -0
  200. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_hotfix_command.py +0 -0
  201. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_init_guard.py +0 -0
  202. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_lang_profiles.py +0 -0
  203. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_list_stories.py +0 -0
  204. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_mcp_integration.py +0 -0
  205. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_model_config.py +0 -0
  206. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_modular_constitution.py +0 -0
  207. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_monorepo_detect.py +0 -0
  208. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_multi_prefix.py +0 -0
  209. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_profiles.py +0 -0
  210. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_project_visibility.py +0 -0
  211. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_prompts_package.py +0 -0
  212. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_release.py +0 -0
  213. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_release_field.py +0 -0
  214. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_release_v110.py +0 -0
  215. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_render_prompt.py +0 -0
  216. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_review_command.py +0 -0
  217. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_rules_enrichment.py +0 -0
  218. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_scaffold.py +0 -0
  219. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_scaffold_developer_prefix.py +0 -0
  220. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_schemas.py +0 -0
  221. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_script_extraction.py +0 -0
  222. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_session_context.py +0 -0
  223. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_skill_structure.py +0 -0
  224. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_skills_enrichment.py +0 -0
  225. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_smart_regression.py +0 -0
  226. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_sprint_command.py +0 -0
  227. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_stack_references.py +0 -0
  228. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_status_command.py +0 -0
  229. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_statusline.py +0 -0
  230. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story014_release.py +0 -0
  231. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story015_ci_lint_gate.py +0 -0
  232. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story016_claude_md.py +0 -0
  233. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story018_arch_staleness.py +0 -0
  234. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story019_bailout.py +0 -0
  235. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story020_horizon.py +0 -0
  236. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story021_rfc.py +0 -0
  237. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story022_decision_tree.py +0 -0
  238. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story023_test_quality.py +0 -0
  239. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story024_native_agent.py +0 -0
  240. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story025_ci_pipeline.py +0 -0
  241. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story026_issue_tracker.py +0 -0
  242. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story027_hooks.py +0 -0
  243. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story028_rule_scoping.py +0 -0
  244. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story029_doctor.py +0 -0
  245. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story030_lint.py +0 -0
  246. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story031_git_init_guard.py +0 -0
  247. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story032_greenfield_redirect.py +0 -0
  248. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story033_config_backfill.py +0 -0
  249. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story034_plan_config_refresh.py +0 -0
  250. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story035_readme_docs.py +0 -0
  251. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story037_regression_fix.py +0 -0
  252. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story038_call_graph_update.py +0 -0
  253. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story039_venv_config.py +0 -0
  254. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story040_layered_claude_md.py +0 -0
  255. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story042_spec_linter.py +0 -0
  256. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story043_active_clarify.py +0 -0
  257. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story044_consistency_check.py +0 -0
  258. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story045_auto_pr.py +0 -0
  259. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story046_agent_adapter.py +0 -0
  260. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story047_enterprise_flags.py +0 -0
  261. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story048_worktree_isolation.py +0 -0
  262. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story049_community_standards.py +0 -0
  263. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story050_doc_only_shortcut.py +0 -0
  264. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story051_workflow_streamlining.py +0 -0
  265. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story052_conditional_github_release.py +0 -0
  266. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story053_impact_regression.py +0 -0
  267. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story055_commands.py +0 -0
  268. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story055_config.py +0 -0
  269. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story055_spec_linter.py +0 -0
  270. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story056_commands.py +0 -0
  271. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story056_config.py +0 -0
  272. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story057_implicit_cleanup.py +0 -0
  273. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story058_opencode_extraction.py +0 -0
  274. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story058_routing_fix.py +0 -0
  275. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story059_codex_removal.py +0 -0
  276. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story060_init_hang.py +0 -0
  277. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story061_remove_thinking.py +0 -0
  278. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story062_mcp_recommendations.py +0 -0
  279. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story064_venv_local_md.py +0 -0
  280. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story065_sprint_model.py +0 -0
  281. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story072_developer_prefix.py +0 -0
  282. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim009_lazy_rules.py +0 -0
  283. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim010_dry_refactor.py +0 -0
  284. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim011_command_rules.py +0 -0
  285. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim012_ci.py +0 -0
  286. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_clean.py +0 -0
  287. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_context.py +0 -0
  288. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_guard.py +0 -0
  289. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_lazy_viz.py +0 -0
  290. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_next_id.py +0 -0
  291. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_regression.py +0 -0
  292. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_sec_scope.py +0 -0
  293. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim015_doctor.py +0 -0
  294. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim016_testmap_lint.py +0 -0
  295. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim017.py +0 -0
  296. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim018.py +0 -0
  297. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim019_plan_subphases.py +0 -0
  298. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim020_explore_stall_fix.py +0 -0
  299. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim021.py +0 -0
  300. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim022.py +0 -0
  301. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim023.py +0 -0
  302. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim024.py +0 -0
  303. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim025.py +0 -0
  304. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim026.py +0 -0
  305. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim027.py +0 -0
  306. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim028.py +0 -0
  307. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim029.py +0 -0
  308. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim030.py +0 -0
  309. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim031.py +0 -0
  310. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim032.py +0 -0
  311. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim033.py +0 -0
  312. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim034.py +0 -0
  313. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim035.py +0 -0
  314. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim036.py +0 -0
  315. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim037.py +0 -0
  316. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim038.py +0 -0
  317. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim039.py +0 -0
  318. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim040.py +0 -0
  319. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim041.py +0 -0
  320. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim042.py +0 -0
  321. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim043.py +0 -0
  322. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim044.py +0 -0
  323. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim045.py +0 -0
  324. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim046.py +0 -0
  325. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim047.py +0 -0
  326. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim048.py +0 -0
  327. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim049.py +0 -0
  328. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim051.py +0 -0
  329. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim052.py +0 -0
  330. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim053.py +0 -0
  331. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim054.py +0 -0
  332. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim055.py +0 -0
  333. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim056.py +0 -0
  334. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim060_codex_profile.py +0 -0
  335. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim065.py +0 -0
  336. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_tools.py +0 -0
  337. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_topology_parsers_066.py +0 -0
  338. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_update_task.py +0 -0
  339. {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_visualize_modes.py +0 -0
@@ -24,8 +24,8 @@ jobs:
24
24
  - name: Install dependencies
25
25
  run: |
26
26
  python -m pip install --upgrade pip
27
- pip install --no-deps -e ".[multilang]"
28
- pip install pyyaml pytest ruff==0.15.1
27
+ pip install --no-deps -e .
28
+ pip install pyyaml pytest ruff==0.15.1 tree-sitter tree-sitter-go tree-sitter-java tree-sitter-typescript
29
29
 
30
30
  - name: Lint with ruff
31
31
  run: ruff check src/ tests/
@@ -20,7 +20,7 @@ jobs:
20
20
  - name: Install dependencies
21
21
  run: |
22
22
  python -m pip install --upgrade pip
23
- pip install -e ".[multilang]"
23
+ pip install -e .
24
24
  pip install pytest ruff
25
25
  pactkit init
26
26
 
@@ -45,6 +45,12 @@ docs/architecture/governance/
45
45
  # Claude Code local config
46
46
  .claude/
47
47
 
48
+ # Codex CLI local config
49
+ .codex/
50
+
51
+ # Plugin deployment artifacts (generated by pactkit update)
52
+ pactkit-plugin/
53
+
48
54
  # Secrets
49
55
  .pypirc
50
56
 
@@ -1,8 +1,8 @@
1
1
  # PactKit Configuration
2
- # All agents, commands, skills, and rules are deployed by default.
3
- # To exclude specific items, add an exclude list (e.g., exclude_skills: [pactkit-draw]).
2
+ # Edit this file to customize which components are deployed.
3
+ # Remove items from a list to disable them. Default: all enabled.
4
4
 
5
- version: "2.9.1"
5
+ version: "2.9.5"
6
6
  stack: python
7
7
  root: .
8
8
  developer: "slim"
@@ -10,7 +10,10 @@ developer: "slim"
10
10
  # CI/CD — set provider to github or gitlab to generate pipeline config
11
11
  ci:
12
12
  provider: github
13
- install_cmd: "python -m pip install --upgrade pip\n pip install -e \".[multilang]\"\n pip install pytest ruff\n pactkit init"
13
+ # runner: ubuntu-latest
14
+ # language_version: "3.11"
15
+ # github_host: "" # GHE server (empty = github.com)
16
+ # actions_ref: "" # GHE actions prefix
14
17
 
15
18
  # Issue Tracker — set provider to github to link stories to issues
16
19
  issue_tracker:
@@ -4,6 +4,27 @@ All notable changes to PactKit will be documented in this file.
4
4
 
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [2.9.4] - 2026-03-31
8
+
9
+ ### Fixed
10
+ - **Init playbook DIP violation** (STORY-slim-074) — Eliminated `DETECTED_ENV` runtime IDE detection; all hardcoded paths replaced with template variables (`{FORMAT_NAME}`, `{PROJECT_CONFIG_DIR}`, etc.). Adding a new IDE format now requires zero playbook changes.
11
+ - **Full DIP audit** — Fixed hardcoded IDE paths in doctor skill, core-protocol rule, and done command.
12
+ - **tree-sitter promoted to core dependency** — No longer optional; CI install updated to include `tree-sitter-go`, `tree-sitter-java`, `tree-sitter-typescript`.
13
+ - **`--focus` scan optimization** — `_scan_files` now scans only the focused subdirectory, not the full project root. `pactkit visualize --focus` without `--mode` now correctly passes focus through.
14
+ - **SCAN_EXCLUDES expanded** — From 13 to 30+ entries covering Go (`vendor`), Java (`target`, `.gradle`, `.mvn`), Node (`.next`, `.nuxt`, `.turbo`), IDE (`.idea`, `.vscode`), VCS (`.svn`, `.hg`), and more.
15
+ - **Codex pactkit.yaml candidates** — 3 functions in `visualize.py` now include `.codex/pactkit.yaml` in search paths.
16
+ - **Topology markers** — `_TOPOLOGY_MARKERS` and `PdcaParser.markers` now include all 3 IDE format directories.
17
+
18
+ ## [2.9.3] - 2026-03-31
19
+
20
+ ### Added
21
+ - **Multi-language call chain fix** (STORY-slim-069) — Dispatch hint comment parsing (`pactkit-trace: dispatches_to`) and inheritance edge linking extended from Python-only to Go (struct embedding), Java (extends/implements), and TypeScript (class extends) tree-sitter analyzers.
22
+ - **CLI visualize args exposed** (HOTFIX-slim-070) — `--entry`, `--focus`, `--reverse`, `--depth`, `--max-nodes` now reachable from `pactkit visualize` CLI.
23
+
24
+ ### Fixed
25
+ - **4 call chain断链** (STORY-slim-068) — dict.update scan collision, dynamic dispatch hints, abstract method orphan nodes, cross-package stub edges.
26
+ - **CI install command** — Fallback from `.[multilang]` to `.[dev]` in generated pactkit.yml.
27
+
7
28
  ## [2.9.2] - 2026-03-30
8
29
 
9
30
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pactkit
3
- Version: 2.9.2
3
+ Version: 2.9.5
4
4
  Summary: Spec-driven agentic DevOps toolkit for AI coding assistants
5
5
  Project-URL: Homepage, https://pactkit.dev
6
6
  Project-URL: Repository, https://github.com/pactkit/pactkit-public
@@ -24,11 +24,10 @@ Requires-Python: >=3.10
24
24
  Requires-Dist: pactkit-codex>=2.9.0
25
25
  Requires-Dist: pactkit-opencode>=2.9.0
26
26
  Requires-Dist: pyyaml>=6.0
27
- Provides-Extra: multilang
28
- Requires-Dist: tree-sitter-go>=0.25; extra == 'multilang'
29
- Requires-Dist: tree-sitter-java>=0.23; extra == 'multilang'
30
- Requires-Dist: tree-sitter-typescript>=0.23; extra == 'multilang'
31
- Requires-Dist: tree-sitter>=0.25; extra == 'multilang'
27
+ Requires-Dist: tree-sitter-go>=0.25
28
+ Requires-Dist: tree-sitter-java>=0.23
29
+ Requires-Dist: tree-sitter-typescript>=0.23
30
+ Requires-Dist: tree-sitter>=0.25
32
31
  Description-Content-Type: text/markdown
33
32
 
34
33
  <p align="center">
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pactkit"
7
- version = "2.9.2"
7
+ version = "2.9.5"
8
8
  description = "Spec-driven agentic DevOps toolkit for AI coding assistants"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -13,6 +13,10 @@ dependencies = [
13
13
  "pyyaml>=6.0",
14
14
  "pactkit-opencode>=2.9.0",
15
15
  "pactkit-codex>=2.9.0",
16
+ "tree-sitter>=0.25",
17
+ "tree-sitter-go>=0.25",
18
+ "tree-sitter-java>=0.23",
19
+ "tree-sitter-typescript>=0.23",
16
20
  ]
17
21
 
18
22
  authors = [
@@ -47,14 +51,6 @@ classifiers = [
47
51
  "Topic :: Software Development :: Quality Assurance",
48
52
  ]
49
53
 
50
- [project.optional-dependencies]
51
- multilang = [
52
- "tree-sitter>=0.25",
53
- "tree-sitter-go>=0.25",
54
- "tree-sitter-java>=0.23",
55
- "tree-sitter-typescript>=0.23",
56
- ]
57
-
58
54
  [project.urls]
59
55
  Homepage = "https://pactkit.dev"
60
56
  Repository = "https://github.com/pactkit/pactkit-public"
@@ -1,3 +1,3 @@
1
1
  """PactKit - Spec-driven agentic DevOps toolkit."""
2
2
 
3
- __version__ = "2.9.2"
3
+ __version__ = "2.9.5"
@@ -223,8 +223,13 @@ def main():
223
223
  regression_parser = subparsers.add_parser("regression", help="Classify changes for regression testing")
224
224
  regression_parser.add_argument("files", nargs="*", help="Changed file paths (default: git diff)")
225
225
 
226
- # pactkit context (STORY-slim-014 R1)
227
- subparsers.add_parser("context", help="Generate docs/product/context.md")
226
+ # pactkit context (STORY-slim-014 R1, STORY-slim-071 continuation)
227
+ ctx_parser = subparsers.add_parser("context", help="Generate docs/product/context.md")
228
+ ctx_parser.add_argument("--continuation", action="store_true", default=False,
229
+ help="Update Agent Continuation section")
230
+ ctx_parser.add_argument("--last-command", default=None, help="Last PDCA command run")
231
+ ctx_parser.add_argument("--phase", default=None, help="Phase reached")
232
+ ctx_parser.add_argument("--blockers", default=None, help="Blockers or open questions")
228
233
 
229
234
  # pactkit sec-scope (STORY-slim-014 R6)
230
235
  sec_scope_parser = subparsers.add_parser("sec-scope", help="Auto-detect security scope")
@@ -252,6 +257,22 @@ def main():
252
257
  "--mode", choices=["file", "class", "call"], default=None,
253
258
  help="Graph mode (default: all three)",
254
259
  )
260
+ # HOTFIX-slim-070: expose visualize.py args to CLI
261
+ viz_parser.add_argument("--entry", default=None, help="Entry function for call graph BFS")
262
+ viz_parser.add_argument("--focus", default=None, help="Focus on specific module")
263
+ viz_parser.add_argument("--reverse", action="store_true", default=False, help="Reverse BFS: find callers of entry")
264
+ viz_parser.add_argument("--depth", type=int, default=0, help="Limit traversal depth (0=unlimited)")
265
+ viz_parser.add_argument("--max-nodes", type=int, default=0, help="Truncate graph to N nodes (0=unlimited)")
266
+
267
+ # pactkit garden (STORY-slim-070)
268
+ garden_parser = subparsers.add_parser("garden", help="Codebase quality patrol")
269
+ garden_parser.add_argument("--json", action="store_true", default=False, help="JSON output")
270
+ garden_parser.add_argument("--scope", default=None, help="Scan only this directory (relative path)")
271
+
272
+ # pactkit observe (STORY-slim-073)
273
+ observe_parser = subparsers.add_parser("observe", help="Collect runtime observability signals")
274
+ observe_parser.add_argument("--report", action="store_true", default=False, help="Human-readable report")
275
+ observe_parser.add_argument("--json", action="store_true", default=False, help="JSON output")
255
276
 
256
277
  # pactkit doctor (STORY-slim-015 R1-R3)
257
278
  subparsers.add_parser("doctor", help="Diagnose project health")
@@ -415,7 +436,19 @@ def main():
415
436
 
416
437
  from pactkit.context_gen import generate_context
417
438
 
418
- content = generate_context(Path.cwd(), command="pactkit context")
439
+ continuation_args = None
440
+ if args.continuation and args.last_command:
441
+ continuation_args = {"last_command": args.last_command}
442
+ if args.phase:
443
+ continuation_args["phase"] = args.phase
444
+ if args.blockers:
445
+ continuation_args["blockers"] = args.blockers
446
+
447
+ content = generate_context(
448
+ Path.cwd(),
449
+ command="pactkit context",
450
+ continuation_args=continuation_args,
451
+ )
419
452
  ctx_path = Path.cwd() / "docs" / "product" / "context.md"
420
453
  ctx_path.parent.mkdir(parents=True, exist_ok=True)
421
454
  ctx_path.write_text(content, encoding="utf-8")
@@ -485,9 +518,31 @@ def main():
485
518
  print(f"Visualize needed: {reason}")
486
519
  # HOTFIX-slim-023: support --mode for single or all modes
487
520
  if args.mode:
488
- run_visualize_single(project_root, args.mode)
521
+ run_visualize_single(
522
+ project_root, args.mode,
523
+ entry=args.entry, focus=args.focus, reverse=args.reverse,
524
+ depth=args.depth, max_nodes=args.max_nodes,
525
+ )
489
526
  else:
490
- run_visualize_graphs(project_root)
527
+ run_visualize_graphs(project_root, focus=args.focus)
528
+
529
+ elif args.command == "garden":
530
+ from pathlib import Path
531
+
532
+ from pactkit.garden import run_garden
533
+
534
+ root = Path.cwd()
535
+ scope = Path(args.scope) if args.scope else None
536
+ output, exit_code = run_garden(root, scope=scope, json_output=args.json)
537
+ print(output)
538
+ raise SystemExit(exit_code)
539
+
540
+ elif args.command == "observe":
541
+ from pactkit.observe import run_observe
542
+
543
+ output, exit_code = run_observe(report=args.report, json_output=args.json)
544
+ print(output)
545
+ raise SystemExit(exit_code)
491
546
 
492
547
  elif args.command == "doctor":
493
548
  from pathlib import Path
@@ -55,6 +55,7 @@ VALID_SKILLS = frozenset(
55
55
  "pactkit-draw",
56
56
  "pactkit-status",
57
57
  "pactkit-doctor",
58
+ "pactkit-garden",
58
59
  "pactkit-review",
59
60
  "pactkit-release",
60
61
  "pactkit-analyze",
@@ -180,6 +181,18 @@ def get_default_config() -> dict:
180
181
  "check": {
181
182
  "security_checklist": True,
182
183
  "security_scope_override": "none",
184
+ "pactguard": {
185
+ "enabled": False,
186
+ "mode": "all",
187
+ "ruleset": "",
188
+ "blocking": False,
189
+ },
190
+ "observe": {
191
+ "enabled": False,
192
+ "sources": "auto",
193
+ "max_console": 100,
194
+ "max_network": 200,
195
+ },
183
196
  },
184
197
  "e2e": {
185
198
  "type": "none",
@@ -396,8 +409,15 @@ def load_config(path: Path | str | None = None) -> dict:
396
409
  if key in merged:
397
410
  # BUG-022: Deep merge for nested dict sections
398
411
  if key in DEEP_MERGE_KEYS and isinstance(merged[key], dict) and isinstance(value, dict):
399
- # Preserve default sub-keys, override with user values
400
- merged[key] = {**merged[key], **value}
412
+ # Two-level deep merge: for sub-keys that are also dicts, merge them too
413
+ # (e.g., check.pactguard, check.observe)
414
+ result = {**merged[key]}
415
+ for sub_key, sub_value in value.items():
416
+ if sub_key in result and isinstance(result[sub_key], dict) and isinstance(sub_value, dict):
417
+ result[sub_key] = {**result[sub_key], **sub_value}
418
+ else:
419
+ result[sub_key] = sub_value
420
+ merged[key] = result
401
421
  else:
402
422
  # Shallow override for non-dict keys (strings, lists, booleans)
403
423
  merged[key] = value
@@ -642,7 +662,7 @@ def _rewrite_yaml(path: Path, data: dict) -> None:
642
662
  lines.append(f" max_impact_tests: {regression.get('max_impact_tests', 50)}")
643
663
  lines.append("")
644
664
 
645
- # Write check section (STORY-055, STORY-056)
665
+ # Write check section (STORY-055, STORY-056, STORY-slim-072, STORY-slim-073)
646
666
  check = data.get("check", {})
647
667
  if isinstance(check, dict):
648
668
  lines.append("# Check — configure QA verification behavior")
@@ -651,6 +671,22 @@ def _rewrite_yaml(path: Path, data: dict) -> None:
651
671
  lines.append(f" security_checklist: {'true' if sc else 'false'}")
652
672
  sso = check.get("security_scope_override", "none")
653
673
  lines.append(f" security_scope_override: {sso}")
674
+ # PactGuard sub-section (STORY-slim-072)
675
+ pg = check.get("pactguard", {})
676
+ if isinstance(pg, dict):
677
+ lines.append(" pactguard:")
678
+ lines.append(f" enabled: {'true' if pg.get('enabled') else 'false'}")
679
+ lines.append(f" mode: {pg.get('mode', 'all')}")
680
+ lines.append(f" ruleset: \"{pg.get('ruleset', '')}\"")
681
+ lines.append(f" blocking: {'true' if pg.get('blocking') else 'false'}")
682
+ # Observe sub-section (STORY-slim-073)
683
+ obs = check.get("observe", {})
684
+ if isinstance(obs, dict):
685
+ lines.append(" observe:")
686
+ lines.append(f" enabled: {'true' if obs.get('enabled') else 'false'}")
687
+ lines.append(f" sources: {obs.get('sources', 'auto')}")
688
+ lines.append(f" max_console: {obs.get('max_console', 100)}")
689
+ lines.append(f" max_network: {obs.get('max_network', 200)}")
654
690
  lines.append("")
655
691
 
656
692
  # Write done section (STORY-055)
@@ -832,7 +868,7 @@ def validate_config(config: dict) -> None:
832
868
  if not isinstance(max_impact, int) or max_impact <= 0:
833
869
  warnings.warn(f"regression.max_impact_tests should be a positive integer, got {max_impact!r}")
834
870
 
835
- # Validate check section (STORY-055, STORY-056)
871
+ # Validate check section (STORY-055, STORY-056, STORY-slim-072, STORY-slim-073)
836
872
  check = config.get("check", {})
837
873
  if isinstance(check, dict):
838
874
  sc = check.get("security_checklist", True)
@@ -842,6 +878,25 @@ def validate_config(config: dict) -> None:
842
878
  if sso not in ("none", "full"):
843
879
  warnings.warn(f"check.security_scope_override should be 'none' or 'full', got {sso!r}")
844
880
 
881
+ # Validate check.pactguard (STORY-slim-072)
882
+ pactguard = check.get("pactguard", {})
883
+ if isinstance(pactguard, dict):
884
+ pg_mode = pactguard.get("mode", "all")
885
+ if pg_mode not in ("pattern", "all"):
886
+ warnings.warn(
887
+ f"check.pactguard.mode should be 'pattern' or 'all', got '{pg_mode}'"
888
+ )
889
+
890
+ # Validate check.observe (STORY-slim-073)
891
+ observe = check.get("observe", {})
892
+ if isinstance(observe, dict):
893
+ obs_sources = observe.get("sources", "auto")
894
+ if obs_sources not in ("auto", "chrome-devtools", "playwright", "all"):
895
+ warnings.warn(
896
+ f"check.observe.sources should be 'auto', 'chrome-devtools', "
897
+ f"'playwright', or 'all', got '{obs_sources}'"
898
+ )
899
+
845
900
  # Validate done section (STORY-055)
846
901
  done_cfg = config.get("done", {})
847
902
  if isinstance(done_cfg, dict):
@@ -955,7 +1010,7 @@ def generate_default_yaml() -> str:
955
1010
  lines.append(f" strategy: {regression.get('strategy', 'impact')}")
956
1011
  lines.append(f" max_impact_tests: {regression.get('max_impact_tests', 50)}")
957
1012
 
958
- # Write check section (STORY-055, STORY-056)
1013
+ # Write check section (STORY-055, STORY-056, STORY-slim-072, STORY-slim-073)
959
1014
  check = cfg.get("check", {})
960
1015
  lines.extend(["", "# Check — configure QA verification behavior"])
961
1016
  lines.append("check:")
@@ -963,6 +1018,20 @@ def generate_default_yaml() -> str:
963
1018
  lines.append(f" security_checklist: {'true' if sc else 'false'}")
964
1019
  sso = check.get("security_scope_override", "none")
965
1020
  lines.append(f" security_scope_override: {sso}")
1021
+ # PactGuard sub-section (STORY-slim-072)
1022
+ pg = check.get("pactguard", {})
1023
+ lines.append(" pactguard:")
1024
+ lines.append(f" enabled: {'true' if pg.get('enabled') else 'false'}")
1025
+ lines.append(f" mode: {pg.get('mode', 'all')}")
1026
+ lines.append(f" ruleset: \"{pg.get('ruleset', '')}\"")
1027
+ lines.append(f" blocking: {'true' if pg.get('blocking') else 'false'}")
1028
+ # Observe sub-section (STORY-slim-073)
1029
+ obs = check.get("observe", {})
1030
+ lines.append(" observe:")
1031
+ lines.append(f" enabled: {'true' if obs.get('enabled') else 'false'}")
1032
+ lines.append(f" sources: {obs.get('sources', 'auto')}")
1033
+ lines.append(f" max_console: {obs.get('max_console', 100)}")
1034
+ lines.append(f" max_network: {obs.get('max_network', 200)}")
966
1035
 
967
1036
  # Write done section (STORY-055)
968
1037
  done_cfg = cfg.get("done", {})
@@ -18,6 +18,7 @@ from pactkit.schemas import (
18
18
  BOARD_SECTION_DONE,
19
19
  BOARD_SECTION_IN_PROGRESS,
20
20
  CONTEXT_HEADER,
21
+ CONTEXT_SECTION_CONTINUATION,
21
22
  CONTEXT_SECTIONS,
22
23
  LESSONS_TABLE_HEADER,
23
24
  )
@@ -145,11 +146,104 @@ def _parse_last_lessons(lessons_path: Path, n: int = 5) -> list[str]:
145
146
  return rows[-n:]
146
147
 
147
148
 
149
+ # ---------------------------------------------------------------------------
150
+ # Agent Continuation helpers (STORY-slim-071)
151
+ # ---------------------------------------------------------------------------
152
+
153
+ # Pattern to extract AC titles: ### AC1: Title (R1)
154
+ _AC_TITLE_RE = re.compile(r"^###\s+(AC\d+:\s*.+?)(?:\s*\(R\d+\))?\s*$", re.MULTILINE)
155
+
156
+ # Pattern to extract story ID from command string
157
+ _STORY_ID_FROM_CMD_RE = re.compile(r"((?:STORY|BUG|HOTFIX)-[\w]+-\d+)")
158
+
159
+
160
+ def _sanitize_text(text: str) -> str:
161
+ """Sanitize free-text input: keep only the first line, strip markdown headers."""
162
+ # Take only the first line to prevent injection of markdown structure
163
+ first_line = text.split("\n")[0]
164
+ # Remove any markdown header markers
165
+ sanitized = re.sub(r"#{1,6}\s+", "", first_line)
166
+ return sanitized.strip()
167
+
168
+
169
+ def _extract_sprint_contract(spec_path: Path) -> str:
170
+ """Extract AC titles from a spec file as a markdown checklist.
171
+
172
+ Returns a string like:
173
+ ### Sprint Contract (STORY-slim-070)
174
+ - [ ] AC1: First Check
175
+ - [ ] AC2: Second Check
176
+ """
177
+ if not spec_path.exists():
178
+ return ""
179
+
180
+ text = spec_path.read_text(encoding="utf-8")
181
+ ac_matches = _AC_TITLE_RE.findall(text)
182
+ if not ac_matches:
183
+ return ""
184
+
185
+ # Extract story ID from filename
186
+ stem = spec_path.stem
187
+ lines = [f"### Sprint Contract ({stem})"]
188
+ for ac_title in ac_matches:
189
+ lines.append(f"- [ ] {ac_title}")
190
+
191
+ return "\n".join(lines)
192
+
193
+
194
+ def _generate_continuation(
195
+ project_root: Path,
196
+ board_text: str,
197
+ continuation_args: dict[str, str] | None,
198
+ ) -> str:
199
+ """Generate the Agent Continuation section content.
200
+
201
+ Args:
202
+ project_root: Project root directory.
203
+ board_text: The sprint board content (for detecting in-progress stories).
204
+ continuation_args: Dict with keys 'last_command', 'phase', 'blockers'.
205
+ If None, returns the default "no active session" message.
206
+
207
+ Returns:
208
+ Content for the ## Agent Continuation section (without the header).
209
+ """
210
+ if continuation_args is None:
211
+ return "No active work session."
212
+
213
+ last_cmd = _sanitize_text(continuation_args.get("last_command", "unknown"))
214
+ phase = _sanitize_text(continuation_args.get("phase", "unknown"))
215
+ blockers = continuation_args.get("blockers")
216
+
217
+ lines = [
218
+ f"Last Command: {last_cmd}",
219
+ f"Phase Reached: {phase}",
220
+ ]
221
+
222
+ if blockers:
223
+ lines.append(f"Blockers: {_sanitize_text(blockers)}")
224
+
225
+ # Extract story ID from the command to find spec
226
+ story_match = _STORY_ID_FROM_CMD_RE.search(last_cmd)
227
+ if story_match:
228
+ story_id = story_match.group(1)
229
+ spec_path = project_root / "docs" / "specs" / f"{story_id}.md"
230
+ contract = _extract_sprint_contract(spec_path)
231
+ if contract:
232
+ lines.append("")
233
+ lines.append(contract)
234
+
235
+ return "\n".join(lines)
236
+
237
+
148
238
  # ---------------------------------------------------------------------------
149
239
  # Main public API
150
240
  # ---------------------------------------------------------------------------
151
241
 
152
- def generate_context(project_root: Path, command: str = "pactkit context") -> str:
242
+ def generate_context(
243
+ project_root: Path,
244
+ command: str = "pactkit context",
245
+ continuation_args: dict[str, str] | None = None,
246
+ ) -> str:
153
247
  """Generate the contents of docs/product/context.md.
154
248
 
155
249
  Reads the sprint board, git branch list, and lessons file, then composes
@@ -232,4 +326,12 @@ def generate_context(project_root: Path, command: str = "pactkit context") -> st
232
326
  lines.append("`/project-design`")
233
327
  lines.append("")
234
328
 
329
+ # ---- Agent Continuation (STORY-slim-071) ----
330
+ lines.append(CONTEXT_SECTION_CONTINUATION)
331
+ continuation_content = _generate_continuation(
332
+ project_root, board_text, continuation_args,
333
+ )
334
+ lines.append(continuation_content)
335
+ lines.append("")
336
+
235
337
  return "\n".join(lines)