dotscope 1.7.4__tar.gz → 1.7.7__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 (163) hide show
  1. {dotscope-1.7.4 → dotscope-1.7.7}/PKG-INFO +2 -2
  2. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/__init__.py +1 -1
  3. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/cli/__init__.py +4 -3
  4. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/cli/ingest.py +1 -1
  5. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/search.py +1 -1
  6. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/__init__.py +28 -34
  7. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/core.py +2 -2
  8. dotscope-1.7.7/dotscope/mcp/hooks.py +38 -0
  9. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/help.py +65 -7
  10. {dotscope-1.7.4 → dotscope-1.7.7}/pyproject.toml +2 -2
  11. dotscope-1.7.4/dotscope/mcp/hooks.py +0 -8
  12. {dotscope-1.7.4 → dotscope-1.7.7}/LICENSE +0 -0
  13. {dotscope-1.7.4 → dotscope-1.7.7}/README.md +0 -0
  14. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/Cargo.lock +0 -0
  15. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/Cargo.toml +0 -0
  16. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/build.rs +0 -0
  17. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/ast.rs +0 -0
  18. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/bin/daemon.rs +0 -0
  19. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/daemon.rs +0 -0
  20. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/git.rs +0 -0
  21. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/graph.rs +0 -0
  22. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/ipc.rs +0 -0
  23. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/lib.rs +0 -0
  24. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/mmap.rs +0 -0
  25. {dotscope-1.7.4 → dotscope-1.7.7}/crates/dotscope-core/src/walk.rs +0 -0
  26. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/assets/ui/bundle.js +0 -0
  27. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/assets/ui/index.html +0 -0
  28. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/cli/core.py +0 -0
  29. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/cli/hooks.py +0 -0
  30. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/cli/observability.py +0 -0
  31. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/cli/serve.py +0 -0
  32. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/__init__.py +0 -0
  33. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/assertions.py +0 -0
  34. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/composer.py +0 -0
  35. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/constants.py +0 -0
  36. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/context.py +0 -0
  37. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/discovery.py +0 -0
  38. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/ignore.py +0 -0
  39. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/matcher.py +0 -0
  40. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/parser.py +0 -0
  41. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/resolver.py +0 -0
  42. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/runtime_overlay.py +0 -0
  43. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/scanner.py +0 -0
  44. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/tokens.py +0 -0
  45. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/engine/utility.py +0 -0
  46. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/eval/__init__.py +0 -0
  47. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/eval/bootstrap.py +0 -0
  48. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/eval/compare.py +0 -0
  49. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/eval/corpus.py +0 -0
  50. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/eval/harness.py +0 -0
  51. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/eval/replay.py +0 -0
  52. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/generate/__init__.py +0 -0
  53. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/generate/atlas.py +0 -0
  54. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/generate/contracts.py +0 -0
  55. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/generate/engine.py +0 -0
  56. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/generate/models.py +0 -0
  57. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/generate/network.py +0 -0
  58. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/ingest.py +0 -0
  59. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/logger.py +0 -0
  60. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/middleware.py +0 -0
  61. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/mvcc.py +0 -0
  62. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/observability.py +0 -0
  63. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/mcp/pipelines.py +0 -0
  64. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/__init__.py +0 -0
  65. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/classifier.py +0 -0
  66. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/composer.py +0 -0
  67. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/differ.py +0 -0
  68. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/driver.py +0 -0
  69. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/imports.py +0 -0
  70. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/models.py +0 -0
  71. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/merge/swarm.py +0 -0
  72. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/__init__.py +0 -0
  73. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/core.py +0 -0
  74. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/eval.py +0 -0
  75. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/history.py +0 -0
  76. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/intent.py +0 -0
  77. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/passes.py +0 -0
  78. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/models/state.py +0 -0
  79. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/__init__.py +0 -0
  80. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/ast_analyzer.py +0 -0
  81. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/backtest.py +0 -0
  82. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/budget_allocator.py +0 -0
  83. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/convention_compliance.py +0 -0
  84. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/convention_discovery.py +0 -0
  85. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/convention_parser.py +0 -0
  86. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/graph_builder.py +0 -0
  87. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/hint_generator.py +0 -0
  88. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/history_miner.py +0 -0
  89. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/incremental.py +0 -0
  90. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/__init__.py +0 -0
  91. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/_base.py +0 -0
  92. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/_treesitter.py +0 -0
  93. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/go.py +0 -0
  94. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/java.py +0 -0
  95. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/javascript.py +0 -0
  96. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/rust.py +0 -0
  97. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lang/solidity.py +0 -0
  98. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/lazy.py +0 -0
  99. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/preflight.py +0 -0
  100. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/semantic_diff.py +0 -0
  101. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/__init__.py +0 -0
  102. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/acknowledge.py +0 -0
  103. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checker.py +0 -0
  104. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/__init__.py +0 -0
  105. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/antipattern.py +0 -0
  106. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/boundary.py +0 -0
  107. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/contracts.py +0 -0
  108. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/convention.py +0 -0
  109. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/direction.py +0 -0
  110. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/intent.py +0 -0
  111. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/network.py +0 -0
  112. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/spatial.py +0 -0
  113. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/stability.py +0 -0
  114. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/checks/voice.py +0 -0
  115. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/constraints.py +0 -0
  116. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/line_filter.py +0 -0
  117. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/sentinel/models.py +0 -0
  118. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/spatial_autofix.py +0 -0
  119. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/virtual.py +0 -0
  120. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/voice.py +0 -0
  121. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/voice_defaults.py +0 -0
  122. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/voice_discovery.py +0 -0
  123. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/passes/z3_transpiler.py +0 -0
  124. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/paths/__init__.py +0 -0
  125. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/paths/repo.py +0 -0
  126. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/__init__.py +0 -0
  127. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/chunker.py +0 -0
  128. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/expander.py +0 -0
  129. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/flattener.py +0 -0
  130. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/models.py +0 -0
  131. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/observation.py +0 -0
  132. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/reranker.py +0 -0
  133. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/retriever.py +0 -0
  134. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/search/synthesizer.py +0 -0
  135. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/__init__.py +0 -0
  136. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/cache.py +0 -0
  137. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/claude_hooks.py +0 -0
  138. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/git_hooks.py +0 -0
  139. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/incremental_state.py +0 -0
  140. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/mcp_config.py +0 -0
  141. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/near_miss.py +0 -0
  142. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/onboarding.py +0 -0
  143. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/session_manager.py +0 -0
  144. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/swarm_state.py +0 -0
  145. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/storage/timing.py +0 -0
  146. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/__init__.py +0 -0
  147. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/bench.py +0 -0
  148. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/counterfactual.py +0 -0
  149. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/debug.py +0 -0
  150. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/explain.py +0 -0
  151. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/formatter.py +0 -0
  152. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/health.py +0 -0
  153. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/progress.py +0 -0
  154. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/textio.py +0 -0
  155. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/ux/visibility.py +0 -0
  156. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/__init__.py +0 -0
  157. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/absorber.py +0 -0
  158. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/ingest.py +0 -0
  159. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/intent.py +0 -0
  160. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/lessons.py +0 -0
  161. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/refresh.py +0 -0
  162. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/regression.py +0 -0
  163. {dotscope-1.7.4 → dotscope-1.7.7}/dotscope/workflows/sync.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotscope
