pactkit 2.6.0__tar.gz → 2.7.0__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.6.0 → pactkit-2.7.0}/.opencode/pactkit.yaml +12 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/CHANGELOG.md +21 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/PKG-INFO +2 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/pyproject.toml +2 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/__init__.py +1 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/config.py +13 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/generators/deployer.py +40 -10
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/lazy_visualize.py +2 -8
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/profiles.py +22 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/skills/board.py +4 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/skills/visualize.py +1 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/e2e/cli/test_cli_e2e.py +11 -8
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_command_frontmatter.py +2 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_config.py +2 -1
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_deployer_cleanup.py +5 -2
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_pdca_slim.py +4 -2
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_profiles.py +7 -3
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_render_prompt.py +6 -5
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_selective_deploy.py +20 -13
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_sprint_command.py +10 -7
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story031_git_init_guard.py +3 -2
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story032_greenfield_redirect.py +3 -2
- pactkit-2.7.0/tests/unit/test_story059_codex_removal.py +68 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim011_command_rules.py +12 -10
- pactkit-2.7.0/tests/unit/test_story_slim060_codex_profile.py +77 -0
- pactkit-2.7.0/tests/unit/test_story_slim063.py +162 -0
- pactkit-2.6.0/tests/unit/test_story059_codex_removal.py +0 -90
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/dependabot.yml +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/workflows/ci.yml +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/workflows/pactkit.yml +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.github/workflows/publish.yml +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/.gitignore +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/AGENTS.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/CODE_OF_CONDUCT.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/CONTRIBUTING.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/LICENSE +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/README.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/SECURITY.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/assets/logo.png +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/guides/codex-integration-preresearch.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/guides/tool-integration-checklist.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-001.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-002.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-003.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-004.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-005.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-006.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-007.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-008.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-009.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-010.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-011.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-012.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-013.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-014.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-015.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-016.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-017.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-018.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-019.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-020.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-021.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-022.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-023.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-024.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-025.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-026.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-027.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-028.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-029.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-030.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-031.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-032.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-033.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-034.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-035.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-001.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-002.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-003.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-004.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-005.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-006.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/specs/BUG-slim-007.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/test_cases/BUG-001_case.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/docs/test_cases/BUG-002_case.md +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/opencode.json +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/__main__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/backfill.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/cleaners.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/cli.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/context_gen.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/coverage_gate.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/doctor.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/generators/__init__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/generators/adapter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/generators/deploy_base.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/guards.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/id_generator.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/invariants.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/issue_sync.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/lessons.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/lint_runner.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/__init__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/agents.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/commands.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/references.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/rules.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/skills.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/prompts/workflows.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/regression.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/schemas.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/scripts.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/sec_scope.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/skills/__init__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/skills/scaffold.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/skills/spec_linter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/spec_status.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/test_mapper.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/utils.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/src/pactkit/validators.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/conftest.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/e2e/__init__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/e2e/cli/__init__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/integration/__init__.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/integration/test_deploy_classic.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_agent_features.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_agent_frontmatter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_agents_enrichment.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_board_bug027.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_board_sections.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug001_skill_path.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug002_plugin_paths.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug003_multi_import.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug004_dead_set.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug005_archive_taskless.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug006_scan_excludes.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug007_stale_trace_refs.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug008_stale_command_refs.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug009_project_config_backfill.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug010_rewrite_yaml.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug011_stale_refs.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug012_call_graph_filter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug013_config_single_source.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug014_version_hygiene.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug017_project_init_playbook.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug018_issue_tracker_verification.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug019_venv_deployment.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug020_claude_md_backup.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug025_release_delegation.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug026_version_sync.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug028_ghost_refs.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug029_stack_detection_fallback.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug030_spec_lint_cli.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug031_docstring_accuracy.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug034_plan_metadata_template.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_021.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_022.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_023.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_024.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_slim001_env_detection.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_slim002_instruction_collision.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_slim003.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_slim004.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_slim005.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_bug_slim006.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_check_command.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_command_visualize_modes.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_config_auto_merge.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_constitution_sharpening.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_create_skill.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_cross_flow_matrix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_deploy_base.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_deployer_plugin.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_design_command.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_done_gates.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_draw_prompt.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_draw_references.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_drawio_mcp.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_home_path_fix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_hotfix_command.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_init_guard.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_lang_profiles.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_list_stories.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_mcp_integration.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_model_config.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_modular_constitution.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_multi_prefix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_project_visibility.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_prompt_cli_refs.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_prompt_structural_invariants.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_prompts_package.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_release.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_release_field.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_release_v110.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_review_command.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_rules_enrichment.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_scaffold.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_scaffold_developer_prefix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_schemas.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_script_extraction.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_session_context.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_skill_structure.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_skills_enrichment.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_smart_regression.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_stack_references.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_status_command.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_statusline.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story014_release.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story015_ci_lint_gate.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story016_claude_md.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story017_init_claude_md.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story018_arch_staleness.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story019_bailout.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story020_horizon.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story021_rfc.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story022_decision_tree.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story023_test_quality.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story024_native_agent.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story025_ci_pipeline.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story026_issue_tracker.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story027_hooks.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story028_rule_scoping.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story029_doctor.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story030_lint.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story033_config_backfill.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story034_plan_config_refresh.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story035_readme_docs.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story037_regression_fix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story038_call_graph_update.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story039_venv_config.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story040_layered_claude_md.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story042_spec_linter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story043_active_clarify.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story044_consistency_check.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story045_auto_pr.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story046_agent_adapter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story047_enterprise_flags.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story048_worktree_isolation.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story049_community_standards.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story050_doc_only_shortcut.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story051_workflow_streamlining.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story052_conditional_github_release.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story053_impact_regression.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story055_commands.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story055_config.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story055_spec_linter.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story056_commands.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story056_config.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story057_implicit_cleanup.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story058_opencode_extraction.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story058_routing_fix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story060_init_hang.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story061_remove_thinking.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story062_mcp_recommendations.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story063_prompt_slimming.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story064_venv_local_md.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story065_sprint_model.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story072_developer_prefix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim009_lazy_rules.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim010_dry_refactor.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim012_ci.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_clean.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_context.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_guard.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_lazy_viz.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_next_id.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_regression.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_sec_scope.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim014_validators.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim015_doctor.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim016_testmap_lint.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim017.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim018.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim019_plan_subphases.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim020_explore_stall_fix.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim021.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim022.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim023.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim024.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim025.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim026.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim027.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim028.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim029.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim030.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim031.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim032.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim033.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim034.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim035.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim036.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim037.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim038.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim039.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim040.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim041.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim042.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim043.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim044.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim045.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim046.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim047.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim048.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim049.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim051.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim052.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim053.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim054.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim055.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_story_slim056.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_tools.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_update_task.py +0 -0
- {pactkit-2.6.0 → pactkit-2.7.0}/tests/unit/test_visualize_modes.py +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Edit this file to customize which components are deployed.
|
|
3
3
|
# Remove items from a list to disable them. Default: all enabled.
|
|
4
4
|
|
|
5
|
-
version: "2.6.
|
|
5
|
+
version: "2.6.1"
|
|
6
6
|
stack: python
|
|
7
7
|
root: .
|
|
8
8
|
developer: "slim"
|
|
@@ -45,6 +45,17 @@ skills:
|
|
|
45
45
|
- pactkit-trace
|
|
46
46
|
- pactkit-visualize
|
|
47
47
|
- pactkit-analyze
|
|
48
|
+
- project-act
|
|
49
|
+
- project-check
|
|
50
|
+
- project-clarify
|
|
51
|
+
- project-design
|
|
52
|
+
- project-done
|
|
53
|
+
- project-hotfix
|
|
54
|
+
- project-init
|
|
55
|
+
- project-plan
|
|
56
|
+
- project-pr
|
|
57
|
+
- project-release
|
|
58
|
+
- project-sprint
|
|
48
59
|
|
|
49
60
|
# Rules — constitution modules
|
|
50
61
|
rules:
|
|
@@ -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.7.0] - 2026-03-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Commands → Skills Migration** (STORY-slim-063) — 11 PDCA commands now deploy as `skills/{name}/SKILL.md` subdirectories instead of flat `commands/{name}.md` files for Claude Code format. `VALID_SKILLS` expanded from 10 to 21 entries (10 embedded + 11 commands).
|
|
11
|
+
- **Legacy Command Cleanup** — `_cleanup_legacy_commands()` auto-removes old `project-*.md` from `commands/` on upgrade, preserving non-PactKit files.
|
|
12
|
+
- **Codex FormatProfile** (STORY-slim-060) — Re-added `codex` profile to core for thin adapter pattern support (`pactkit-codex` package).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **board.py update_task** (HOTFIX-slim-061) — `update_task` now recognizes bullet-format Done entries (`- **STORY-xxx**:`), not just heading format.
|
|
16
|
+
- **visualize --lazy focus** (HOTFIX-slim-062) — Removed hardcoded `--focus cli` refresh; added stem matching for focus target resolution.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **Deploy summary** — Output now shows unified `Skills (embedded + commands)` count instead of separate Commands/Skills lines.
|
|
20
|
+
- **Cross-format isolation** — OpenCode and Codex profiles unaffected; commands still deploy as flat `.md` files for non-classic formats.
|
|
21
|
+
|
|
22
|
+
## [2.6.1] - 2026-03-26
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- **OpenCode backward compatibility** — `pactkit-opencode` added as core dependency so `pip install pactkit` automatically includes OpenCode deployment support. Users upgrading from 2.5.0 no longer lose `--format opencode`.
|
|
26
|
+
- **Version sync** — `pactkit-opencode` version aligned to 2.6.0 to match core versioning.
|
|
27
|
+
|
|
7
28
|
## [2.6.0] - 2026-03-26
|
|
8
29
|
|
|
9
30
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pactkit
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
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
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Build Tools
|
|
22
22
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: pactkit-opencode>=2.6.0
|
|
24
25
|
Requires-Dist: pyyaml>=6.0
|
|
25
26
|
Provides-Extra: multilang
|
|
26
27
|
Requires-Dist: tree-sitter-go>=0.25; extra == 'multilang'
|
|
@@ -4,13 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pactkit"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.7.0"
|
|
8
8
|
description = "Spec-driven agentic DevOps toolkit for AI coding assistants"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
dependencies = [
|
|
13
13
|
"pyyaml>=6.0",
|
|
14
|
+
"pactkit-opencode>=2.6.0",
|
|
14
15
|
]
|
|
15
16
|
|
|
16
17
|
authors = [
|
|
@@ -47,6 +47,7 @@ VALID_COMMANDS = frozenset(
|
|
|
47
47
|
|
|
48
48
|
VALID_SKILLS = frozenset(
|
|
49
49
|
{
|
|
50
|
+
# Embedded skills (auto-invoked by commands)
|
|
50
51
|
"pactkit-visualize",
|
|
51
52
|
"pactkit-board",
|
|
52
53
|
"pactkit-scaffold",
|
|
@@ -57,6 +58,18 @@ VALID_SKILLS = frozenset(
|
|
|
57
58
|
"pactkit-review",
|
|
58
59
|
"pactkit-release",
|
|
59
60
|
"pactkit-analyze",
|
|
61
|
+
# PDCA commands (STORY-slim-063: migrated from commands/ to skills/)
|
|
62
|
+
"project-plan",
|
|
63
|
+
"project-act",
|
|
64
|
+
"project-check",
|
|
65
|
+
"project-done",
|
|
66
|
+
"project-init",
|
|
67
|
+
"project-sprint",
|
|
68
|
+
"project-hotfix",
|
|
69
|
+
"project-design",
|
|
70
|
+
"project-clarify",
|
|
71
|
+
"project-release",
|
|
72
|
+
"project-pr",
|
|
60
73
|
}
|
|
61
74
|
)
|
|
62
75
|
|
|
@@ -314,7 +314,9 @@ def _deploy_classic(config=None, target=None):
|
|
|
314
314
|
_deploy_claude_md(claude_root, enabled_rules)
|
|
315
315
|
agent_models = config.get("agent_models", {})
|
|
316
316
|
n_agents = _deploy_agents(agents_dir, enabled_agents, profile=classic_profile, agent_models=agent_models)
|
|
317
|
-
|
|
317
|
+
# STORY-slim-063: Deploy commands as skills (to skills_dir, not commands_dir)
|
|
318
|
+
_cleanup_legacy_commands(commands_dir)
|
|
319
|
+
n_commands = _deploy_commands(skills_dir, enabled_commands, profile=classic_profile, config=config)
|
|
318
320
|
|
|
319
321
|
# Deploy CI pipeline if configured (STORY-025)
|
|
320
322
|
ci_config = config.get("ci", {})
|
|
@@ -334,16 +336,15 @@ def _deploy_classic(config=None, target=None):
|
|
|
334
336
|
if target is None:
|
|
335
337
|
_generate_project_claude_md(config)
|
|
336
338
|
|
|
337
|
-
# Summary
|
|
339
|
+
# Summary — STORY-slim-063: commands are now deployed as skills
|
|
338
340
|
total_agents = len(VALID_AGENTS)
|
|
339
|
-
total_commands = len(VALID_COMMANDS)
|
|
340
341
|
total_skills = len(VALID_SKILLS)
|
|
341
342
|
total_rules = len(VALID_RULES)
|
|
342
343
|
|
|
343
344
|
print(
|
|
344
345
|
f"\n✅ Deployed: {n_agents}/{total_agents} Agents, "
|
|
345
|
-
f"{n_commands}/{
|
|
346
|
-
f"{n_skills}
|
|
346
|
+
f"{n_skills + n_commands}/{total_skills} Skills "
|
|
347
|
+
f"({n_skills} embedded + {n_commands} commands), "
|
|
347
348
|
f"{n_rules}/{total_rules} Rules"
|
|
348
349
|
)
|
|
349
350
|
_print_mcp_recommendations()
|
|
@@ -484,6 +485,19 @@ def _cleanup_legacy(skills_dir):
|
|
|
484
485
|
legacy.unlink()
|
|
485
486
|
|
|
486
487
|
|
|
488
|
+
def _cleanup_legacy_commands(commands_dir):
|
|
489
|
+
"""Remove legacy project-*.md files from commands/ (STORY-slim-063).
|
|
490
|
+
|
|
491
|
+
After migration, commands are deployed as skills. Old flat .md files
|
|
492
|
+
in commands/ would be shadowed but should be cleaned up.
|
|
493
|
+
Non-PactKit files (e.g., ultra-think.md) are preserved.
|
|
494
|
+
"""
|
|
495
|
+
if not commands_dir.exists():
|
|
496
|
+
return
|
|
497
|
+
for f in commands_dir.glob("project-*.md"):
|
|
498
|
+
f.unlink()
|
|
499
|
+
|
|
500
|
+
|
|
487
501
|
def _migrate_from_scafpy(claude_root):
|
|
488
502
|
"""Migrate legacy scafpy-* remnants to pactkit-* naming.
|
|
489
503
|
|
|
@@ -792,11 +806,22 @@ def _deploy_commands(
|
|
|
792
806
|
# e.g. 'project-plan' -> 'project-plan.md'
|
|
793
807
|
enabled_filenames = {f"{cmd}.md" for cmd in enabled_commands}
|
|
794
808
|
|
|
795
|
-
#
|
|
809
|
+
# STORY-slim-063: For classic format, commands deploy as skills (subdirectory/SKILL.md).
|
|
810
|
+
# For other formats (plugin/marketplace), keep flat .md files.
|
|
811
|
+
_deploy_as_skill = profile.name == "classic" and _legacy_prefix is None
|
|
812
|
+
|
|
813
|
+
# Clean managed command files/dirs not in enabled set
|
|
796
814
|
if commands_dir.exists():
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
815
|
+
if _deploy_as_skill:
|
|
816
|
+
# Clean skill subdirectories for commands no longer enabled
|
|
817
|
+
for d in commands_dir.iterdir():
|
|
818
|
+
if d.is_dir() and d.name.startswith("project-") and d.name not in enabled_set:
|
|
819
|
+
import shutil
|
|
820
|
+
shutil.rmtree(d)
|
|
821
|
+
else:
|
|
822
|
+
for f in commands_dir.glob("*.md"):
|
|
823
|
+
if f.name.startswith("project-") and f.name not in enabled_filenames:
|
|
824
|
+
f.unlink()
|
|
800
825
|
|
|
801
826
|
# Deploy enabled commands
|
|
802
827
|
deployed = 0
|
|
@@ -820,7 +845,12 @@ def _deploy_commands(
|
|
|
820
845
|
if _legacy_prefix is None
|
|
821
846
|
else _rewrite_skills_prefix(content, _effective_prefix)
|
|
822
847
|
)
|
|
823
|
-
|
|
848
|
+
|
|
849
|
+
if _deploy_as_skill:
|
|
850
|
+
# STORY-slim-063: Write as skills_dir/{name}/SKILL.md
|
|
851
|
+
atomic_write(commands_dir / cmd_name / "SKILL.md", rendered)
|
|
852
|
+
else:
|
|
853
|
+
atomic_write(commands_dir / filename, rendered)
|
|
824
854
|
deployed += 1
|
|
825
855
|
|
|
826
856
|
return deployed
|
|
@@ -110,11 +110,5 @@ def run_visualize_graphs(project_root: Path) -> None:
|
|
|
110
110
|
subprocess.run([sys.executable, str(viz_script), "visualize", mode_flag, mode],
|
|
111
111
|
cwd=str(project_root))
|
|
112
112
|
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
if (graph_dir / "focus_file_graph.mmd").exists():
|
|
116
|
-
subprocess.run([sys.executable, str(viz_script), "visualize", "--focus", "cli"],
|
|
117
|
-
cwd=str(project_root))
|
|
118
|
-
if (graph_dir / "focus_call_graph.mmd").exists():
|
|
119
|
-
subprocess.run([sys.executable, str(viz_script), "visualize", "--focus", "cli", mode_flag, "call"],
|
|
120
|
-
cwd=str(project_root))
|
|
113
|
+
# Focus graphs are user-generated on demand (--focus <target>), not auto-refreshed.
|
|
114
|
+
# Removed hardcoded --focus cli refresh (HOTFIX-slim-062).
|
|
@@ -146,6 +146,26 @@ FORMAT_PROFILES: dict[str, FormatProfile] = {
|
|
|
146
146
|
supports_mcp=True,
|
|
147
147
|
skills_path_var="~/.config/opencode/skills",
|
|
148
148
|
),
|
|
149
|
+
"codex": FormatProfile(
|
|
150
|
+
name="codex",
|
|
151
|
+
display_name="Codex CLI",
|
|
152
|
+
global_config_dir="~/.codex",
|
|
153
|
+
project_config_dir=".codex",
|
|
154
|
+
skills_dir="~/.codex/skills",
|
|
155
|
+
agents_dir="~/.codex/agents",
|
|
156
|
+
commands_dir="~/.codex/prompts",
|
|
157
|
+
rules_dir="~/.codex/rules",
|
|
158
|
+
project_instructions_file="AGENTS.md",
|
|
159
|
+
global_instructions_file="AGENTS.md",
|
|
160
|
+
pactkit_yaml_path=".codex/pactkit.yaml",
|
|
161
|
+
agent_format="md",
|
|
162
|
+
rules_import_style="inline",
|
|
163
|
+
excluded_agent_fields=frozenset({"permissionMode", "memory", "skills", "hooks"}),
|
|
164
|
+
has_custom_commands=True,
|
|
165
|
+
supports_model_routing=False,
|
|
166
|
+
supports_mcp=True,
|
|
167
|
+
skills_path_var="~/.codex/skills",
|
|
168
|
+
),
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
# Deployment modes that are not environment formats
|
|
@@ -155,9 +175,10 @@ _DEPLOYMENT_MODES: frozenset[str] = frozenset({"plugin", "marketplace"})
|
|
|
155
175
|
VALID_FORMATS: frozenset[str] = frozenset(FORMAT_PROFILES.keys()) | _DEPLOYMENT_MODES
|
|
156
176
|
|
|
157
177
|
# Ordered candidate paths for pactkit.yaml discovery (first existing wins)
|
|
158
|
-
# Order = preference: OpenCode > Classic
|
|
178
|
+
# Order = preference: OpenCode > Codex > Classic
|
|
159
179
|
PACTKIT_YAML_CANDIDATES: list[str] = [
|
|
160
180
|
FORMAT_PROFILES["opencode"].pactkit_yaml_path,
|
|
181
|
+
FORMAT_PROFILES["codex"].pactkit_yaml_path,
|
|
161
182
|
FORMAT_PROFILES["classic"].pactkit_yaml_path,
|
|
162
183
|
]
|
|
163
184
|
|
|
@@ -189,6 +189,10 @@ def update_task(sid, tasks_list):
|
|
|
189
189
|
story_pat = rf"(#{{3,4}} \[?{re.escape(sid)}\]?:?.*?)(?=\n#{{3,4}} |\Z)"
|
|
190
190
|
story_match = re.search(story_pat, content, re.DOTALL)
|
|
191
191
|
if not story_match:
|
|
192
|
+
# Fallback: check if story exists as a bullet in the Done section
|
|
193
|
+
done_pos = content.find(_DONE)
|
|
194
|
+
if done_pos != -1 and re.search(rf"\*\*{re.escape(sid)}\*\*", content[done_pos:]):
|
|
195
|
+
return f"✅ Already done: {sid} (all tasks complete)"
|
|
192
196
|
return f"❌ Story {sid} not found"
|
|
193
197
|
story_block = story_match.group(1)
|
|
194
198
|
|
|
@@ -600,7 +600,7 @@ def _build_file_graph(root, all_files, module_index, file_to_node, focus, depth=
|
|
|
600
600
|
target_ids = set()
|
|
601
601
|
for f, nid in file_to_node.items():
|
|
602
602
|
rel = str(f.relative_to(root))
|
|
603
|
-
if rel == focus or rel.endswith('/' + focus):
|
|
603
|
+
if rel == focus or rel.endswith('/' + focus) or f.stem == focus:
|
|
604
604
|
target_ids.add(nid)
|
|
605
605
|
if not target_ids:
|
|
606
606
|
return None, f"❌ Focus target '{focus}' not found. (Scanned {len(all_files)} files)"
|
|
@@ -117,13 +117,14 @@ class TestInitClassicFormat:
|
|
|
117
117
|
assert len(agent_files) >= 1
|
|
118
118
|
|
|
119
119
|
def test_init_creates_command_files(self, tmp_path):
|
|
120
|
-
"""pactkit init creates command playbook files."""
|
|
120
|
+
"""pactkit init creates command playbook files (STORY-slim-063: now in skills/ subdirs)."""
|
|
121
121
|
target = tmp_path / "classic_deploy"
|
|
122
122
|
run_pactkit("init", "-t", str(target))
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
skills_dir = target / "skills"
|
|
125
|
+
# Commands are now deployed as skills/{name}/SKILL.md
|
|
126
|
+
command_skill_files = list(skills_dir.glob("project-*/SKILL.md"))
|
|
127
|
+
assert len(command_skill_files) >= 1
|
|
127
128
|
|
|
128
129
|
|
|
129
130
|
@pytest.mark.e2e
|
|
@@ -260,14 +261,16 @@ class TestDeploymentCompleteness:
|
|
|
260
261
|
assert deployed == set(VALID_AGENTS)
|
|
261
262
|
|
|
262
263
|
def test_all_commands_deployed(self, deploy_target):
|
|
263
|
-
"""AC2: Exactly 11 commands deployed
|
|
264
|
+
"""AC2: Exactly 11 commands deployed as skills/{name}/SKILL.md (STORY-slim-063)."""
|
|
264
265
|
from pactkit.config import VALID_COMMANDS
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
skills_dir = deploy_target / "skills"
|
|
267
|
+
# Commands are now deployed as skills/{name}/SKILL.md subdirectories
|
|
268
|
+
deployed = {d.name for d in skills_dir.iterdir()
|
|
269
|
+
if d.is_dir() and d.name.startswith("project-")}
|
|
267
270
|
assert deployed == set(VALID_COMMANDS)
|
|
268
271
|
|
|
269
272
|
def test_all_skills_deployed_with_skill_md(self, deploy_target):
|
|
270
|
-
"""AC3: Exactly
|
|
273
|
+
"""AC3: Exactly 21 skill dirs deployed (10 embedded + 11 commands), each contains SKILL.md."""
|
|
271
274
|
from pactkit.config import VALID_SKILLS
|
|
272
275
|
skills_dir = deploy_target / "skills"
|
|
273
276
|
deployed_dirs = {d.name for d in skills_dir.iterdir() if d.is_dir()}
|
|
@@ -112,7 +112,8 @@ class TestDeployedCommandFiles:
|
|
|
112
112
|
from pactkit.generators.deployer import deploy
|
|
113
113
|
deploy(mode='expert')
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
116
|
+
check_file = tmp_path / '.claude' / 'skills' / 'project-check' / 'SKILL.md'
|
|
116
117
|
assert check_file.exists()
|
|
117
118
|
content = check_file.read_text()
|
|
118
119
|
fm = _parse_frontmatter(content)
|
|
@@ -43,8 +43,9 @@ class TestGetDefaultConfig:
|
|
|
43
43
|
assert len(cfg["commands"]) == 11
|
|
44
44
|
|
|
45
45
|
def test_default_skills_count(self):
|
|
46
|
+
# STORY-slim-063: VALID_SKILLS now includes 11 commands migrated to skills/ subdirs
|
|
46
47
|
cfg = _config().get_default_config()
|
|
47
|
-
assert len(cfg["skills"]) ==
|
|
48
|
+
assert len(cfg["skills"]) == 21
|
|
48
49
|
|
|
49
50
|
def test_default_rules_count(self):
|
|
50
51
|
"""9 rule modules (added 09-sectional-write)."""
|
|
@@ -76,11 +76,14 @@ class TestScenario1_StaleCommandsCleaned:
|
|
|
76
76
|
assert not (cmds / "project-old-removed.md").exists()
|
|
77
77
|
|
|
78
78
|
def test_current_commands_all_exist(self, tmp_path):
|
|
79
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
79
80
|
_run_deploy(tmp_path)
|
|
80
|
-
|
|
81
|
+
skills_dir = tmp_path / ".claude" / "skills"
|
|
81
82
|
|
|
82
83
|
for filename in COMMANDS_CONTENT:
|
|
83
|
-
|
|
84
|
+
cmd_name = filename.removesuffix(".md")
|
|
85
|
+
assert (skills_dir / cmd_name / "SKILL.md").is_file(), \
|
|
86
|
+
f"Missing: skills/{cmd_name}/SKILL.md"
|
|
84
87
|
|
|
85
88
|
|
|
86
89
|
class TestScenario2_UserCommandsPreserved:
|
|
@@ -97,8 +97,9 @@ class TestSkillPromotion:
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
def test_valid_skills_count(self):
|
|
100
|
+
# STORY-slim-063: VALID_SKILLS expanded to 21 (10 embedded + 11 commands)
|
|
100
101
|
cfg = _config()
|
|
101
|
-
assert len(cfg.VALID_SKILLS) ==
|
|
102
|
+
assert len(cfg.VALID_SKILLS) == 21
|
|
102
103
|
|
|
103
104
|
def test_new_skills_present(self):
|
|
104
105
|
cfg = _config()
|
|
@@ -136,9 +137,10 @@ class TestSkillPromotion:
|
|
|
136
137
|
f"{var_name} should have YAML frontmatter"
|
|
137
138
|
|
|
138
139
|
def test_default_config_has_10_skills(self):
|
|
140
|
+
# STORY-slim-063: default config skills now includes all 21 (10 embedded + 11 commands)
|
|
139
141
|
cfg = _config()
|
|
140
142
|
default = cfg.get_default_config()
|
|
141
|
-
assert len(default['skills']) ==
|
|
143
|
+
assert len(default['skills']) == 21
|
|
142
144
|
|
|
143
145
|
def test_default_config_has_9_commands(self):
|
|
144
146
|
"""STORY-051: default config now has 11 commands (added project-release, project-pr)."""
|
|
@@ -109,11 +109,15 @@ class TestFormatProfilesRegistry:
|
|
|
109
109
|
assert p.supports_model_routing is True
|
|
110
110
|
assert p.pactkit_yaml_path == ".opencode/pactkit.yaml"
|
|
111
111
|
|
|
112
|
-
def
|
|
113
|
-
"""STORY-slim-
|
|
112
|
+
def test_codex_profile_exists(self):
|
|
113
|
+
"""STORY-slim-060: codex profile registered for thin adapter."""
|
|
114
114
|
from pactkit.profiles import FORMAT_PROFILES
|
|
115
115
|
|
|
116
|
-
assert "codex"
|
|
116
|
+
assert "codex" in FORMAT_PROFILES
|
|
117
|
+
p = FORMAT_PROFILES["codex"]
|
|
118
|
+
assert p.name == "codex"
|
|
119
|
+
assert p.display_name == "Codex CLI"
|
|
120
|
+
assert p.global_config_dir == "~/.codex"
|
|
117
121
|
|
|
118
122
|
def test_classic_excluded_fields(self):
|
|
119
123
|
from pactkit.profiles import FORMAT_PROFILES
|
|
@@ -285,16 +285,17 @@ class TestDeployedOutputIntegration:
|
|
|
285
285
|
assert "{VISUALIZE_CMD}" not in content
|
|
286
286
|
|
|
287
287
|
def test_classic_command_has_classic_path(self, tmp_path):
|
|
288
|
-
"""Classic commands should have ~/.claude/skills/ references."""
|
|
288
|
+
"""Classic commands should have ~/.claude/skills/ references (STORY-slim-063: now skills subdirs)."""
|
|
289
289
|
from pactkit.generators.deployer import _deploy_commands
|
|
290
290
|
from pactkit.profiles import get_profile
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
skills_dir = tmp_path / "skills"
|
|
293
|
+
skills_dir.mkdir()
|
|
294
294
|
profile = get_profile("classic")
|
|
295
|
-
_deploy_commands(
|
|
295
|
+
_deploy_commands(skills_dir, ["project-done"], profile=profile)
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
# STORY-slim-063: deployed as skills_dir/{name}/SKILL.md
|
|
298
|
+
done_cmd = skills_dir / "project-done" / "SKILL.md"
|
|
298
299
|
assert done_cmd.exists()
|
|
299
300
|
content = done_cmd.read_text()
|
|
300
301
|
assert "~/.claude/skills" in content
|
|
@@ -42,10 +42,13 @@ class TestFullConfigDeploysAll:
|
|
|
42
42
|
assert (agents_dir / f"{name}.md").is_file(), f"Missing agent: {name}"
|
|
43
43
|
|
|
44
44
|
def test_all_commands_deployed(self, tmp_path):
|
|
45
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
45
46
|
claude = _run_deploy(tmp_path, config=get_default_config())
|
|
46
|
-
|
|
47
|
+
skills_dir = claude / "skills"
|
|
47
48
|
for filename in COMMANDS_CONTENT:
|
|
48
|
-
|
|
49
|
+
cmd_name = filename.removesuffix(".md")
|
|
50
|
+
assert (skills_dir / cmd_name / "SKILL.md").is_file(), \
|
|
51
|
+
f"Missing command skill: skills/{cmd_name}/SKILL.md"
|
|
49
52
|
|
|
50
53
|
def test_all_skills_deployed(self, tmp_path):
|
|
51
54
|
claude = _run_deploy(tmp_path, config=get_default_config())
|
|
@@ -123,19 +126,20 @@ class TestPartialAgentConfig:
|
|
|
123
126
|
|
|
124
127
|
class TestPartialCommandConfig:
|
|
125
128
|
def test_only_selected_commands_deployed(self, tmp_path):
|
|
129
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
126
130
|
cfg = get_default_config()
|
|
127
131
|
cfg["commands"] = ["project-plan", "project-act", "project-done"]
|
|
128
132
|
|
|
129
133
|
claude = _run_deploy(tmp_path, config=cfg)
|
|
130
|
-
|
|
134
|
+
skills_dir = claude / "skills"
|
|
131
135
|
|
|
132
|
-
assert (
|
|
133
|
-
assert (
|
|
134
|
-
assert (
|
|
136
|
+
assert (skills_dir / "project-plan" / "SKILL.md").is_file()
|
|
137
|
+
assert (skills_dir / "project-act" / "SKILL.md").is_file()
|
|
138
|
+
assert (skills_dir / "project-done" / "SKILL.md").is_file()
|
|
135
139
|
|
|
136
140
|
# Others should NOT exist
|
|
137
|
-
assert not (
|
|
138
|
-
assert not (
|
|
141
|
+
assert not (skills_dir / "project-check" / "SKILL.md").exists()
|
|
142
|
+
assert not (skills_dir / "project-sprint" / "SKILL.md").exists()
|
|
139
143
|
|
|
140
144
|
def test_user_custom_command_preserved(self, tmp_path):
|
|
141
145
|
claude = tmp_path / ".claude"
|
|
@@ -235,16 +239,18 @@ class TestConfigAutoGeneration:
|
|
|
235
239
|
|
|
236
240
|
class TestDeploymentSummary:
|
|
237
241
|
def test_summary_printed_full(self, tmp_path, capsys):
|
|
238
|
-
# STORY-
|
|
242
|
+
# STORY-slim-063: summary format changed to unified Skills count
|
|
243
|
+
# "21/21 Skills (10 embedded + 11 commands)"
|
|
239
244
|
_run_deploy(tmp_path, config=get_default_config())
|
|
240
245
|
output = capsys.readouterr().out
|
|
241
246
|
assert "9/9 Agents" in output
|
|
242
|
-
assert "
|
|
243
|
-
assert "10
|
|
247
|
+
assert "21/21 Skills" in output
|
|
248
|
+
assert "10 embedded" in output
|
|
249
|
+
assert "11 commands" in output
|
|
244
250
|
assert "8/8 Rules" in output or "9/9 Rules" in output # 9 rules after 09-sectional-write
|
|
245
251
|
|
|
246
252
|
def test_summary_printed_partial(self, tmp_path, capsys):
|
|
247
|
-
# STORY-
|
|
253
|
+
# STORY-slim-063: partial deploy — commands counted within Skills line
|
|
248
254
|
cfg = get_default_config()
|
|
249
255
|
cfg["agents"] = ["system-architect", "senior-developer"]
|
|
250
256
|
cfg["commands"] = ["project-plan", "project-act", "project-done"]
|
|
@@ -252,7 +258,8 @@ class TestDeploymentSummary:
|
|
|
252
258
|
_run_deploy(tmp_path, config=cfg)
|
|
253
259
|
output = capsys.readouterr().out
|
|
254
260
|
assert "2/9 Agents" in output
|
|
255
|
-
|
|
261
|
+
# 10 embedded skills + 3 commands = 13 total skills deployed out of 21
|
|
262
|
+
assert "13/21 Skills" in output
|
|
256
263
|
|
|
257
264
|
|
|
258
265
|
# ===========================================================================
|
|
@@ -145,13 +145,15 @@ class TestSprintDeployment:
|
|
|
145
145
|
"""Scenario 6: 部署后文件存在且正确"""
|
|
146
146
|
|
|
147
147
|
def test_sprint_file_deployed(self, tmp_path):
|
|
148
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
148
149
|
_deploy(tmp_path)
|
|
149
|
-
sprint_file = tmp_path / '.claude' / '
|
|
150
|
-
assert sprint_file.exists(), 'project-sprint.md 未部署'
|
|
150
|
+
sprint_file = tmp_path / '.claude' / 'skills' / 'project-sprint' / 'SKILL.md'
|
|
151
|
+
assert sprint_file.exists(), 'project-sprint/SKILL.md 未部署'
|
|
151
152
|
|
|
152
153
|
def test_deployed_sprint_has_frontmatter(self, tmp_path):
|
|
154
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
153
155
|
_deploy(tmp_path)
|
|
154
|
-
sprint_file = tmp_path / '.claude' / '
|
|
156
|
+
sprint_file = tmp_path / '.claude' / 'skills' / 'project-sprint' / 'SKILL.md'
|
|
155
157
|
content = sprint_file.read_text()
|
|
156
158
|
fm = _parse_frontmatter(content)
|
|
157
159
|
assert 'description' in fm
|
|
@@ -184,14 +186,15 @@ class TestBackwardCompatibility:
|
|
|
184
186
|
assert agent in AGENTS_EXPERT, f'现有 Agent {agent} 丢失'
|
|
185
187
|
|
|
186
188
|
def test_all_existing_commands_deployed(self, tmp_path):
|
|
189
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
187
190
|
_deploy(tmp_path)
|
|
188
|
-
|
|
191
|
+
skills_dir = tmp_path / '.claude' / 'skills'
|
|
189
192
|
expected = [
|
|
190
|
-
'project-plan
|
|
191
|
-
'project-done
|
|
193
|
+
'project-plan', 'project-act', 'project-check',
|
|
194
|
+
'project-done', 'project-init',
|
|
192
195
|
]
|
|
193
196
|
for cmd in expected:
|
|
194
|
-
assert (
|
|
197
|
+
assert (skills_dir / cmd / 'SKILL.md').exists(), f'部署后 skills/{cmd}/SKILL.md 丢失'
|
|
195
198
|
|
|
196
199
|
|
|
197
200
|
# =============================================================================
|
|
@@ -84,12 +84,13 @@ class TestAC4DeclineWarning:
|
|
|
84
84
|
class TestDeployedInitCommand:
|
|
85
85
|
|
|
86
86
|
def test_deployed_init_contains_git_guard(self, tmp_path):
|
|
87
|
-
"""Deployed project-init.md contains git guard section."""
|
|
87
|
+
"""Deployed project-init/SKILL.md contains git guard section (STORY-slim-063)."""
|
|
88
88
|
from pactkit.config import get_default_config
|
|
89
89
|
from pactkit.generators.deployer import deploy
|
|
90
90
|
config = get_default_config()
|
|
91
91
|
deploy(config=config, target=str(tmp_path / '.claude'))
|
|
92
|
-
|
|
92
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
93
|
+
init_file = tmp_path / '.claude' / 'skills' / 'project-init' / 'SKILL.md'
|
|
93
94
|
assert init_file.exists()
|
|
94
95
|
content = init_file.read_text()
|
|
95
96
|
assert 'git' in content.lower()
|
|
@@ -92,12 +92,13 @@ class TestAC4UserConfirmation:
|
|
|
92
92
|
class TestDeployedPlanCommand:
|
|
93
93
|
|
|
94
94
|
def test_deployed_plan_contains_greenfield(self, tmp_path):
|
|
95
|
-
"""Deployed project-plan.md contains greenfield heuristic."""
|
|
95
|
+
"""Deployed project-plan/SKILL.md contains greenfield heuristic (STORY-slim-063)."""
|
|
96
96
|
from pactkit.config import get_default_config
|
|
97
97
|
from pactkit.generators.deployer import deploy
|
|
98
98
|
config = get_default_config()
|
|
99
99
|
deploy(config=config, target=str(tmp_path / '.claude'))
|
|
100
|
-
|
|
100
|
+
# STORY-slim-063: commands are now deployed as skills/{name}/SKILL.md
|
|
101
|
+
plan_file = tmp_path / '.claude' / 'skills' / 'project-plan' / 'SKILL.md'
|
|
101
102
|
assert plan_file.exists()
|
|
102
103
|
content = plan_file.read_text()
|
|
103
104
|
assert 'greenfield' in content.lower() or 'Greenfield' in content
|