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.
Files changed (320) hide show
  1. {dev10x-0.97.0/src/Dev10x.egg-info → dev10x-0.99.0}/PKG-INFO +1 -1
  2. {dev10x-0.97.0 → dev10x-0.99.0}/pyproject.toml +1 -1
  3. {dev10x-0.97.0 → dev10x-0.99.0/src/Dev10x.egg-info}/PKG-INFO +1 -1
  4. {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/SOURCES.txt +13 -1
  5. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/github_app.py +87 -2
  6. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/permission.py +41 -0
  7. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/session.py +38 -0
  8. dev10x-0.99.0/src/dev10x/domain/common/ide_choice.py +109 -0
  9. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/config_migration.py +177 -18
  10. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/dev10x_paths.py +80 -1
  11. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/session_yaml.py +107 -0
  12. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/gate_policy.py +55 -7
  13. dev10x-0.99.0/src/dev10x/domain/pr_body.py +220 -0
  14. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/sensitivity.py +121 -1
  15. dev10x-0.99.0/src/dev10x/domain/transport_budget.py +118 -0
  16. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/__init__.py +204 -6
  17. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/app_auth.py +2 -0
  18. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session.py +2 -0
  19. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session_dispatch.py +41 -0
  20. dev10x-0.99.0/src/dev10x/hooks/stop_verdict.py +354 -0
  21. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/gate_tools.py +68 -0
  22. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/github_tools.py +28 -2
  23. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/misc_tools.py +15 -5
  24. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/monitor_tools.py +7 -4
  25. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/monitor/__init__.py +23 -2
  26. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/runner/__init__.py +82 -12
  27. dev10x-0.99.0/src/dev10x/session/ide_pin.py +79 -0
  28. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/service.py +24 -4
  29. dev10x-0.99.0/src/dev10x/skills/common/jtbd.py +130 -0
  30. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/registry.py +2 -0
  31. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/ask_shadows_allow.py +66 -10
  32. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/mcp_vs_script_drift.py +43 -2
  33. dev10x-0.99.0/src/dev10x/skills/doctor/strategies/retired_session_yaml.py +180 -0
  34. dev10x-0.99.0/src/dev10x/skills/doctor/strategies/shell_equivalent_mcp_tools.py +143 -0
  35. dev10x-0.99.0/src/dev10x/skills/merge/__init__.py +9 -0
  36. dev10x-0.99.0/src/dev10x/skills/merge/fixes_scope.py +281 -0
  37. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/monitor/ci_check_status.py +11 -9
  38. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/gchat_review_request.py +143 -2
  39. dev10x-0.99.0/src/dev10x/skills/permission/catalog_merge.py +431 -0
  40. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/enumerate_mcp.py +1 -0
  41. dev10x-0.99.0/src/dev10x/skills/permission/ide_resolve.py +53 -0
  42. dev10x-0.99.0/src/dev10x/skills/permission/ignore_session_state.py +175 -0
  43. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/update_paths.py +66 -0
  44. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/subprocess_utils.py +68 -1
  45. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/__init__.py +6 -0
  46. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/command-skill-map.yaml +77 -18
  47. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/sensitivity_target.py +24 -3
  48. dev10x-0.99.0/src/dev10x/validators/write_destination.py +227 -0
  49. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_subprocess_utils.py +89 -0
  50. dev10x-0.99.0/tests/test_use_cwd_absolute.py +140 -0
  51. dev10x-0.97.0/src/dev10x/domain/pr_body.py +0 -125
  52. dev10x-0.97.0/src/dev10x/skills/common/jtbd.py +0 -44
  53. dev10x-0.97.0/src/dev10x/skills/permission/catalog_merge.py +0 -201
  54. {dev10x-0.97.0 → dev10x-0.99.0}/LICENSE +0 -0
  55. {dev10x-0.97.0 → dev10x-0.99.0}/README.md +0 -0
  56. {dev10x-0.97.0 → dev10x-0.99.0}/setup.cfg +0 -0
  57. {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/dependency_links.txt +0 -0
  58. {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/entry_points.txt +0 -0
  59. {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/requires.txt +0 -0
  60. {dev10x-0.97.0 → dev10x-0.99.0}/src/Dev10x.egg-info/top_level.txt +0 -0
  61. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/__init__.py +0 -0
  62. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/__init__.py +0 -0
  63. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/analyze.py +0 -0
  64. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/log_pruner.py +0 -0
  65. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/log_reader.py +0 -0
  66. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/permissions_model.py +0 -0
  67. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/summarizer.py +0 -0
  68. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/audit/transcript_grammar.py +0 -0
  69. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/cli.py +0 -0
  70. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/__init__.py +0 -0
  71. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/config.py +0 -0
  72. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/deps.py +0 -0
  73. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/foreman.py +0 -0
  74. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/github.py +0 -0
  75. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/github_app_api.py +0 -0
  76. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/hook.py +0 -0
  77. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/init.py +0 -0
  78. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/platform.py +0 -0
  79. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/playbook.py +0 -0
  80. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/skill.py +0 -0
  81. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/spec.py +0 -0
  82. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/usage.py +0 -0
  83. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/validate.py +0 -0
  84. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/commands/watchdog.py +0 -0
  85. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/config/__init__.py +0 -0
  86. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/config/friction_presets.py +0 -0
  87. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/config/loader.py +0 -0
  88. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/db/__init__.py +0 -0
  89. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/dependency_pins.py +0 -0
  90. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/dependency_sweep.py +0 -0
  91. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/__init__.py +0 -0
  92. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/audit_writer.py +0 -0
  93. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/claude_paths.py +0 -0
  94. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/__init__.py +0 -0
  95. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/accepted_findings.py +0 -0
  96. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/allow_rule.py +0 -0
  97. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/baseline_catalog.py +0 -0
  98. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/bash_tokens.py +0 -0
  99. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/branch_name.py +0 -0
  100. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/commit_subject.py +0 -0
  101. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/config_io.py +0 -0
  102. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/mcp_tool_name.py +0 -0
  103. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/mktmp_path.py +0 -0
  104. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/plugin_loader.py +0 -0
  105. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/plugin_version.py +0 -0
  106. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/policy.py +0 -0
  107. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/policy_migration.py +0 -0
  108. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/policy_resolution.py +0 -0
  109. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/repository_ref.py +0 -0
  110. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/result.py +0 -0
  111. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/rule_id.py +0 -0
  112. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/singleton_holder.py +0 -0
  113. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/skill_name.py +0 -0
  114. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/ticket_id.py +0 -0
  115. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/tool_signature.py +0 -0
  116. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/tracker_choice.py +0 -0
  117. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/common/workspace.py +0 -0
  118. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/config_loader.py +0 -0
  119. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/cwd_resolver.py +0 -0
  120. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/__init__.py +0 -0
  121. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/config_document.py +0 -0
  122. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/plan.py +0 -0
  123. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/session_context.py +0 -0
  124. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/session_state.py +0 -0
  125. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/settings_document.py +0 -0
  126. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/documents/task.py +0 -0
  127. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/events/__init__.py +0 -0
  128. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/events/hook_event.py +0 -0
  129. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/events/hook_input.py +0 -0
  130. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/file_locks.py +0 -0
  131. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/friction_level.py +0 -0
  132. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/git_context.py +0 -0
  133. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/gitmoji.py +0 -0
  134. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/hook_telemetry.py +0 -0
  135. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/install_version.py +0 -0
  136. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/plugin_identity.py +0 -0
  137. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/plugin_origin.py +0 -0
  138. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/plugin_root.py +0 -0
  139. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/profile_tier.py +0 -0
  140. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/__init__.py +0 -0
  141. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/policy_rule.py +0 -0
  142. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/rule_engine.py +0 -0
  143. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/sql.py +0 -0
  144. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/rules/validation_rule.py +0 -0
  145. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/session_document.py +0 -0
  146. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/session_rules.py +0 -0
  147. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/session_staleness.py +0 -0
  148. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/usage/__init__.py +0 -0
  149. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/usage/pricing.py +0 -0
  150. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/usage/reader.py +0 -0
  151. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/domain/watchdog.py +0 -0
  152. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/git/__init__.py +0 -0
  153. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/candidate_rules.py +0 -0
  154. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/learn_loop.py +0 -0
  155. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/pattern_validation.py +0 -0
  156. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/review_patterns.py +0 -0
  157. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/rule_authoring.py +0 -0
  158. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/github/rule_confidence.py +0 -0
  159. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/__init__.py +0 -0
  160. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/audit_emit.py +0 -0
  161. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/base.py +0 -0
  162. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/edit_validator.py +0 -0
  163. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/format_scope.py +0 -0
  164. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/hook_transport.py +0 -0
  165. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/permission_diagnostics.py +0 -0
  166. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session_place.py +0 -0
  167. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/session_policy.py +0 -0
  168. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/skill.py +0 -0
  169. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/task_guard.py +0 -0
  170. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/hooks/task_plan_sync.py +0 -0
  171. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/__init__.py +0 -0
  172. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/_app.py +0 -0
  173. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/audit_tools.py +0 -0
  174. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/daemon.py +0 -0
  175. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/gate_query.py +0 -0
  176. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/git_tools.py +0 -0
  177. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/knowledge_prompts.py +0 -0
  178. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/knowledge_resources.py +0 -0
  179. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/plan_tools.py +0 -0
  180. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/release_tools.py +0 -0
  181. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/resource_watcher.py +0 -0
  182. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/roots_manager.py +0 -0
  183. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/roots_tools.py +0 -0
  184. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/sampling_manager.py +0 -0
  185. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/sampling_tools.py +0 -0
  186. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/server_cli.py +0 -0
  187. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/server_db.py +0 -0
  188. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/session_store.py +0 -0
  189. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/strict_args.py +0 -0
  190. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/task_index_tools.py +0 -0
  191. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/transport.py +0 -0
  192. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/usage_tools.py +0 -0
  193. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/mcp/wiring.py +0 -0
  194. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/permission/__init__.py +0 -0
  195. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/permission/service.py +0 -0
  196. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/plan/__init__.py +0 -0
  197. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/plan/service.py +0 -0
  198. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/platform/__init__.py +0 -0
  199. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/platform/registry.py +0 -0
  200. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/release/__init__.py +0 -0
  201. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/__init__.py +0 -0
  202. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/_matcher.py +0 -0
  203. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/index_parser.py +0 -0
  204. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/norms_renderer.py +0 -0
  205. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/scope/safeguards_renderer.py +0 -0
  206. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/__init__.py +0 -0
  207. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/preset_pin.py +0 -0
  208. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/supervisor_review_pin.py +0 -0
  209. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/task_index.py +0 -0
  210. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/session/tracker_pin.py +0 -0
  211. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/__init__.py +0 -0
  212. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/builder.py +0 -0
  213. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/catalog.py +0 -0
  214. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skill_index/resolver.py +0 -0
  215. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/__init__.py +0 -0
  216. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/__init__.py +0 -0
  217. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/analyze_actions.py +0 -0
  218. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/analyze_permissions.py +0 -0
  219. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/cli_friction.py +0 -0
  220. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/eval_gaps.py +0 -0
  221. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/extract_session.py +0 -0
  222. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/instruction_budget.py +0 -0
  223. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/audit/privacy.py +0 -0
  224. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/common/__init__.py +0 -0
  225. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/database/__init__.py +0 -0
  226. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/__init__.py +0 -0
  227. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/__init__.py +0 -0
  228. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/forbidden_token_priming.py +0 -0
  229. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/home_in_additional_directories.py +0 -0
  230. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/mcp_horizontal_duplicates.py +0 -0
  231. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategies/missing_linear_mcp_allow.py +0 -0
  232. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/doctor/strategy.py +0 -0
  233. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/evidence/__init__.py +0 -0
  234. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/foreman/__init__.py +0 -0
  235. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/foreman/watch.py +0 -0
  236. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/git_fixup/__init__.py +0 -0
  237. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/git_fixup/find_fixup_target.py +0 -0
  238. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/monitor/__init__.py +0 -0
  239. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/monitor/pr_notify.py +0 -0
  240. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/__init__.py +0 -0
  241. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/_gh.py +0 -0
  242. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/gchat_cards.py +0 -0
  243. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/gchat_notify.py +0 -0
  244. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/slack_notify.py +0 -0
  245. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/notifications/slack_review_request.py +0 -0
  246. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/orchestration/__init__.py +0 -0
  247. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/orchestration/batch_detection.py +0 -0
  248. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/orchestration/subagent_protocol.py +0 -0
  249. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/__init__.py +0 -0
  250. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/accepted_findings.py +0 -0
  251. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/backup.py +0 -0
  252. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/baseline-permissions.yaml +0 -0
  253. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/catalog_gap.py +0 -0
  254. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/catalog_paths.py +0 -0
  255. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/clean_project_files.py +0 -0
  256. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/cli_catalog.py +0 -0
  257. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/config.py +0 -0
  258. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/doctor.py +0 -0
  259. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/file_lock.py +0 -0
  260. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/generalize.py +0 -0
  261. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/manifest.py +0 -0
  262. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/merge_worktree_permissions.py +0 -0
  263. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/migrate_config.py +0 -0
  264. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_audit.py +0 -0
  265. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_authoring.py +0 -0
  266. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_catalog_migration.py +0 -0
  267. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/policy_renderer.py +0 -0
  268. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/promote.py +0 -0
  269. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/provenance.py +0 -0
  270. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/resolve.py +0 -0
  271. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission/tracker_resolve.py +0 -0
  272. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/__init__.py +0 -0
  273. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/fixtures.py +0 -0
  274. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/matrix.py +0 -0
  275. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/policy_report.py +0 -0
  276. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/report.py +0 -0
  277. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/permission_investigator/runner.py +0 -0
  278. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/__init__.py +0 -0
  279. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/compare.py +0 -0
  280. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/discovery.py +0 -0
  281. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/playbook/report.py +0 -0
  282. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/release/__init__.py +0 -0
  283. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/release/classifier.py +0 -0
  284. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/skills/release/collect_prs.py +0 -0
  285. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/spec/__init__.py +0 -0
  286. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/spec/drift_detector.py +0 -0
  287. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/utilities/__init__.py +0 -0
  288. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/utilities/slack.py +0 -0
  289. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/_quote_strip.py +0 -0
  290. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/base.py +0 -0
  291. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/bash_aggregation.py +0 -0
  292. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/command_substitution.py +0 -0
  293. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/commit_jtbd.py +0 -0
  294. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/execution_safety.py +0 -0
  295. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/inline_linter.py +0 -0
  296. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/mcp_prefix.py +0 -0
  297. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/pipeline_allow.py +0 -0
  298. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/pr_base.py +0 -0
  299. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/prefix_friction.py +0 -0
  300. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/redundant_fetch.py +0 -0
  301. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/registry.py +0 -0
  302. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/safe_expansion.py +0 -0
  303. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/safe_subshell.py +0 -0
  304. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/sensitivity_exceptions.py +0 -0
  305. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/skill_redirect.py +0 -0
  306. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/spec_drift.py +0 -0
  307. {dev10x-0.97.0 → dev10x-0.99.0}/src/dev10x/validators/sql_safety.py +0 -0
  308. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cli.py +0 -0
  309. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cli_lazy.py +0 -0
  310. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cwd_discipline_sweep.py +0 -0
  311. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_cwd_enforcement.py +0 -0
  312. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_dependency_pins.py +0 -0
  313. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_dependency_sweep.py +0 -0
  314. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_integration.py +0 -0
  315. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_no_home_claude_literals.py +0 -0
  316. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_no_module_scope_gitcontext.py +0 -0
  317. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_regenerable_caches_untracked.py +0 -0
  318. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_retired_config_paths.py +0 -0
  319. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_script_loadability.py +0 -0
  320. {dev10x-0.97.0 → dev10x-0.99.0}/tests/test_shell_script_flags.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Dev10x
3
- Version: 0.97.0
3
+ Version: 0.99.0
4
4
  Summary: Claude Code plugin providing reusable skills, hooks, and commands
5
5
  Author: Janusz Skonieczny
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Dev10x"
3
- version = "0.97.0"
3
+ version = "0.99.0"
4
4
  description = "Claude Code plugin providing reusable skills, hooks, and commands"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Dev10x
3
- Version: 0.97.0
3
+ Version: 0.99.0
4
4
  Summary: Claude Code plugin providing reusable skills, hooks, and commands
5
5
  Author: Janusz Skonieczny
6
6
  License: MIT License
@@ -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 ``~/.claude/Dev10x/github-bot/``.
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 current GitHub App config status."""
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
+ ]