3
- Version: 1.7.4
3
+ Version: 1.7.7
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Topic :: Software Development :: Libraries
@@ -17,7 +17,7 @@ Requires-Dist: dotscope[mcp,tokens,search] ; extra == 'all'
17
17
  Requires-Dist: pytest ; extra == 'dev'
18
18
  Requires-Dist: ruff ; extra == 'dev'
19
19
  Requires-Dist: sentence-transformers ; extra == 'embeddings'
20
- Requires-Dist: mcp>=1.2.0,<3 ; extra == 'mcp'
20
+ Requires-Dist: mcp>=1.6.0,<3 ; extra == 'mcp'
21
21
  Requires-Dist: sentence-transformers ; extra == 'search'
22
22
  Requires-Dist: numpy ; extra == 'search'
23
23
  Requires-Dist: tiktoken ; extra == 'tokens'
@@ -1,3 +1,3 @@
1
1
  """dotscope — Directory-scoped context boundaries for AI coding agents."""
2
2
 
3
- __version__ = "1.7.4"
3
+ __version__ = "1.7.7"
@@ -86,12 +86,13 @@ def main(argv=None):
86
86
  sub.add_parser("health", help="Scope health: staleness, coverage, drift")
87
87
 
88
88
  # --- sync ---
89
- p_sync = sub.add_parser("sync", help="Structurally synchronize stale .scope boundary configurations against the real-time AST engine")
90
- p_sync.add_argument("scopes", nargs="*", help="Specific scope boundaries to sync (omit for entire repo)")
89
+ p_sync = sub.add_parser("sync", help="Re-align .scope boundaries against the current import graph")
90
+ p_sync.add_argument("scopes", nargs="*", help="Specific scopes to sync (omit for entire repo)")
91
91
 
