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.
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/workflows/ci.yml +2 -2
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/workflows/pactkit.yml +1 -1
- {pactkit-2.9.2 → pactkit-2.9.5}/.gitignore +6 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/.opencode/pactkit.yaml +7 -4
- {pactkit-2.9.2 → pactkit-2.9.5}/CHANGELOG.md +21 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/PKG-INFO +5 -6
- {pactkit-2.9.2 → pactkit-2.9.5}/pyproject.toml +5 -9
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/__init__.py +1 -1
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/cli.py +60 -5
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/config.py +74 -5
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/context_gen.py +103 -1
- pactkit-2.9.5/src/pactkit/garden.py +387 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/deployer.py +2 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/lazy_visualize.py +25 -11
- pactkit-2.9.5/src/pactkit/lessons.py +160 -0
- pactkit-2.9.5/src/pactkit/observe.py +193 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/profiles.py +22 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/__init__.py +1 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/agents.py +8 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/commands.py +48 -51
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/rules.py +12 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/skills.py +40 -1
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/workflows.py +5 -5
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/schemas.py +4 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/visualize.py +392 -30
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/validators.py +13 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/e2e/cli/test_cli_e2e.py +105 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug002_plugin_paths.py +7 -9
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug017_project_init_playbook.py +4 -4
- pactkit-2.9.5/tests/unit/test_bug_slim001_env_detection.py +50 -0
- pactkit-2.9.5/tests/unit/test_check_extensions_072_073.py +292 -0
- pactkit-2.9.5/tests/unit/test_check_playbook_072_073.py +51 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_config.py +2 -1
- pactkit-2.9.5/tests/unit/test_context_gen.py +285 -0
- pactkit-2.9.5/tests/unit/test_garden.py +336 -0
- pactkit-2.9.5/tests/unit/test_init_playbook_074.py +204 -0
- pactkit-2.9.5/tests/unit/test_lessons_rotation_075.py +200 -0
- pactkit-2.9.5/tests/unit/test_observe.py +186 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_pdca_slim.py +6 -4
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_prompt_cli_refs.py +1 -1
- pactkit-2.9.5/tests/unit/test_prompt_quality_075.py +157 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_prompt_structural_invariants.py +2 -2
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_selective_deploy.py +6 -5
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story017_init_claude_md.py +7 -4
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story063_prompt_slimming.py +2 -1
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_validators.py +3 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim063.py +2 -2
- pactkit-2.9.5/tests/unit/test_visualize_call_nested.py +217 -0
- pactkit-2.9.5/tests/unit/test_visualize_chain_fix.py +229 -0
- pactkit-2.9.5/tests/unit/test_visualize_multilang_chain.py +230 -0
- pactkit-2.9.2/src/pactkit/lessons.py +0 -80
- pactkit-2.9.2/tests/unit/test_bug_slim001_env_detection.py +0 -70
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/dependabot.yml +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/.github/workflows/publish.yml +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/AGENTS.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/CODE_OF_CONDUCT.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/CONTRIBUTING.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/LICENSE +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/README.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/SECURITY.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/assets/logo.png +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/guides/codex-integration-preresearch.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/guides/tool-integration-checklist.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-001.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-002.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-003.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-004.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-005.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-006.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-007.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-008.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-009.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-010.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-011.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-012.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-013.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-014.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-015.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-016.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-017.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-018.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-019.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-020.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-021.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-022.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-023.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-024.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-025.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-026.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-027.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-028.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-029.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-030.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-031.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-032.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-033.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-034.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-035.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-001.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-002.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-003.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-004.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-005.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/specs/BUG-slim-006.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/test_cases/BUG-001_case.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/docs/test_cases/BUG-002_case.md +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/opencode.json +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/__main__.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/backfill.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/cleaners.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/coverage_gate.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/doctor.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/__init__.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/adapter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/generators/deploy_base.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/guards.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/id_generator.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/invariants.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/issue_sync.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/lint_runner.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/prompts/references.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/regression.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/scripts.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/sec_scope.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/__init__.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/board.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/scaffold.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/skills/spec_linter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/spec_status.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/test_mapper.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/src/pactkit/utils.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/conftest.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/e2e/__init__.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/e2e/cli/__init__.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/agents_dir/researcher.yaml +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/agents_dir/writer.yaml +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/langgraph_app.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/agent_parser/mcp_settings.json +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/api_call_parser/axios_style.tsx +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/api_call_parser/dynamic.tsx +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/fixtures/api_call_parser/page.tsx +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/integration/__init__.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/integration/test_deploy_classic.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_agent_features.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_agent_frontmatter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_agents_enrichment.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_board_bug027.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_board_sections.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug001_skill_path.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug003_multi_import.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug004_dead_set.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug005_archive_taskless.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug006_scan_excludes.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug007_stale_trace_refs.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug008_stale_command_refs.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug009_project_config_backfill.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug010_rewrite_yaml.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug011_stale_refs.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug012_call_graph_filter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug013_config_single_source.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug014_version_hygiene.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug018_issue_tracker_verification.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug019_venv_deployment.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug020_claude_md_backup.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug025_release_delegation.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug026_version_sync.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug028_ghost_refs.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug029_stack_detection_fallback.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug030_spec_lint_cli.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug031_docstring_accuracy.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug034_plan_metadata_template.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_021.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_022.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_023.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_024.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim002_instruction_collision.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim003.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim004.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim005.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_bug_slim006.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_check_command.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_command_frontmatter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_command_visualize_modes.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_config_auto_merge.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_constitution_sharpening.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_create_skill.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_cross_flow_matrix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_deploy_base.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_deployer_cleanup.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_deployer_plugin.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_design_command.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_done_gates.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_draw_prompt.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_draw_references.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_drawio_mcp.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_home_path_fix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_hotfix_command.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_init_guard.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_lang_profiles.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_list_stories.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_mcp_integration.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_model_config.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_modular_constitution.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_monorepo_detect.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_multi_prefix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_profiles.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_project_visibility.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_prompts_package.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_release.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_release_field.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_release_v110.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_render_prompt.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_review_command.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_rules_enrichment.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_scaffold.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_scaffold_developer_prefix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_schemas.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_script_extraction.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_session_context.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_skill_structure.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_skills_enrichment.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_smart_regression.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_sprint_command.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_stack_references.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_status_command.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_statusline.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story014_release.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story015_ci_lint_gate.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story016_claude_md.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story018_arch_staleness.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story019_bailout.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story020_horizon.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story021_rfc.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story022_decision_tree.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story023_test_quality.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story024_native_agent.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story025_ci_pipeline.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story026_issue_tracker.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story027_hooks.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story028_rule_scoping.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story029_doctor.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story030_lint.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story031_git_init_guard.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story032_greenfield_redirect.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story033_config_backfill.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story034_plan_config_refresh.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story035_readme_docs.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story037_regression_fix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story038_call_graph_update.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story039_venv_config.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story040_layered_claude_md.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story042_spec_linter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story043_active_clarify.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story044_consistency_check.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story045_auto_pr.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story046_agent_adapter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story047_enterprise_flags.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story048_worktree_isolation.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story049_community_standards.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story050_doc_only_shortcut.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story051_workflow_streamlining.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story052_conditional_github_release.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story053_impact_regression.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story055_commands.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story055_config.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story055_spec_linter.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story056_commands.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story056_config.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story057_implicit_cleanup.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story058_opencode_extraction.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story058_routing_fix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story059_codex_removal.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story060_init_hang.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story061_remove_thinking.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story062_mcp_recommendations.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story064_venv_local_md.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story065_sprint_model.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story072_developer_prefix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim009_lazy_rules.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim010_dry_refactor.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim011_command_rules.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim012_ci.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_clean.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_context.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_guard.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_lazy_viz.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_next_id.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_regression.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim014_sec_scope.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim015_doctor.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim016_testmap_lint.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim017.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim018.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim019_plan_subphases.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim020_explore_stall_fix.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim021.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim022.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim023.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim024.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim025.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim026.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim027.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim028.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim029.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim030.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim031.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim032.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim033.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim034.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim035.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim036.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim037.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim038.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim039.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim040.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim041.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim042.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim043.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim044.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim045.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim046.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim047.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim048.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim049.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim051.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim052.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim053.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim054.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim055.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim056.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim060_codex_profile.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_story_slim065.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_tools.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_topology_parsers_066.py +0 -0
- {pactkit-2.9.2 → pactkit-2.9.5}/tests/unit/test_update_task.py +0 -0
- {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
|
|
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/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# PactKit Configuration
|
|
2
|
-
#
|
|
3
|
-
#
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
28
|
-
Requires-Dist: tree-sitter-
|
|
29
|
-
Requires-Dist: tree-sitter-
|
|
30
|
-
Requires-Dist: tree-sitter
|
|
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.
|
|
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"
|
|
@@ -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
|
-
|
|
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(
|
|
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
|
-
#
|
|
400
|
-
|
|
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(
|
|
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)
|