Dev10x 0.97.0__tar.gz → 0.99.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.
- {dev10x-0.97.0/src/Dev10x.egg-info → dev10x-0.99.0}/PKG-INFO +1 -1
- {dev10x-0.97.0 → dev10x-0.99.0}/pyproject.toml +1 -1
- {dev10x-0.97.0 → dev10x-0.99.0/src/Dev10x.egg-info}/PKG-INFO +1 -1
- {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/SOURCES.txt +13 -1
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/github_app.py +87 -2
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/permission.py +41 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/session.py +38 -0
- dev10x-0.99.0/src/dev10x/domain/common/ide_choice.py +109 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/config_migration.py +177 -18
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/dev10x_paths.py +80 -1
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/session_yaml.py +107 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/gate_policy.py +55 -7
- dev10x-0.99.0/src/dev10x/domain/pr_body.py +220 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/sensitivity.py +121 -1
- dev10x-0.99.0/src/dev10x/domain/transport_budget.py +118 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/__init__.py +204 -6
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/app_auth.py +2 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session.py +2 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session_dispatch.py +41 -0
- dev10x-0.99.0/src/dev10x/hooks/stop_verdict.py +354 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/gate_tools.py +68 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/github_tools.py +28 -2
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/misc_tools.py +15 -5
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/monitor_tools.py +7 -4
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/monitor/__init__.py +23 -2
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/runner/__init__.py +82 -12
- dev10x-0.99.0/src/dev10x/session/ide_pin.py +79 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/service.py +24 -4
- dev10x-0.99.0/src/dev10x/skills/common/jtbd.py +130 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/registry.py +2 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/ask_shadows_allow.py +66 -10
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/mcp_vs_script_drift.py +43 -2
- dev10x-0.99.0/src/dev10x/skills/doctor/strategies/retired_session_yaml.py +180 -0
- dev10x-0.99.0/src/dev10x/skills/doctor/strategies/shell_equivalent_mcp_tools.py +143 -0
- dev10x-0.99.0/src/dev10x/skills/merge/__init__.py +9 -0
- dev10x-0.99.0/src/dev10x/skills/merge/fixes_scope.py +281 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/monitor/ci_check_status.py +11 -9
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/gchat_review_request.py +143 -2
- dev10x-0.99.0/src/dev10x/skills/permission/catalog_merge.py +431 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/enumerate_mcp.py +1 -0
- dev10x-0.99.0/src/dev10x/skills/permission/ide_resolve.py +53 -0
- dev10x-0.99.0/src/dev10x/skills/permission/ignore_session_state.py +175 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/update_paths.py +66 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/subprocess_utils.py +68 -1
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/__init__.py +6 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/command-skill-map.yaml +77 -18
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/sensitivity_target.py +24 -3
- dev10x-0.99.0/src/dev10x/validators/write_destination.py +227 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_subprocess_utils.py +89 -0
- dev10x-0.99.0/tests/test_use_cwd_absolute.py +140 -0
- dev10x-0.97.0/src/dev10x/domain/pr_body.py +0 -125
- dev10x-0.97.0/src/dev10x/skills/common/jtbd.py +0 -44
- dev10x-0.97.0/src/dev10x/skills/permission/catalog_merge.py +0 -201
- {dev10x-0.97.0 → dev10x-0.99.0}/LICENSE +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/README.md +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/setup.cfg +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/dependency_links.txt +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/entry_points.txt +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/requires.txt +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/top_level.txt +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/analyze.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/log_pruner.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/log_reader.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/permissions_model.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/summarizer.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/transcript_grammar.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/cli.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/config.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/deps.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/foreman.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/github.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/github_app_api.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/hook.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/init.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/platform.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/playbook.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/skill.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/spec.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/usage.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/validate.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/watchdog.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/config/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/config/friction_presets.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/config/loader.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/db/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/dependency_pins.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/dependency_sweep.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/audit_writer.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/claude_paths.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/accepted_findings.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/allow_rule.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/baseline_catalog.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/bash_tokens.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/branch_name.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/commit_subject.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/config_io.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/mcp_tool_name.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/mktmp_path.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/plugin_loader.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/plugin_version.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/policy.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/policy_migration.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/policy_resolution.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/repository_ref.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/result.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/rule_id.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/singleton_holder.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/skill_name.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/ticket_id.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/tool_signature.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/tracker_choice.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/workspace.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/config_loader.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/cwd_resolver.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/config_document.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/plan.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/session_context.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/session_state.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/settings_document.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/task.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/events/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/events/hook_event.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/events/hook_input.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/file_locks.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/friction_level.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/git_context.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/gitmoji.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/hook_telemetry.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/install_version.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/plugin_identity.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/plugin_origin.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/plugin_root.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/profile_tier.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/policy_rule.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/rule_engine.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/sql.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/validation_rule.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/session_document.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/session_rules.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/session_staleness.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/usage/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/usage/pricing.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/usage/reader.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/watchdog.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/git/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/candidate_rules.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/learn_loop.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/pattern_validation.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/review_patterns.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/rule_authoring.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/rule_confidence.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/audit_emit.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/base.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/edit_validator.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/format_scope.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/hook_transport.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/permission_diagnostics.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session_place.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session_policy.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/skill.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/task_guard.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/task_plan_sync.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/_app.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/audit_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/daemon.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/gate_query.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/git_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/knowledge_prompts.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/knowledge_resources.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/plan_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/release_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/resource_watcher.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/roots_manager.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/roots_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/sampling_manager.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/sampling_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/server_cli.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/server_db.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/session_store.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/strict_args.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/task_index_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/transport.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/usage_tools.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/wiring.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/permission/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/permission/service.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/plan/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/plan/service.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/platform/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/platform/registry.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/release/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/_matcher.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/index_parser.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/norms_renderer.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/safeguards_renderer.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/preset_pin.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/supervisor_review_pin.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/task_index.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/tracker_pin.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/builder.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/catalog.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/resolver.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/analyze_actions.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/analyze_permissions.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/cli_friction.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/eval_gaps.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/extract_session.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/instruction_budget.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/privacy.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/common/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/database/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/forbidden_token_priming.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/home_in_additional_directories.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/mcp_horizontal_duplicates.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/missing_linear_mcp_allow.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategy.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/evidence/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/foreman/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/foreman/watch.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/git_fixup/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/git_fixup/find_fixup_target.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/monitor/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/monitor/pr_notify.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/_gh.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/gchat_cards.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/gchat_notify.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/slack_notify.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/slack_review_request.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/orchestration/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/orchestration/batch_detection.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/orchestration/subagent_protocol.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/accepted_findings.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/backup.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/baseline-permissions.yaml +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/catalog_gap.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/catalog_paths.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/clean_project_files.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/cli_catalog.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/config.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/doctor.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/file_lock.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/generalize.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/manifest.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/merge_worktree_permissions.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/migrate_config.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_audit.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_authoring.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_catalog_migration.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_renderer.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/promote.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/provenance.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/resolve.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/tracker_resolve.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/fixtures.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/matrix.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/policy_report.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/report.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/runner.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/compare.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/discovery.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/report.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/release/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/release/classifier.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/release/collect_prs.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/spec/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/spec/drift_detector.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/utilities/__init__.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/utilities/slack.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/_quote_strip.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/base.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/bash_aggregation.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/command_substitution.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/commit_jtbd.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/execution_safety.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/inline_linter.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/mcp_prefix.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/pipeline_allow.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/pr_base.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/prefix_friction.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/redundant_fetch.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/registry.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/safe_expansion.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/safe_subshell.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/sensitivity_exceptions.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/skill_redirect.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/spec_drift.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/sql_safety.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cli.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cli_lazy.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cwd_discipline_sweep.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cwd_enforcement.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_dependency_pins.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_dependency_sweep.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_integration.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_no_home_claude_literals.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_no_module_scope_gitcontext.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_regenerable_caches_untracked.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_retired_config_paths.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_script_loadability.py +0 -0
- {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_shell_script_flags.py +0 -0
|
@@ -64,6 +64,7 @@ src/dev10x/domain/sensitivity.py
|
|
|
64
64
|
src/dev10x/domain/session_document.py
|
|
65
65
|
src/dev10x/domain/session_rules.py
|
|
66
66
|
src/dev10x/domain/session_staleness.py
|
|
67
|
+
src/dev10x/domain/transport_budget.py
|
|
67
68
|
src/dev10x/domain/watchdog.py
|
|
68
69
|
src/dev10x/domain/common/__init__.py
|
|
69
70
|
src/dev10x/domain/common/accepted_findings.py
|
|
@@ -73,6 +74,7 @@ src/dev10x/domain/common/bash_tokens.py
|
|
|
73
74
|
src/dev10x/domain/common/branch_name.py
|
|
74
75
|
src/dev10x/domain/common/commit_subject.py
|
|
75
76
|
src/dev10x/domain/common/config_io.py
|
|
77
|
+
src/dev10x/domain/common/ide_choice.py
|
|
76
78
|
src/dev10x/domain/common/mcp_tool_name.py
|
|
77
79
|
src/dev10x/domain/common/mktmp_path.py
|
|
78
80
|
src/dev10x/domain/common/plugin_loader.py
|
|
@@ -129,6 +131,7 @@ src/dev10x/hooks/session_dispatch.py
|
|
|
129
131
|
src/dev10x/hooks/session_place.py
|
|
130
132
|
src/dev10x/hooks/session_policy.py
|
|
131
133
|
src/dev10x/hooks/skill.py
|
|
134
|
+
src/dev10x/hooks/stop_verdict.py
|
|
132
135
|
src/dev10x/hooks/task_guard.py
|
|
133
136
|
src/dev10x/hooks/task_plan_sync.py
|
|
134
137
|
src/dev10x/mcp/__init__.py
|
|
@@ -173,6 +176,7 @@ src/dev10x/scope/index_parser.py
|
|
|
173
176
|
src/dev10x/scope/norms_renderer.py
|
|
174
177
|
src/dev10x/scope/safeguards_renderer.py
|
|
175
178
|
src/dev10x/session/__init__.py
|
|
179
|
+
src/dev10x/session/ide_pin.py
|
|
176
180
|
src/dev10x/session/preset_pin.py
|
|
177
181
|
src/dev10x/session/service.py
|
|
178
182
|
src/dev10x/session/supervisor_review_pin.py
|
|
@@ -204,11 +208,15 @@ src/dev10x/skills/doctor/strategies/home_in_additional_directories.py
|
|
|
204
208
|
src/dev10x/skills/doctor/strategies/mcp_horizontal_duplicates.py
|
|
205
209
|
src/dev10x/skills/doctor/strategies/mcp_vs_script_drift.py
|
|
206
210
|
src/dev10x/skills/doctor/strategies/missing_linear_mcp_allow.py
|
|
211
|
+
src/dev10x/skills/doctor/strategies/retired_session_yaml.py
|
|
212
|
+
src/dev10x/skills/doctor/strategies/shell_equivalent_mcp_tools.py
|
|
207
213
|
src/dev10x/skills/evidence/__init__.py
|
|
208
214
|
src/dev10x/skills/foreman/__init__.py
|
|
209
215
|
src/dev10x/skills/foreman/watch.py
|
|
210
216
|
src/dev10x/skills/git_fixup/__init__.py
|
|
211
217
|
src/dev10x/skills/git_fixup/find_fixup_target.py
|
|
218
|
+
src/dev10x/skills/merge/__init__.py
|
|
219
|
+
src/dev10x/skills/merge/fixes_scope.py
|
|
212
220
|
src/dev10x/skills/monitor/__init__.py
|
|
213
221
|
src/dev10x/skills/monitor/ci_check_status.py
|
|
214
222
|
src/dev10x/skills/monitor/pr_notify.py
|
|
@@ -236,6 +244,8 @@ src/dev10x/skills/permission/doctor.py
|
|
|
236
244
|
src/dev10x/skills/permission/enumerate_mcp.py
|
|
237
245
|
src/dev10x/skills/permission/file_lock.py
|
|
238
246
|
src/dev10x/skills/permission/generalize.py
|
|
247
|
+
src/dev10x/skills/permission/ide_resolve.py
|
|
248
|
+
src/dev10x/skills/permission/ignore_session_state.py
|
|
239
249
|
src/dev10x/skills/permission/manifest.py
|
|
240
250
|
src/dev10x/skills/permission/merge_worktree_permissions.py
|
|
241
251
|
src/dev10x/skills/permission/migrate_config.py
|
|
@@ -287,6 +297,7 @@ src/dev10x/validators/sensitivity_target.py
|
|
|
287
297
|
src/dev10x/validators/skill_redirect.py
|
|
288
298
|
src/dev10x/validators/spec_drift.py
|
|
289
299
|
src/dev10x/validators/sql_safety.py
|
|
300
|
+
src/dev10x/validators/write_destination.py
|
|
290
301
|
tests/test_cli.py
|
|
291
302
|
tests/test_cli_lazy.py
|
|
292
303
|
tests/test_cwd_discipline_sweep.py
|
|
@@ -300,4 +311,5 @@ tests/test_regenerable_caches_untracked.py
|
|
|
300
311
|
tests/test_retired_config_paths.py
|
|
301
312
|
tests/test_script_loadability.py
|
|
302
313
|
tests/test_shell_script_flags.py
|
|
303
|
-
tests/test_subprocess_utils.py
|
|
314
|
+
tests/test_subprocess_utils.py
|
|
315
|
+
tests/test_use_cwd_absolute.py
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Walks the engineer through GitHub App registration with install-target
|
|
4
4
|
guidance, picks up the downloaded ``.pem`` from disk, and runs an
|
|
5
5
|
end-to-end verification (App JWT → installations → installation token →
|
|
6
|
-
repo read) before writing config under ``~/.
|
|
6
|
+
repo read) before writing config under ``~/.config/Dev10x/github-bot/``.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
@@ -13,17 +13,37 @@ import shutil
|
|
|
13
13
|
import sys
|
|
14
14
|
from dataclasses import dataclass, field
|
|
15
15
|
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
16
17
|
|
|
17
18
|
import click
|
|
18
19
|
|
|
19
20
|
from dev10x.commands import github_app_api as api
|
|
20
21
|
from dev10x.domain.dev10x_paths import Dev10xConfigDir
|
|
21
22
|
from dev10x.domain.file_locks import atomic_write_text
|
|
23
|
+
from dev10x.github.app_auth import AppConfig
|
|
22
24
|
|
|
23
25
|
CONFIG_DIR = Dev10xConfigDir.github_bot_dir()
|
|
24
26
|
CONFIG_PATH = Dev10xConfigDir.github_app_yaml()
|
|
25
27
|
KEY_PATH = CONFIG_DIR / "dev10x-bot.pem"
|
|
26
28
|
|
|
29
|
+
# What the plugin calls, and the capability each one unlocks. Reported
|
|
30
|
+
# against the permissions the INSTALLATION was granted, not the ones the
|
|
31
|
+
# App requests: editing an App's permissions raises a request the
|
|
32
|
+
# installation must accept, and until it does every call runs on the old
|
|
33
|
+
# set (GH-1271).
|
|
34
|
+
NEEDED_PERMISSIONS = {
|
|
35
|
+
"pull_requests": ("write", "review-thread replies, PR summary comments"),
|
|
36
|
+
"issues": ("write", "comments on issues — the shared /issues/ endpoint"),
|
|
37
|
+
"contents": ("write", "repo reads; bot-authored commits"),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Short of these the bot is degraded, not broken, so status still passes.
|
|
41
|
+
# The setup doc sanctions `Contents: read` for comments-without-commit-
|
|
42
|
+
# identity; failing that configuration would contradict our own guidance.
|
|
43
|
+
OPTIONAL_PERMISSIONS = {"contents": "commit identity unavailable; comments still work"}
|
|
44
|
+
|
|
45
|
+
_PERMISSION_RANK = {"read": 1, "write": 2, "admin": 3}
|
|
46
|
+
|
|
27
47
|
PERSONAL_NEW_APP_URL = "https://github.com/settings/apps/new"
|
|
28
48
|
|
|
29
49
|
INTRO = """\
|
|
@@ -476,7 +496,7 @@ def setup(*, force: bool, paste: bool) -> None:
|
|
|
476
496
|
|
|
477
497
|
@github_app.command()
|
|
478
498
|
def status() -> None:
|
|
479
|
-
"""Show
|
|
499
|
+
"""Show GitHub App config status and the permissions actually granted."""
|
|
480
500
|
if not CONFIG_PATH.exists():
|
|
481
501
|
click.echo(f"No config at {CONFIG_PATH}")
|
|
482
502
|
click.echo("Run `dev10x github-app setup` to create one.")
|
|
@@ -490,3 +510,68 @@ def status() -> None:
|
|
|
490
510
|
click.echo(f"Key mode: {mode}")
|
|
491
511
|
if mode != "0o600":
|
|
492
512
|
click.echo(" ⚠ Expected 0o600 — fix with: chmod 600 " + str(KEY_PATH))
|
|
513
|
+
|
|
514
|
+
click.echo("")
|
|
515
|
+
if not _report_granted_permissions():
|
|
516
|
+
sys.exit(1)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def _report_granted_permissions() -> bool:
|
|
520
|
+
"""Exercise the credentials and report granted vs needed permissions.
|
|
521
|
+
|
|
522
|
+
A status command that reads config and stops is the defect GH-1271
|
|
523
|
+
names: token resolution falls back to user auth silently, so an
|
|
524
|
+
unusable App and an absent one look identical from the outside.
|
|
525
|
+
"""
|
|
526
|
+
config = AppConfig.load(path=CONFIG_PATH)
|
|
527
|
+
if config is None:
|
|
528
|
+
click.echo("Token check: ✗ config is unreadable, disabled, or missing app_id")
|
|
529
|
+
click.echo(" The bot will fall back to your engineer identity on every call.")
|
|
530
|
+
return False
|
|
531
|
+
|
|
532
|
+
try:
|
|
533
|
+
jwt_token = api.mint_app_jwt(
|
|
534
|
+
app_id=config.app_id, private_key=config.private_key_path.read_text()
|
|
535
|
+
)
|
|
536
|
+
installations = api.list_installations(jwt_token=jwt_token)
|
|
537
|
+
except Exception as exc:
|
|
538
|
+
# Any failure here is a report line, not a traceback: the whole
|
|
539
|
+
# point is to name what silently falls back (GH-1271).
|
|
540
|
+
click.echo(f"Token check: ✗ {exc}")
|
|
541
|
+
click.echo(" The bot will fall back to your engineer identity on every call.")
|
|
542
|
+
return False
|
|
543
|
+
|
|
544
|
+
if not installations:
|
|
545
|
+
click.echo("Token check: ✗ App is registered but not installed anywhere")
|
|
546
|
+
return False
|
|
547
|
+
|
|
548
|
+
# Every installation is reported, not just the first: an App on both
|
|
549
|
+
# a personal account and an org has independent permission sets, and
|
|
550
|
+
# picking one is a coin flip on exactly the setup the doc calls a trap.
|
|
551
|
+
complete = True
|
|
552
|
+
for installation in installations:
|
|
553
|
+
complete = _report_one_installation(installation) and complete
|
|
554
|
+
|
|
555
|
+
if not complete:
|
|
556
|
+
click.echo("")
|
|
557
|
+
click.echo(" A permission you added still needs the INSTALLATION to accept it.")
|
|
558
|
+
click.echo(" Personal install: https://github.com/settings/installations")
|
|
559
|
+
click.echo(" Org install: /organizations/<org>/settings/installations")
|
|
560
|
+
return complete
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def _report_one_installation(installation: dict[str, Any]) -> bool:
|
|
564
|
+
account = (installation.get("account") or {}).get("login", "?")
|
|
565
|
+
click.echo(f"Installation {installation.get('id')} ({account}):")
|
|
566
|
+
|
|
567
|
+
granted = installation.get("permissions") or {}
|
|
568
|
+
satisfied = True
|
|
569
|
+
for name, (needed, unlocks) in NEEDED_PERMISSIONS.items():
|
|
570
|
+
actual = granted.get(name)
|
|
571
|
+
has_level = _PERMISSION_RANK.get(actual or "", 0) >= _PERMISSION_RANK[needed]
|
|
572
|
+
optional = name in OPTIONAL_PERMISSIONS
|
|
573
|
+
mark = "✓" if has_level else ("•" if optional else "✗")
|
|
574
|
+
suffix = "" if has_level else (f" — {OPTIONAL_PERMISSIONS[name]}" if optional else "")
|
|
575
|
+
click.echo(f" {mark} {name}: {actual or 'none'} (need {needed}) — {unlocks}{suffix}")
|
|
576
|
+
satisfied = satisfied and (has_level or optional)
|
|
577
|
+
return satisfied
|
|
@@ -774,6 +774,47 @@ def seed_worktree(*, worktree_path: str, dry_run: bool) -> None:
|
|
|
774
774
|
click.echo(f"{verb} {result.value['added']} rule(s) → {result.value['path']}")
|
|
775
775
|
|
|
776
776
|
|
|
777
|
+
@permission.command(name="ensure-ignored")
|
|
778
|
+
@click.option("--dry-run", is_flag=True, help="Show changes without modifying files")
|
|
779
|
+
@click.option("--quiet", is_flag=True, help="Suppress per-repo details")
|
|
780
|
+
def ensure_ignored(*, dry_run: bool, quiet: bool) -> None:
|
|
781
|
+
"""Keep Dev10x's own session state out of `git status` (GH-1275).
|
|
782
|
+
|
|
783
|
+
Writes `.claude/Dev10x/` to each managed repo's `.git/info/exclude`.
|
|
784
|
+
That file lives in the git COMMON dir, so one write per repo covers
|
|
785
|
+
every present and future worktree of it — there is no per-worktree
|
|
786
|
+
pass to keep up to date.
|
|
787
|
+
"""
|
|
788
|
+
from dev10x.skills.permission import ignore_session_state as mod
|
|
789
|
+
|
|
790
|
+
ctx = _require_context()
|
|
791
|
+
roots = ctx.config.get("roots", [])
|
|
792
|
+
if not roots:
|
|
793
|
+
click.echo("No roots configured. Run `dev10x permission init` first.")
|
|
794
|
+
return
|
|
795
|
+
|
|
796
|
+
if dry_run and not quiet:
|
|
797
|
+
click.echo("(dry run — no files will be modified)\n")
|
|
798
|
+
|
|
799
|
+
outcomes = mod.ensure_ignored_for_roots(
|
|
800
|
+
repo_roots=[Path(root) for root in roots],
|
|
801
|
+
dry_run=dry_run,
|
|
802
|
+
)
|
|
803
|
+
if not quiet:
|
|
804
|
+
for outcome in outcomes:
|
|
805
|
+
click.echo(outcome.summary())
|
|
806
|
+
|
|
807
|
+
changed = [outcome for outcome in outcomes if outcome.changed]
|
|
808
|
+
verb = "would gain" if dry_run else "gained"
|
|
809
|
+
click.echo(f"\n{len(changed)} of {len(outcomes)} repo(s) {verb} the rule.")
|
|
810
|
+
|
|
811
|
+
suggestions = [text for text in map(mod.suggestion_for, changed) if text]
|
|
812
|
+
if suggestions:
|
|
813
|
+
click.echo("\nOptional — a tracked rule survives a fresh clone:")
|
|
814
|
+
for text in suggestions:
|
|
815
|
+
click.echo(text)
|
|
816
|
+
|
|
817
|
+
|
|
777
818
|
@permission.command(name="merge-worktree")
|
|
778
819
|
@click.option("--dry-run", is_flag=True, help="Show changes without modifying files")
|
|
779
820
|
@click.option("--restore", is_flag=True, help="Restore settings from most recent backups")
|
|
@@ -336,3 +336,41 @@ def set_playbook(*, skill: str, modes: tuple[str, ...], skip_steps: tuple[str, .
|
|
|
336
336
|
skip_steps=list(skip_steps) or None,
|
|
337
337
|
)
|
|
338
338
|
click.echo(f"wrote playbook modes for {skill} to {written}")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@session.command("reap")
|
|
342
|
+
@click.option(
|
|
343
|
+
"--dry-run",
|
|
344
|
+
is_flag=True,
|
|
345
|
+
help="Report what would be removed without writing.",
|
|
346
|
+
)
|
|
347
|
+
def reap(*, dry_run: bool) -> None:
|
|
348
|
+
"""Drop durable project pins whose every named checkout is gone.
|
|
349
|
+
|
|
350
|
+
The maintenance machinery's only removal path (GH-1253). Every other
|
|
351
|
+
command ensures something is present, so nothing has ever retired a
|
|
352
|
+
pin for an ephemeral worktree that no longer exists — and
|
|
353
|
+
first-match-wins evaluation walks all of them before reaching a real
|
|
354
|
+
project.
|
|
355
|
+
|
|
356
|
+
An entry is removed only when it names at least one absolute path and
|
|
357
|
+
every such path is absent. A glob-only entry names no checkout that
|
|
358
|
+
can be checked, so it is always kept.
|
|
359
|
+
"""
|
|
360
|
+
from dev10x.domain.dev10x_paths import Dev10xConfigDir
|
|
361
|
+
from dev10x.domain.documents.session_yaml import (
|
|
362
|
+
is_provably_dead,
|
|
363
|
+
project_entries,
|
|
364
|
+
reap_dead_projects,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
if dry_run:
|
|
368
|
+
path = Dev10xConfigDir.friction_yaml()
|
|
369
|
+
entries = project_entries(path=path)
|
|
370
|
+
dead = [entry for entry in entries if is_provably_dead(entry)]
|
|
371
|
+
click.echo(f" {path}: {len(entries)} entries, {len(dead)} provably dead")
|
|
372
|
+
for entry in dead:
|
|
373
|
+
click.echo(f" − {', '.join(str(m) for m in entry.get('match', []))}")
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
click.echo(reap_dead_projects().summary())
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""IDE-keyed permission seeding (GH-1261).
|
|
2
|
+
|
|
3
|
+
The mirror of :mod:`dev10x.domain.common.tracker_choice`, for the same
|
|
4
|
+
reason it exists: an IDE MCP server is a property of *some* setups, so
|
|
5
|
+
seeding its rules unconditionally hands everyone else a pile of inert
|
|
6
|
+
allows — the failure GH-768 fixed for trackers.
|
|
7
|
+
|
|
8
|
+
``none`` is the resolved default rather than a named IDE, because unlike
|
|
9
|
+
a tracker (every project has one) most checkouts have no IDE server at
|
|
10
|
+
all. A user who pins nothing folds nothing.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from enum import StrEnum
|
|
17
|
+
|
|
18
|
+
from dev10x.domain.common.tracker_choice import ALLOW_KEY, DENY_KEY
|
|
19
|
+
|
|
20
|
+
IDE_ALLOW_KEY = "ide_permissions"
|
|
21
|
+
IDE_DENY_KEY = "ide_denies"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Ide(StrEnum):
|
|
25
|
+
"""IDE MCP servers with a first-class permission block (v1 scope).
|
|
26
|
+
|
|
27
|
+
VS Code and the JetBrains siblings beyond PyCharm are absent
|
|
28
|
+
deliberately: their tool surfaces have not been enumerated, and an
|
|
29
|
+
empty block would look supported.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
PYCHARM = "pycharm"
|
|
33
|
+
NONE = "none"
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def default(cls) -> Ide:
|
|
37
|
+
return cls.NONE
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class IdeRules:
|
|
42
|
+
"""The allow/deny rules one IDE contributes to the baseline."""
|
|
43
|
+
|
|
44
|
+
allow: tuple[str, ...] = ()
|
|
45
|
+
deny: tuple[str, ...] = ()
|
|
46
|
+
|
|
47
|
+
def __bool__(self) -> bool:
|
|
48
|
+
return bool(self.allow or self.deny)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def parse_ide(value: object) -> Ide | None:
|
|
52
|
+
"""Coerce a config value to an :class:`Ide`, or ``None``.
|
|
53
|
+
|
|
54
|
+
``None`` means "not configured". An unrecognised name also returns
|
|
55
|
+
``None`` so a typo degrades to the caller's default rather than
|
|
56
|
+
raising inside a seeding run.
|
|
57
|
+
"""
|
|
58
|
+
if not isinstance(value, str):
|
|
59
|
+
return None
|
|
60
|
+
try:
|
|
61
|
+
return Ide(value.strip().lower())
|
|
62
|
+
except ValueError:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _rules_for(*, config: dict, ide: Ide) -> IdeRules:
|
|
67
|
+
def entries(key: str) -> tuple[str, ...]:
|
|
68
|
+
block = config.get(key)
|
|
69
|
+
if not isinstance(block, dict):
|
|
70
|
+
return ()
|
|
71
|
+
rules = block.get(ide.value)
|
|
72
|
+
if not isinstance(rules, list):
|
|
73
|
+
return ()
|
|
74
|
+
return tuple(rule for rule in rules if isinstance(rule, str))
|
|
75
|
+
|
|
76
|
+
return IdeRules(allow=entries(IDE_ALLOW_KEY), deny=entries(IDE_DENY_KEY))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def ide_inventory(*, config: dict) -> dict[Ide, IdeRules]:
|
|
80
|
+
"""Every IDE block the catalog defines, keyed by IDE."""
|
|
81
|
+
return {ide: _rules_for(config=config, ide=ide) for ide in Ide}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def apply_ide_selection(*, config: dict, ide: Ide) -> dict:
|
|
85
|
+
"""Return ``config`` with only ``ide``'s rules folded into the flat lists.
|
|
86
|
+
|
|
87
|
+
The input is not mutated: seeding runs read the shipped catalog from
|
|
88
|
+
a cached loader, so mutating it would leak the first run's IDE into
|
|
89
|
+
every later one in the same process.
|
|
90
|
+
|
|
91
|
+
Idempotent — a rule already in the flat list is not duplicated.
|
|
92
|
+
"""
|
|
93
|
+
rules = _rules_for(config=config, ide=ide)
|
|
94
|
+
merged = dict(config)
|
|
95
|
+
for key, additions in ((ALLOW_KEY, rules.allow), (DENY_KEY, rules.deny)):
|
|
96
|
+
existing = [rule for rule in merged.get(key, []) if isinstance(rule, str)]
|
|
97
|
+
merged[key] = existing + [rule for rule in additions if rule not in existing]
|
|
98
|
+
return merged
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
__all__ = [
|
|
102
|
+
"IDE_ALLOW_KEY",
|
|
103
|
+
"IDE_DENY_KEY",
|
|
104
|
+
"Ide",
|
|
105
|
+
"IdeRules",
|
|
106
|
+
"apply_ide_selection",
|
|
107
|
+
"ide_inventory",
|
|
108
|
+
"parse_ide",
|
|
109
|
+
]
|