92
92
  # --- ingest ---
93
93
  p_ingest = sub.add_parser("ingest", help="Reverse-engineer .scope files from an existing codebase")
94
- p_ingest.add_argument("--dir", default=".", help="Repository root to ingest")
94
+ p_ingest.add_argument("directory", nargs="?", default=None, help="Repository root to ingest (default: current directory)")
95
+ p_ingest.add_argument("--dir", default=None, help="Repository root to ingest (alias for positional arg)")
95
96
  p_ingest.add_argument("--no-history", action="store_true", help="Skip git history mining")
96
97
  p_ingest.add_argument("--no-docs", action="store_true", help="Skip doc absorption")
97
98
  p_ingest.add_argument("--dry-run", action="store_true", help="Plan only, don't write files")
@@ -5,7 +5,7 @@ import sys
5
5
  def _cmd_ingest(args):
6
6
  from ..workflows.ingest import ingest, format_ingest_report
7
7
 
8
- root = os.path.abspath(args.dir)
8
+ root = os.path.abspath(args.directory or args.dir or ".")
9
9
  plan = ingest(
10
10
  root,
11
11
  mine_history=not args.no_history,
@@ -21,7 +21,7 @@ def execute_semantic_search(root: str, query: str) -> str:
21
21
  query
22
22
  ]
23
23
  # Ignore case by default to maximize recall for the agent
24
- cmd.insert(2, "-i")
24
+ cmd.insert(3, "-i")
25
25
 
26
26
  result = subprocess.run(cmd, cwd=root, capture_output=True, text=True, timeout=15)
27
27
  # 1 means no match, 0 means match. >1 is error
@@ -59,17 +59,17 @@ def main():
59
59
  import contextlib
60
60
 
61
61
  # Redirect stray stdout writes (from libraries, print() calls, etc.)
62
- # to stderr so that only clean JSON-RPC flows over the stdio transport.
62
+ # to stderr so only clean JSON-RPC flows over the stdio transport.
63
63
  #
64
64
  # How it works:
65
65
  # 1. Save the real stdout fd (the MCP pipe to the client).
66
66
  # 2. Point fd 1 and sys.stdout at stderr (captures stray output).
67
- # 3. Build an isolated file object from the saved fd.
68
- # 4. Patch the reference that FastMCP.run_stdio_async actually uses
69
- # the local name imported via `from mcp.server.stdio import
70
- # stdio_server` inside mcp/server/fastmcp/server.py. Patching
71
- # `mcp.server.stdio.stdio_server` alone has no effect because
72
- # the `from … import` already bound a direct reference.
67
+ # 3. Build an async file object from the saved fd.
68
+ # 4. Override FastMCP.run_stdio_async to pass the saved stdout
69
+ # into stdio_server(stdout=...) its public API.
70
+ #
71
+ # This avoids monkeypatching internal module references, which break
72
+ # when FastMCP caches them via `from … import` at load time.
73
73
  _isolated_stdout = None
74
74
  try:
75
75
  virgin_stdout_fd = os.dup(1)
@@ -97,35 +97,29 @@ def main():
97
97
  known, _remaining = parser.parse_known_args()
98
98
  _cli_root = known.root
99
99
 
100
- # Patch the stdio_server reference that FastMCP.run_stdio_async actually
101
- # calls. The module does `from mcp.server.stdio import stdio_server` so
102
- # we must overwrite that *local* name, not the module-level attribute.
100
+ mcp = FastMCP("dotscope")
101
+
102
+ # Override run_stdio_async to pass the isolated stdout directly into
103
+ # stdio_server's public `stdout=` parameter. This is stable across
104
+ # mcp versions — no module-level monkeypatching required.
103
105
  if _isolated_stdout is not None:
104
- try:
105
- import mcp.server.stdio as _mcp_stdio
106
- import mcp.server.fastmcp.server as _fastmcp_mod
107
- import anyio
108
- from io import TextIOWrapper
109
-
110
- _original_stdio_server = _mcp_stdio.stdio_server
111
-
112
- @contextlib.asynccontextmanager
113
- async def _patched_stdio_server(stdin=None, stdout=None):
114
- if stdout is None:
115
- stdout = anyio.wrap_file(
116
- TextIOWrapper(_isolated_stdout, encoding="utf-8")
117
- )
118
- async with _original_stdio_server(stdin=stdin, stdout=stdout) as streams:
119
- yield streams
120
-
121
- # Patch both: the module attr (for anyone else importing it) and
122
- # the local name inside fastmcp.server (the one that matters).
123
- _mcp_stdio.stdio_server = _patched_stdio_server
124
- _fastmcp_mod.stdio_server = _patched_stdio_server
125
- except Exception as e:
126
- print(f"dotscope warning: stdio patch failed: {e}", file=sys.stderr)
106
+ from mcp.server.stdio import stdio_server
107
+ import anyio
108
+ from io import TextIOWrapper
127
109
 
128
- mcp = FastMCP("dotscope")
110
+ _mcp_stdout = anyio.wrap_file(
111
+ TextIOWrapper(_isolated_stdout, encoding="utf-8")
112
+ )
113
+
114
+ async def _run_stdio_with_isolated_stdout(self_ref=mcp):
115
+ async with stdio_server(stdout=_mcp_stdout) as (read_stream, write_stream):
116
+ await self_ref._mcp_server.run(
117
+ read_stream,
118
+ write_stream,
119
+ self_ref._mcp_server.create_initialization_options(),
120
+ )
121
+
122
+ mcp.run_stdio_async = _run_stdio_with_isolated_stdout
129
123
 
130
124
  # Session-level tracker (lives across tool calls in a single MCP session)
131
125
  from ..ux.visibility import SessionTracker
@@ -110,7 +110,7 @@ def register_core_tools(mcp, **kwargs):
110
110
 
111
111
  index = load_resolution_index(root)
112
112
  scopes = []
113
- if index:
113
+ if index and index.scopes:
114
114
  for name, entry in index.scopes.items():
115
115
  scopes.append((name, entry.keywords, entry.description or ""))
116
116
  else:
@@ -167,7 +167,7 @@ def register_core_tools(mcp, **kwargs):
167
167
  scopes = []
168
168
  index = load_resolution_index(root)
169
169
 
170
- if index:
170
+ if index and index.scopes:
171
171
  for name, entry in index.scopes.items():
172
172
  scopes.append({
173
173
  "name": name,
@@ -0,0 +1,38 @@
1
+ def register_hooks_tools(mcp, **kwargs):
2
+ tracker = kwargs.get('tracker')
3
+ client_id = kwargs.get('client_id')
4
+ _root = kwargs.get('_root')
5
+ _repo_tokens = kwargs.get('_repo_tokens')
6
+ _cached_history = kwargs.get('_cached_history')
7
+ _cached_graph_hubs = kwargs.get('_cached_graph_hubs')
8
+ _cli_root = kwargs.get('_cli_root')
9
+
10
+ @mcp.tool()
11
+ def dotscope_sync(
12
+ scopes: list[str] | None = None,
13
+ root: str | None = None,
14
+ ) -> str:
15
+ """Synchronize .scope file boundaries against the current AST topology.
16
+
17
+ Re-scans the dependency graph and updates each .scope file's includes
18
+ and excludes to match the real imports. Preserves any lines marked
19
+ with ``# keep`` or ``# manual``. Non-destructive: context, description,
20
+ keywords, and related fields are never touched.
21
+
22
+ Args:
23
+ scopes: Specific scope names to sync (omit for entire repo).
24
+ root: Repository root path (auto-detected if omitted).
25
+ """
26
+ import json
27
+ from ..paths.repo import find_repo_root
28
+ from ..workflows.sync import sync_scopes
29
+
30
+ effective_root = root or _root or find_repo_root(_cli_root)
31
+ if effective_root is None:
32
+ return json.dumps({"error": "Could not find repository root"})
33
+
34
+ count = sync_scopes(effective_root, scopes)
35
+ return json.dumps({
36
+ "scopes_modified": count,
37
+ "message": f"Synchronized {count} scope(s)." if count else "All scopes already in sync.",
38
+ })
@@ -8,17 +8,25 @@ HELP_ROOT = """\
8
8
  Usage: dotscope <command>
9
9
 
10
10
  init One command: ingest, hooks, MCP config
11
- resolve <scope> Serve context to an agent
11
+ resolve <scope> Serve files + context to an agent
12
+ context <scope> Architectural context only (no files)
13
+ match <task> Find matching scope for a task description
14
+ list List all scopes
12
15
  check Verify routing (guards block, nudges guide)
13
- conventions View and manage conventions
14
- voice View and manage code style
15
16
  health Scope staleness and drift
16
- refresh Runtime refresh queue and worker
17
+ validate Check .scope files for broken paths
18
+
19
+ ingest . Reverse-engineer .scope files from codebase
20
+ sync Re-align .scope boundaries against import graph
21
+ refresh Reload runtime cache from disk
17
22
 
18
- ingest . Re-ingest (full scan)
23
+ conventions View and manage conventions
24
+ voice View and manage code style
19
25
  intent Declare architectural direction
20
- diff --staged Semantic diff
21
- hook install Re-install hooks
26
+ diff --staged Semantic diff against conventions
27
+ impact <file> Predict blast radius of changes
28
+
29
+ hook install Install git hooks
22
30
  bench Performance metrics
23
31
  test-compiler Regression suite
24
32
  debug --last Diagnose a bad session
@@ -188,10 +196,60 @@ Usage: dotscope refresh <action> [options]
188
196
  Automatic refresh writes only to .dotscope/. Tracked .scope files stay stable
189
197
  until you run dotscope ingest."""
190
198
 
199
+ HELP_SYNC = """\
200
+ Usage: dotscope sync [scopes...]
201
+
202
+ dotscope sync Re-align all .scope boundaries
203
+ dotscope sync auth payments Sync specific scopes only
204
+
205
+ Scans the AST dependency graph and updates each .scope file's includes
206
+ and excludes to match real imports. Lines marked # keep or # manual are
207
+ preserved. Context, description, and keywords are never touched."""
208
+
209
+ HELP_CONTEXT = """\
210
+ Usage: dotscope context <scope> [options]
211
+
212
+ dotscope context auth Full architectural context
213
+ dotscope context auth --section gotchas Specific section only
214
+
215
+ Options:
216
+ --section <name> Filter to a named section (invariants, gotchas, etc.)"""
217
+
218
+ HELP_MATCH = """\
219
+ Usage: dotscope match <task>
220
+
221
+ dotscope match "fix the auth token refresh bug"
222
+ dotscope match "add retry logic to API client"
223
+
224
+ Returns ranked scopes with confidence scores based on keyword overlap."""
225
+
226
+ HELP_VALIDATE = """\
227
+ Usage: dotscope validate
228
+
229
+ dotscope validate Check all .scope files
230
+
231
+ Checks:
232
+ - Include paths exist on disk
233
+ - Related scope files exist
234
+ - Description is not empty
235
+ - Context field is present"""
236
+
237
+ HELP_IMPACT = """\
238
+ Usage: dotscope impact <file>
239
+
240
+ dotscope impact auth/tokens.py Show blast radius
241
+
242
+ Returns direct dependents, two-hop dependents, and co-change companions."""
243
+
191
244
  HELP_COMMANDS = {
192
245
  "ingest": HELP_INGEST,
193
246
  "resolve": HELP_RESOLVE,
247
+ "context": HELP_CONTEXT,
248
+ "match": HELP_MATCH,
194
249
  "check": HELP_CHECK,
250
+ "sync": HELP_SYNC,
251
+ "validate": HELP_VALIDATE,
252
+ "impact": HELP_IMPACT,
195
253
  "intent": HELP_INTENT,
196
254
  "conventions": HELP_CONVENTIONS,
197
255
  "diff": HELP_DIFF,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dotscope"
3
- version = "1.7.4"
3
+ version = "1.7.7"
4
4
  description = "Agents see files. You see architecture. dotscope gives agents the architecture."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -26,7 +26,7 @@ dependencies = [
26
26
  ]
27
27
 
28
28
  [project.optional-dependencies]
29
- mcp = ["mcp>=1.2.0,<3"]
29
+ mcp = ["mcp>=1.6.0,<3"]
30
30
  tokens = ["tiktoken"]
31
31
  embeddings = ["sentence-transformers"]
32
32
  search = ["sentence-transformers", "numpy"]
@@ -1,8 +0,0 @@
1
- def register_hooks_tools(mcp, **kwargs):
2
- tracker = kwargs.get('tracker')
3
- client_id = kwargs.get('client_id')
4
- _root = kwargs.get('_root')
5
- _repo_tokens = kwargs.get('_repo_tokens')
6
- _cached_history = kwargs.get('_cached_history')
7
- _cached_graph_hubs = kwargs.get('_cached_graph_hubs')
8
- _cli_root = kwargs.get('_cli_root')
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes