codebeacon 0.6.5__tar.gz → 0.6.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 (158) hide show
  1. {codebeacon-0.6.5 → codebeacon-0.6.7}/PKG-INFO +72 -49
  2. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.de.md +23 -0
  3. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.es.md +23 -0
  4. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.fr.md +23 -0
  5. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.ja.md +23 -0
  6. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.ko.md +23 -0
  7. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.md +23 -0
  8. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.pt-BR.md +23 -0
  9. {codebeacon-0.6.5 → codebeacon-0.6.7}/README.zh-CN.md +23 -0
  10. codebeacon-0.6.7/codebeacon/__init__.py +1 -0
  11. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/affected.py +6 -3
  12. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/cache.py +63 -5
  13. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/discover/ignore.py +91 -9
  14. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/discover/scanner.py +9 -10
  15. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/mcp.py +10 -3
  16. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/base.py +33 -1
  17. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/components.py +3 -0
  18. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/dependencies.py +3 -0
  19. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/entities.py +3 -0
  20. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/express.scm +7 -2
  21. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/vue.scm +5 -1
  22. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/routes.py +3 -0
  23. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/services.py +3 -0
  24. codebeacon-0.6.7/pyproject.toml +127 -0
  25. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/integration/test_full_pipeline.py +2 -3
  26. codebeacon-0.6.7/tests/test_graphify_parity_0_6_6.py +247 -0
  27. codebeacon-0.6.7/tests/test_graphify_parity_0_6_7.py +131 -0
  28. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_known_bugs.py +15 -8
  29. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_mcp_and_semantic.py +41 -0
  30. codebeacon-0.6.5/codebeacon/__init__.py +0 -1
  31. codebeacon-0.6.5/pyproject.toml +0 -120
  32. {codebeacon-0.6.5 → codebeacon-0.6.7}/.cursorrules +0 -0
  33. {codebeacon-0.6.5 → codebeacon-0.6.7}/.github/CODEOWNERS +0 -0
  34. {codebeacon-0.6.5 → codebeacon-0.6.7}/.github/dependabot.yml +0 -0
  35. {codebeacon-0.6.5 → codebeacon-0.6.7}/.github/workflows/ci.yml +0 -0
  36. {codebeacon-0.6.5 → codebeacon-0.6.7}/.github/workflows/release.yml +0 -0
  37. {codebeacon-0.6.5 → codebeacon-0.6.7}/.gitignore +0 -0
  38. {codebeacon-0.6.5 → codebeacon-0.6.7}/AGENTS.md +0 -0
  39. {codebeacon-0.6.5 → codebeacon-0.6.7}/CLAUDE.md +0 -0
  40. {codebeacon-0.6.5 → codebeacon-0.6.7}/LICENSE +0 -0
  41. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/__main__.py +0 -0
  42. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/cli.py +0 -0
  43. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/common/__init__.py +0 -0
  44. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/common/filters.py +0 -0
  45. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/common/safety.py +0 -0
  46. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/common/symbols.py +0 -0
  47. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/common/types.py +0 -0
  48. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/config.py +0 -0
  49. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/contextmap/__init__.py +0 -0
  50. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/contextmap/generator.py +0 -0
  51. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/diagnostics.py +0 -0
  52. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/discover/__init__.py +0 -0
  53. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/discover/detector.py +0 -0
  54. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/__init__.py +0 -0
  55. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/callflow_html.py +0 -0
  56. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/hooks.py +0 -0
  57. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/merge.py +0 -0
  58. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/obsidian.py +0 -0
  59. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/export/tree_html.py +0 -0
  60. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/__init__.py +0 -0
  61. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/dotnet.py +0 -0
  62. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/README.md +0 -0
  63. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/actix.scm +0 -0
  64. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/angular.scm +0 -0
  65. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/aspnet.scm +0 -0
  66. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/django.scm +0 -0
  67. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/fastapi.scm +0 -0
  68. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/flask.scm +0 -0
  69. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/gin.scm +0 -0
  70. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/ktor.scm +0 -0
  71. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/laravel.scm +0 -0
  72. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/nestjs.scm +0 -0
  73. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/rails.scm +0 -0
  74. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/react.scm +0 -0
  75. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/spring_boot.scm +0 -0
  76. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/svelte.scm +0 -0
  77. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/tauri.scm +0 -0
  78. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/queries/vapor.scm +0 -0
  79. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/extract/semantic.py +0 -0
  80. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/graph/__init__.py +0 -0
  81. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/graph/analyze.py +0 -0
  82. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/graph/build.py +0 -0
  83. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/graph/cluster.py +0 -0
  84. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/graph/enrich.py +0 -0
  85. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/graph/write.py +0 -0
  86. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/knowledge/__init__.py +0 -0
  87. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/knowledge/generator.py +0 -0
  88. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/pipeline.py +0 -0
  89. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/plugins/__init__.py +0 -0
  90. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/plugins/githooks.py +0 -0
  91. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/plugins/skills.py +0 -0
  92. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/semantic_pipeline.py +0 -0
  93. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/skill/SKILL.md +0 -0
  94. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/wave.py +0 -0
  95. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/wiki/__init__.py +0 -0
  96. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/wiki/generator.py +0 -0
  97. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/wiki/index.py +0 -0
  98. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon/wiki/templates.py +0 -0
  99. {codebeacon-0.6.5 → codebeacon-0.6.7}/codebeacon.yaml.example +0 -0
  100. {codebeacon-0.6.5 → codebeacon-0.6.7}/docs/TRANSLATION_STATUS.md +0 -0
  101. {codebeacon-0.6.5 → codebeacon-0.6.7}/public-plan.md +0 -0
  102. {codebeacon-0.6.5 → codebeacon-0.6.7}/skill/install.py +0 -0
  103. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/__init__.py +0 -0
  104. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/conftest.py +0 -0
  105. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/actix/main.rs +0 -0
  106. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/angular/app.component.ts +0 -0
  107. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/aspnet/UserController.cs +0 -0
  108. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/django/views.py +0 -0
  109. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/express/userRouter.js +0 -0
  110. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/fastapi/main.py +0 -0
  111. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/flask/app.py +0 -0
  112. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/gin/main.go +0 -0
  113. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/integration_workspace/api-python/pyproject.toml +0 -0
  114. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/integration_workspace/api-python/src/__init__.py +0 -0
  115. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/integration_workspace/api-python/src/main.py +0 -0
  116. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/integration_workspace/api-python/src/services.py +0 -0
  117. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/integration_workspace/web/package.json +0 -0
  118. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/integration_workspace/web/src/UserPage.tsx +0 -0
  119. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/ktor/UserRoutes.kt +0 -0
  120. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/laravel/UserController.php +0 -0
  121. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/nestjs/user.controller.ts +0 -0
  122. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/rails/users_controller.rb +0 -0
  123. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/react/UserPage.tsx +0 -0
  124. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/spring_boot/UserController.java +0 -0
  125. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/sveltekit/+page.svelte +0 -0
  126. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/vapor/routes.swift +0 -0
  127. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/fixtures/vue/UserList.vue +0 -0
  128. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/integration/__init__.py +0 -0
  129. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_affected.py +0 -0
  130. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_affected_wiki.py +0 -0
  131. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_audit_bugfixes.py +0 -0
  132. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_cli_dispatch.py +0 -0
  133. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_cli_upgrade.py +0 -0
  134. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_contextmap_paths.py +0 -0
  135. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_deep_dive_grouping.py +0 -0
  136. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_dependencies.py +0 -0
  137. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_diagnostics.py +0 -0
  138. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_discover.py +0 -0
  139. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_dotnet.py +0 -0
  140. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_entities.py +0 -0
  141. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_filters.py +0 -0
  142. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_graph.py +0 -0
  143. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_graphify_parity_0_6_3.py +0 -0
  144. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_graphify_parity_fixes.py +0 -0
  145. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_independent_audit_fixes.py +0 -0
  146. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_knowledge.py +0 -0
  147. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_optional_grammars.py +0 -0
  148. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_pipeline_module.py +0 -0
  149. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_plugins.py +0 -0
  150. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_resolve.py +0 -0
  151. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_routes.py +0 -0
  152. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_safety_and_writes.py +0 -0
  153. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_scanner_sensitive.py +0 -0
  154. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_semantic.py +0 -0
  155. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_semantic_hardening.py +0 -0
  156. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_semantic_stats.py +0 -0
  157. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_services.py +0 -0
  158. {codebeacon-0.6.5 → codebeacon-0.6.7}/tests/test_wiki.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codebeacon
3
- Version: 0.6.5
3
+ Version: 0.6.7
4
4
  Summary: Source code AST analysis tool for AI context generation — unified multi-framework knowledge graph
5
5
  Project-URL: Homepage, https://github.com/codebeacon/codebeacon
6
6
  Project-URL: Repository, https://github.com/codebeacon/codebeacon
@@ -20,75 +20,75 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
20
  Requires-Python: >=3.10
21
21
  Requires-Dist: networkx>=3.0
22
22
  Requires-Dist: pyyaml>=6.0
23
- Requires-Dist: tree-sitter-javascript>=0.23
24
- Requires-Dist: tree-sitter-python>=0.23
25
- Requires-Dist: tree-sitter-typescript>=0.23
26
- Requires-Dist: tree-sitter>=0.23
23
+ Requires-Dist: tree-sitter-javascript<0.26,>=0.23
24
+ Requires-Dist: tree-sitter-python<0.26,>=0.23
25
+ Requires-Dist: tree-sitter-typescript<0.24,>=0.23
26
+ Requires-Dist: tree-sitter<0.26,>=0.23
27
27
  Provides-Extra: backend
28
- Requires-Dist: tree-sitter-c-sharp>=0.23; extra == 'backend'
29
- Requires-Dist: tree-sitter-go>=0.23; extra == 'backend'
30
- Requires-Dist: tree-sitter-java>=0.23; extra == 'backend'
31
- Requires-Dist: tree-sitter-kotlin>=0.23; extra == 'backend'
32
- Requires-Dist: tree-sitter-php>=0.23; extra == 'backend'
33
- Requires-Dist: tree-sitter-ruby>=0.23; extra == 'backend'
34
- Requires-Dist: tree-sitter-rust>=0.23; extra == 'backend'
28
+ Requires-Dist: tree-sitter-c-sharp<0.24,>=0.23; extra == 'backend'
29
+ Requires-Dist: tree-sitter-go<0.26,>=0.23; extra == 'backend'
30
+ Requires-Dist: tree-sitter-java<0.24,>=0.23; extra == 'backend'
31
+ Requires-Dist: tree-sitter-kotlin<1.2,>=0.23; extra == 'backend'
32
+ Requires-Dist: tree-sitter-php<0.25,>=0.23; extra == 'backend'
33
+ Requires-Dist: tree-sitter-ruby<0.24,>=0.23; extra == 'backend'
34
+ Requires-Dist: tree-sitter-rust<0.25,>=0.23; extra == 'backend'
35
35
  Provides-Extra: cluster
36
36
  Requires-Dist: graspologic>=1.0; extra == 'cluster'
37
37
  Provides-Extra: csharp
38
- Requires-Dist: tree-sitter-c-sharp>=0.23; extra == 'csharp'
38
+ Requires-Dist: tree-sitter-c-sharp<0.24,>=0.23; extra == 'csharp'
39
39
  Provides-Extra: dev
40
40
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
41
41
  Requires-Dist: pytest>=7.0; extra == 'dev'
42
- Requires-Dist: tree-sitter-c-sharp>=0.23; extra == 'dev'
43
- Requires-Dist: tree-sitter-go>=0.23; extra == 'dev'
44
- Requires-Dist: tree-sitter-html>=0.23; extra == 'dev'
45
- Requires-Dist: tree-sitter-java>=0.23; extra == 'dev'
46
- Requires-Dist: tree-sitter-kotlin>=0.23; extra == 'dev'
47
- Requires-Dist: tree-sitter-php>=0.23; extra == 'dev'
48
- Requires-Dist: tree-sitter-ruby>=0.23; extra == 'dev'
49
- Requires-Dist: tree-sitter-rust>=0.23; extra == 'dev'
50
- Requires-Dist: tree-sitter-svelte>=0.23; extra == 'dev'
51
- Requires-Dist: tree-sitter-swift>=0.0.1; extra == 'dev'
42
+ Requires-Dist: tree-sitter-c-sharp<0.24,>=0.23; extra == 'dev'
43
+ Requires-Dist: tree-sitter-go<0.26,>=0.23; extra == 'dev'
44
+ Requires-Dist: tree-sitter-html<0.24,>=0.23; extra == 'dev'
45
+ Requires-Dist: tree-sitter-java<0.24,>=0.23; extra == 'dev'
46
+ Requires-Dist: tree-sitter-kotlin<1.2,>=0.23; extra == 'dev'
47
+ Requires-Dist: tree-sitter-php<0.25,>=0.23; extra == 'dev'
48
+ Requires-Dist: tree-sitter-ruby<0.24,>=0.23; extra == 'dev'
49
+ Requires-Dist: tree-sitter-rust<0.25,>=0.23; extra == 'dev'
50
+ Requires-Dist: tree-sitter-svelte<1.1,>=0.23; extra == 'dev'
51
+ Requires-Dist: tree-sitter-swift<0.8,>=0.0.1; extra == 'dev'
52
52
  Provides-Extra: dotnet
53
- Requires-Dist: tree-sitter-c-sharp>=0.23; extra == 'dotnet'
53
+ Requires-Dist: tree-sitter-c-sharp<0.24,>=0.23; extra == 'dotnet'
54
54
  Provides-Extra: full
55
- Requires-Dist: tree-sitter-c-sharp>=0.23; extra == 'full'
56
- Requires-Dist: tree-sitter-go>=0.23; extra == 'full'
57
- Requires-Dist: tree-sitter-html>=0.23; extra == 'full'
58
- Requires-Dist: tree-sitter-java>=0.23; extra == 'full'
59
- Requires-Dist: tree-sitter-kotlin>=0.23; extra == 'full'
60
- Requires-Dist: tree-sitter-php>=0.23; extra == 'full'
61
- Requires-Dist: tree-sitter-ruby>=0.23; extra == 'full'
62
- Requires-Dist: tree-sitter-rust>=0.23; extra == 'full'
63
- Requires-Dist: tree-sitter-svelte>=0.23; extra == 'full'
64
- Requires-Dist: tree-sitter-swift>=0.0.1; extra == 'full'
55
+ Requires-Dist: tree-sitter-c-sharp<0.24,>=0.23; extra == 'full'
56
+ Requires-Dist: tree-sitter-go<0.26,>=0.23; extra == 'full'
57
+ Requires-Dist: tree-sitter-html<0.24,>=0.23; extra == 'full'
58
+ Requires-Dist: tree-sitter-java<0.24,>=0.23; extra == 'full'
59
+ Requires-Dist: tree-sitter-kotlin<1.2,>=0.23; extra == 'full'
60
+ Requires-Dist: tree-sitter-php<0.25,>=0.23; extra == 'full'
61
+ Requires-Dist: tree-sitter-ruby<0.24,>=0.23; extra == 'full'
62
+ Requires-Dist: tree-sitter-rust<0.25,>=0.23; extra == 'full'
63
+ Requires-Dist: tree-sitter-svelte<1.1,>=0.23; extra == 'full'
64
+ Requires-Dist: tree-sitter-swift<0.8,>=0.0.1; extra == 'full'
65
65
  Provides-Extra: go
66
- Requires-Dist: tree-sitter-go>=0.23; extra == 'go'
66
+ Requires-Dist: tree-sitter-go<0.26,>=0.23; extra == 'go'
67
67
  Provides-Extra: html
68
- Requires-Dist: tree-sitter-html>=0.23; extra == 'html'
68
+ Requires-Dist: tree-sitter-html<0.24,>=0.23; extra == 'html'
69
69
  Provides-Extra: java
70
- Requires-Dist: tree-sitter-java>=0.23; extra == 'java'
70
+ Requires-Dist: tree-sitter-java<0.24,>=0.23; extra == 'java'
71
71
  Provides-Extra: jvm
72
- Requires-Dist: tree-sitter-java>=0.23; extra == 'jvm'
73
- Requires-Dist: tree-sitter-kotlin>=0.23; extra == 'jvm'
72
+ Requires-Dist: tree-sitter-java<0.24,>=0.23; extra == 'jvm'
73
+ Requires-Dist: tree-sitter-kotlin<1.2,>=0.23; extra == 'jvm'
74
74
  Provides-Extra: kotlin
75
- Requires-Dist: tree-sitter-kotlin>=0.23; extra == 'kotlin'
75
+ Requires-Dist: tree-sitter-kotlin<1.2,>=0.23; extra == 'kotlin'
76
76
  Provides-Extra: mobile
77
- Requires-Dist: tree-sitter-kotlin>=0.23; extra == 'mobile'
78
- Requires-Dist: tree-sitter-swift>=0.0.1; extra == 'mobile'
77
+ Requires-Dist: tree-sitter-kotlin<1.2,>=0.23; extra == 'mobile'
78
+ Requires-Dist: tree-sitter-swift<0.8,>=0.0.1; extra == 'mobile'
79
79
  Provides-Extra: php
80
- Requires-Dist: tree-sitter-php>=0.23; extra == 'php'
80
+ Requires-Dist: tree-sitter-php<0.25,>=0.23; extra == 'php'
81
81
  Provides-Extra: ruby
82
- Requires-Dist: tree-sitter-ruby>=0.23; extra == 'ruby'
82
+ Requires-Dist: tree-sitter-ruby<0.24,>=0.23; extra == 'ruby'
83
83
  Provides-Extra: rust
84
- Requires-Dist: tree-sitter-rust>=0.23; extra == 'rust'
84
+ Requires-Dist: tree-sitter-rust<0.25,>=0.23; extra == 'rust'
85
85
  Provides-Extra: svelte
86
- Requires-Dist: tree-sitter-svelte>=0.23; extra == 'svelte'
86
+ Requires-Dist: tree-sitter-svelte<1.1,>=0.23; extra == 'svelte'
87
87
  Provides-Extra: swift
88
- Requires-Dist: tree-sitter-swift>=0.0.1; extra == 'swift'
88
+ Requires-Dist: tree-sitter-swift<0.8,>=0.0.1; extra == 'swift'
89
89
  Provides-Extra: web
90
- Requires-Dist: tree-sitter-html>=0.23; extra == 'web'
91
- Requires-Dist: tree-sitter-svelte>=0.23; extra == 'web'
90
+ Requires-Dist: tree-sitter-html<0.24,>=0.23; extra == 'web'
91
+ Requires-Dist: tree-sitter-svelte<1.1,>=0.23; extra == 'web'
92
92
  Description-Content-Type: text/markdown
93
93
 
94
94
  <p align="center">
@@ -118,6 +118,29 @@ Description-Content-Type: text/markdown
118
118
 
119
119
  ---
120
120
 
121
+ ## What's new in 0.6.7
122
+
123
+ Follow-ups to the 0.6.6 graphify-parity audit: grammar drift now fails loudly instead of silently, and ignore-file negations no longer slow scans down.
124
+
125
+ - **Grammar drift is a loud failure, not a silent empty graph** — when a tree-sitter query can't compile against a grammar it's *supposed* to support (e.g. a node-type rename in a future grammar bump), `run_query` now raises and the file is recorded as an `ExtractionFailure` instead of silently extracting nothing. Together with the 0.6.6 upper-bound pins and the "every query compiles against every grammar it claims" test, drift is now caught three independent ways.
126
+ - **A single `!` negation in `.codebeaconignore` no longer forces a full-tree walk** — one negation rule anywhere used to disable directory pruning *everywhere*, so the scanner descended into every excluded directory (`node_modules`, `build`, …) even when the negation couldn't possibly rescue anything there. Each ignored directory is now kept only if a negation could actually re-include a file beneath *it*; unrelated `!` rules cost nothing.
127
+ - **Ignore globs compile once** — the gitignore-style matcher memoizes each pattern's compiled regex instead of rebuilding it on every path check, speeding up discovery on deep trees with large ignore files. (Semantics unchanged.)
128
+
129
+ ---
130
+
131
+ ## What's new in 0.6.6
132
+
133
+ A graphify-parity audit of upstream v0.8.37–v0.8.40 (and reported issues through #1362): a verify-then-adversarially-refute sweep of 32 candidates confirmed **6 real bugs**. The headline — three framework extractors were silently producing *nothing*.
134
+
135
+ - **TypeScript Express / Koa / Fastify apps now extract routes** — `express.scm` hard-coded the JavaScript class-name node type, which is an "Impossible pattern" under the TypeScript grammar, so the whole query failed to compile and the error was swallowed: **TS Express apps extracted 0 routes**. (JavaScript apps worked, and the only test fixture was `.js`, so it went unnoticed.) The identical root cause hit `vue.scm` (Vue SFCs with a plain `<script>` → 0 components). Both now use a grammar-neutral node wildcard that compiles under JS and TS.
136
+ - **Kotlin files in Spring projects no longer error** — `spring_boot.scm` is a Java-grammar query but was allowed to run against Kotlin, emitting `Invalid node type: marker_annotation` and dropping every `.kt` file. Kotlin is now gated off cleanly (Kotlin Spring Boot would need its own query).
137
+ - **Tree-sitter grammars are pinned with upper bounds** — `pyproject.toml` pinned grammars open-ended (`>=0.23`), so a future grammar release that renames AST node types could silently re-break the queries. Every grammar now has a compatible-range ceiling, and a new test asserts every shipped `.scm` compiles against every grammar it claims to support.
138
+ - **The extraction cache is versioned** — after upgrading codebeacon, an incremental `--update` could reuse results extracted by the *old* version for unchanged files (a content hash can't detect that the extractor itself changed). The cache is now stamped with the codebeacon version and discarded on mismatch.
139
+ - **Accented / non-ASCII names resolve on macOS** — `codebeacon query` / `path` / MCP and `affected` now Unicode-NFC-normalise labels and paths, so a name copied from a macOS filename (stored as NFD) matches the NFC label in the graph (e.g. `Auditoría`).
140
+ - Plus: a corrupt extraction `cache.json` is backed up and rebuilt instead of being silently reset and then overwritten.
141
+
142
+ ---
143
+
121
144
  ## What's new in 0.6.5
122
145
 
123
146
  `codebeacon upgrade` now works everywhere — it previously assumed a plain pip install and died silently on machines where that wasn't true.
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Neu in 0.6.7
31
+
32
+ Folgearbeiten zum graphify-Parity-Audit aus 0.6.6: Grammar-Drift schlägt jetzt laut fehl statt stillschweigend, und Negationen in der Ignore-Datei verlangsamen Scans nicht mehr.
33
+
34
+ - **Grammar-Drift ist ein lauter Fehler, kein stiller leerer Graph** — wenn eine tree-sitter-Query gegen eine Grammar, die sie unterstützen *soll*, nicht kompiliert (z. B. eine umbenannte Node-Art in einem künftigen Grammar-Update), wirft `run_query` jetzt eine Ausnahme und die Datei wird als `ExtractionFailure` erfasst, statt still nichts zu extrahieren. Zusammen mit den Obergrenzen-Pins aus 0.6.6 und dem Test „jede Query kompiliert gegen jede Grammar, die sie beansprucht" wird Drift nun auf drei unabhängige Weisen erkannt.
35
+ - **Eine einzelne `!`-Negation in `.codebeaconignore` erzwingt keinen vollständigen Baum-Durchlauf mehr** — eine Negationsregel irgendwo hat das Verzeichnis-Pruning *überall* deaktiviert, sodass der Scanner in jedes ausgeschlossene Verzeichnis (`node_modules`, `build`, …) hinabstieg, selbst wenn die Negation dort nichts retten konnte. Ein ignoriertes Verzeichnis wird jetzt nur behalten, wenn eine Negation tatsächlich eine Datei *darunter* wieder einschließen könnte; nicht zugehörige `!`-Regeln kosten nichts.
36
+ - **Ignore-Globs werden einmal kompiliert** — der gitignore-artige Matcher merkt sich die kompilierte Regex je Muster, statt sie bei jeder Pfadprüfung neu zu bauen (schnellere Erkennung bei tiefen Bäumen mit großen Ignore-Dateien). Semantik unverändert.
37
+
38
+ ---
39
+
40
+ ## Neu in 0.6.6
41
+
42
+ Ein graphify-Parity-Audit von Upstream v0.8.37–v0.8.40 (und gemeldeten Issues bis #1362): ein „prüfen, dann adversarial widerlegen"-Durchlauf über 32 Kandidaten bestätigte **6 echte Bugs**. Der Hauptpunkt — drei Framework-Extraktoren produzierten still *nichts*.
43
+
44
+ - **TypeScript-Express/Koa/Fastify-Apps extrahieren jetzt Routen** — `express.scm` hatte die JavaScript-Node-Art für Klassennamen fest verdrahtet, was unter der TypeScript-Grammar ein „Impossible pattern" ist; die gesamte Query kompilierte nicht und der Fehler wurde verschluckt: **TS-Express-Apps extrahierten 0 Routen**. (JavaScript-Apps funktionierten, und die einzige Test-Fixture war `.js`, daher blieb es unbemerkt.) Dieselbe Ursache traf `vue.scm` (Vue-SFCs mit reinem `<script>` → 0 Komponenten). Beide nutzen jetzt einen grammar-neutralen Node-Wildcard, der unter JS und TS kompiliert.
45
+ - **Kotlin-Dateien in Spring-Projekten verursachen keinen Fehler mehr** — `spring_boot.scm` ist eine Java-Grammar-Query, durfte aber gegen Kotlin laufen und gab `Invalid node type: marker_annotation` aus, wobei jede `.kt`-Datei verworfen wurde. Kotlin wird jetzt sauber ausgeschlossen (Kotlin-Spring-Boot bräuchte eine eigene Query).
46
+ - **tree-sitter-Grammars haben jetzt Versions-Obergrenzen** — `pyproject.toml` pinnte Grammars nach oben offen (`>=0.23`), sodass ein künftiges Grammar-Release, das AST-Node-Arten umbenennt, die Queries still wieder hätte brechen können. Jede Grammar hat nun eine kompatible Obergrenze, und ein neuer Test prüft, dass jede ausgelieferte `.scm` gegen jede beanspruchte Grammar kompiliert.
47
+ - **Der Extraktions-Cache ist versioniert** — nach einem Upgrade konnte ein inkrementelles `--update` für unveränderte Dateien Ergebnisse der *alten* Version wiederverwenden (ein Content-Hash erkennt nicht, dass sich der Extraktor geändert hat). Der Cache trägt jetzt die codebeacon-Version und wird bei Abweichung verworfen.
48
+ - **Akzentuierte / Nicht-ASCII-Namen werden unter macOS aufgelöst** — `codebeacon query` / `path` / MCP und `affected` normalisieren Labels und Pfade jetzt nach Unicode-NFC, sodass ein aus einem macOS-Dateinamen (als NFD gespeichert) kopierter Name das NFC-Label im Graphen trifft (z. B. `Auditoría`).
49
+ - Außerdem: eine beschädigte `cache.json` wird gesichert und neu aufgebaut, statt still zurückgesetzt und überschrieben zu werden.
50
+
51
+ ---
52
+
30
53
  ## Neu in 0.6.5
31
54
 
32
55
  `codebeacon upgrade` funktioniert jetzt überall — bisher ging der Befehl von einer normalen pip-Installation aus und scheiterte auf anderen Maschinen stillschweigend.
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Novedades en 0.6.7
31
+
32
+ Seguimiento de la auditoría de paridad con graphify de 0.6.6: la deriva de gramática ahora falla de forma ruidosa en lugar de silenciosa, y las negaciones en el archivo de ignore ya no ralentizan los escaneos.
33
+
34
+ - **La deriva de gramática es un fallo ruidoso, no un grafo vacío silencioso** — cuando una query de tree-sitter no compila contra una gramática que *debería* soportar (p. ej. un nodo renombrado en una futura actualización de gramática), `run_query` ahora lanza una excepción y el archivo se registra como `ExtractionFailure` en lugar de extraer silenciosamente nada. Junto con los topes superiores de 0.6.6 y el test «cada query compila contra cada gramática que declara soportar», la deriva se detecta ahora de tres formas independientes.
35
+ - **Una sola negación `!` en `.codebeaconignore` ya no fuerza un recorrido completo del árbol** — una regla de negación en cualquier sitio desactivaba el pruning de directorios *en todas partes*, así que el escáner descendía a cada directorio excluido (`node_modules`, `build`, …) aunque la negación no pudiera rescatar nada allí. Ahora un directorio ignorado solo se conserva si una negación realmente podría reincluir un archivo *debajo de él*; las reglas `!` no relacionadas no cuestan nada.
36
+ - **Los globs de ignore se compilan una sola vez** — el matcher estilo gitignore memoiza la regex compilada por patrón en lugar de reconstruirla en cada comprobación de ruta (descubrimiento más rápido en árboles profundos con archivos de ignore grandes). La semántica no cambia.
37
+
38
+ ---
39
+
40
+ ## Novedades en 0.6.6
41
+
42
+ Una auditoría de paridad con graphify de upstream v0.8.37–v0.8.40 (e issues reportados hasta #1362): un barrido de «verificar y luego refutar de forma adversaria» sobre 32 candidatos confirmó **6 bugs reales**. Lo principal: tres extractores de framework producían silenciosamente *nada*.
43
+
44
+ - **Las apps de Express/Koa/Fastify en TypeScript ahora extraen rutas** — `express.scm` fijaba el nodo de nombre de clase de JavaScript, que es un «Impossible pattern» bajo la gramática de TypeScript, así que toda la query no compilaba y el error se tragaba: **las apps de Express en TS extraían 0 rutas**. (Las apps de JavaScript funcionaban, y la única fixture de test era `.js`, así que pasó desapercibido.) La misma causa afectaba a `vue.scm` (SFC de Vue con `<script>` plano → 0 componentes). Ambos usan ahora un comodín de nodo neutral a la gramática que compila en JS y TS.
45
+ - **Los archivos Kotlin en proyectos Spring ya no dan error** — `spring_boot.scm` es una query de gramática Java pero se permitía ejecutarla contra Kotlin, emitiendo `Invalid node type: marker_annotation` y descartando cada archivo `.kt`. Kotlin ahora se bloquea limpiamente (Kotlin Spring Boot necesitaría su propia query).
46
+ - **Las gramáticas de tree-sitter están fijadas con topes superiores** — `pyproject.toml` fijaba las gramáticas sin límite superior (`>=0.23`), así que un futuro release de gramática que renombrara nodos del AST podría romper las queries de nuevo en silencio. Cada gramática tiene ahora un tope de rango compatible, y un nuevo test verifica que cada `.scm` distribuida compila contra cada gramática que declara soportar.
47
+ - **La caché de extracción está versionada** — tras actualizar codebeacon, un `--update` incremental podía reutilizar resultados extraídos por la versión *antigua* para archivos sin cambios (un hash de contenido no detecta que el extractor cambió). La caché ahora lleva la versión de codebeacon y se descarta si no coincide.
48
+ - **Los nombres acentuados / no ASCII se resuelven en macOS** — `codebeacon query` / `path` / MCP y `affected` ahora normalizan etiquetas y rutas a Unicode NFC, de modo que un nombre copiado de un nombre de archivo de macOS (guardado como NFD) coincide con la etiqueta NFC del grafo (p. ej. `Auditoría`).
49
+ - Además: una `cache.json` corrupta se respalda y se reconstruye en lugar de resetearse y sobrescribirse en silencio.
50
+
51
+ ---
52
+
30
53
  ## Novedades en 0.6.5
31
54
 
32
55
  `codebeacon upgrade` ahora funciona en cualquier entorno — antes asumía una instalación pip normal y fallaba silenciosamente en máquinas donde no lo era.
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Nouveautés en 0.6.7
31
+
32
+ Suite de l'audit de parité graphify de 0.6.6 : la dérive de grammaire échoue désormais bruyamment au lieu de silencieusement, et les négations du fichier d'ignore ne ralentissent plus les scans.
33
+
34
+ - **La dérive de grammaire est un échec bruyant, pas un graphe vide silencieux** — quand une requête tree-sitter ne compile pas contre une grammaire qu'elle est *censée* prendre en charge (p. ex. un nœud renommé dans une future mise à jour de grammaire), `run_query` lève désormais une exception et le fichier est enregistré comme `ExtractionFailure` au lieu de n'extraire silencieusement rien. Avec les bornes supérieures de 0.6.6 et le test « chaque requête compile contre chaque grammaire qu'elle revendique », la dérive est désormais détectée de trois façons indépendantes.
35
+ - **Une seule négation `!` dans `.codebeaconignore` ne force plus un parcours complet de l'arbre** — une règle de négation où que ce soit désactivait l'élagage des répertoires *partout*, de sorte que le scanner descendait dans chaque répertoire exclu (`node_modules`, `build`, …) même si la négation ne pouvait rien y récupérer. Un répertoire ignoré n'est désormais conservé que si une négation pourrait réellement réinclure un fichier *en dessous* ; les règles `!` non liées ne coûtent rien.
36
+ - **Les globs d'ignore sont compilés une seule fois** — le matcher de style gitignore mémorise la regex compilée par motif au lieu de la reconstruire à chaque vérification de chemin (découverte plus rapide sur les arbres profonds avec de gros fichiers d'ignore). Sémantique inchangée.
37
+
38
+ ---
39
+
40
+ ## Nouveautés en 0.6.6
41
+
42
+ Un audit de parité graphify de l'upstream v0.8.37–v0.8.40 (et des issues signalées jusqu'à #1362) : un balayage « vérifier puis réfuter de façon adverse » sur 32 candidats a confirmé **6 vrais bugs**. L'essentiel — trois extracteurs de framework ne produisaient silencieusement *rien*.
43
+
44
+ - **Les apps Express/Koa/Fastify en TypeScript extraient désormais des routes** — `express.scm` codait en dur le nœud de nom de classe de JavaScript, qui est un « Impossible pattern » sous la grammaire TypeScript ; toute la requête ne compilait pas et l'erreur était avalée : **les apps Express en TS extrayaient 0 route**. (Les apps JavaScript fonctionnaient, et la seule fixture de test était `.js`, donc c'est passé inaperçu.) La même cause touchait `vue.scm` (SFC Vue avec `<script>` simple → 0 composant). Les deux utilisent désormais un joker de nœud neutre vis-à-vis de la grammaire qui compile en JS et TS.
45
+ - **Les fichiers Kotlin dans les projets Spring ne provoquent plus d'erreur** — `spring_boot.scm` est une requête de grammaire Java mais était autorisée à s'exécuter contre Kotlin, émettant `Invalid node type: marker_annotation` et écartant chaque fichier `.kt`. Kotlin est désormais proprement bloqué (Kotlin Spring Boot nécessiterait sa propre requête).
46
+ - **Les grammaires tree-sitter sont épinglées avec des bornes supérieures** — `pyproject.toml` épinglait les grammaires sans limite supérieure (`>=0.23`), de sorte qu'une future version de grammaire renommant des nœuds de l'AST pouvait à nouveau casser les requêtes en silence. Chaque grammaire a désormais un plafond de plage compatible, et un nouveau test vérifie que chaque `.scm` livrée compile contre chaque grammaire qu'elle revendique.
47
+ - **Le cache d'extraction est versionné** — après une mise à jour de codebeacon, un `--update` incrémental pouvait réutiliser des résultats extraits par l'*ancienne* version pour des fichiers inchangés (un hash de contenu ne détecte pas que l'extracteur a changé). Le cache porte désormais la version de codebeacon et est rejeté en cas de divergence.
48
+ - **Les noms accentués / non-ASCII se résolvent sous macOS** — `codebeacon query` / `path` / MCP et `affected` normalisent désormais les libellés et chemins en Unicode NFC, de sorte qu'un nom copié depuis un nom de fichier macOS (stocké en NFD) correspond au libellé NFC dans le graphe (p. ex. `Auditoría`).
49
+ - De plus : un `cache.json` corrompu est sauvegardé et reconstruit au lieu d'être silencieusement réinitialisé puis écrasé.
50
+
51
+ ---
52
+
30
53
  ## Nouveautés en 0.6.5
31
54
 
32
55
  `codebeacon upgrade` fonctionne désormais partout — il supposait auparavant une installation pip classique et échouait silencieusement sur les machines où ce n'était pas le cas.
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## 0.6.7 の新機能
31
+
32
+ 0.6.6 の graphify パリティ監査のフォローアップ: grammar ドリフトが静かに埋もれず明示的に失敗するようになり、ignore ファイルの否定ルールがスキャンを遅くしなくなりました。
33
+
34
+ - **grammar ドリフトは「静かな空グラフ」ではなく明示的な失敗に** — tree-sitter クエリがサポートすべき grammar に対してコンパイルに失敗すると(将来の grammar 更新でのノード型改名など)、`run_query` が例外を送出し、該当ファイルは静かに 0 件抽出される代わりに `ExtractionFailure` として記録されます。0.6.6 の上限ピン + 「すべてのクエリが、サポートを宣言したすべての grammar に対してコンパイルされる」テストと合わせ、ドリフトが 3 つの独立した方法で検出されます。
35
+ - **`.codebeaconignore` の単一の `!` 否定が、もはやツリー全体の走査を強制しない** — どこかにある否定ルール 1 つがディレクトリ枝刈りを*全体的に*無効化していたため、その否定が中で何も復活させられない場合でも、スキャナはすべての除外ディレクトリ(`node_modules`、`build` など)へ降りていました。いまや無視されたディレクトリは、否定が実際に*その下*のファイルを再包含しうる場合にのみ走査され、無関係な `!` ルールはコストになりません。
36
+ - **ignore glob を一度だけコンパイル** — gitignore 形式のマッチャは、パス検査ごとに正規表現を再構築する代わりに、パターンごとにコンパイル済み正規表現をメモ化します(大きな ignore ファイルを持つ深いツリーでの探索が高速化)。意味は不変です。
37
+
38
+ ---
39
+
40
+ ## 0.6.6 の新機能
41
+
42
+ アップストリーム v0.8.37–v0.8.40(および #1362 までの報告 issue)の graphify パリティ監査: 32 候補を「検証してから敵対的に反証する」方式で精査し、**実バグ 6 件**を確定しました。要点 — フレームワーク抽出器 3 つが静かに*何も*生成していませんでした。
43
+
44
+ - **TypeScript の Express/Koa/Fastify アプリがルートを抽出するように** — `express.scm` が JavaScript のクラス名ノード型をハードコードしており、これは TypeScript grammar では「Impossible pattern」となるため、クエリ全体がコンパイルに失敗し、そのエラーが握りつぶされていました: **TS の Express アプリはルート 0 件**。(JavaScript アプリは正常で、テストフィクスチャが `.js` のみだったため見過ごされていました。)同じ原因が `vue.scm` にもありました(素の `<script>` の Vue SFC → コンポーネント 0 件)。両者とも JS・TS の双方でコンパイルされる grammar 中立なノードワイルドカードに修正しました。
45
+ - **Spring プロジェクトの Kotlin ファイルがエラーを出さないように** — `spring_boot.scm` は Java grammar のクエリですが Kotlin に対して実行が許可されており、`Invalid node type: marker_annotation` を出してすべての `.kt` ファイルを捨てていました。いまや Kotlin はきれいに除外されます(Kotlin Spring Boot には専用クエリが必要)。
46
+ - **tree-sitter grammar に上限ピンを追加** — `pyproject.toml` が grammar を上限なし(`>=0.23`)でピンしていたため、AST ノード型を改名する将来の grammar リリースがクエリを静かに再び壊しうる状態でした。いまやすべての grammar に互換範囲の上限があり、出荷されるすべての `.scm` が、サポートを宣言したすべての grammar に対してコンパイルされることを検証する新しいテストを追加しました。
47
+ - **抽出キャッシュをバージョン管理** — codebeacon のアップグレード後、変更のないファイルに対して増分 `--update` が*旧*バージョンの抽出結果を再利用しうる状態でした(コンテンツハッシュは抽出器自体の変更を検出できません)。キャッシュには codebeacon バージョンが刻印され、不一致時に破棄されます。
48
+ - **アクセント付き / 非 ASCII 名が macOS で解決される** — `codebeacon query` / `path` / MCP と `affected` がラベルとパスを Unicode NFC 正規化するようになり、macOS のファイル名からコピーした名前(NFD で保存)がグラフ内の NFC ラベルに一致します(例: `Auditoría`)。
49
+ - さらに: 破損した `cache.json` を静かにリセットして上書きする代わりに、バックアップして再構築します。
50
+
51
+ ---
52
+
30
53
  ## 0.6.5 の新機能
31
54
 
32
55
  `codebeacon upgrade` がどの環境でも動作するようになりました — 従来は通常の pip インストールを前提としており、そうでないマシンでは何もせずに静かに失敗していました。
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## 0.6.7 새 소식
31
+
32
+ 0.6.6 graphify-패리티 감사의 후속 작업: grammar 드리프트가 이제 조용히 묻히지 않고 명시적으로 실패하며, ignore 파일의 부정 규칙이 더 이상 스캔을 느리게 하지 않습니다.
33
+
34
+ - **Grammar 드리프트가 "조용한 빈 그래프"가 아니라 명시적 실패로** — tree-sitter 쿼리가 지원해야 할 grammar에 대해 컴파일에 실패하면(향후 grammar 버전업의 노드 타입 개명 등), `run_query`가 이제 예외를 던져 해당 파일이 조용히 0개 추출되는 대신 `ExtractionFailure`로 기록됩니다. 0.6.6의 상한 핀 + "모든 쿼리가 자신이 지원한다고 선언한 모든 grammar에 대해 컴파일된다" 테스트와 함께, 드리프트가 세 가지 독립적인 방법으로 잡힙니다.
35
+ - **`.codebeaconignore`의 단일 `!` 부정 규칙이 더 이상 전체 트리 순회를 강제하지 않음** — 부정 규칙 하나가 어디에 있든 디렉터리 가지치기를 *전역적으로* 비활성화해서, 그 부정 규칙이 안에서 아무것도 되살릴 수 없는 경우에도 스캐너가 모든 제외 디렉터리(`node_modules`, `build` 등)로 내려갔습니다. 이제 각 무시된 디렉터리는 부정 규칙이 실제로 *그 아래* 파일을 되살릴 수 있을 때만 순회되며, 무관한 `!` 규칙은 비용이 들지 않습니다.
36
+ - **Ignore 글롭을 한 번만 컴파일** — gitignore 스타일 매처가 경로를 검사할 때마다 정규식을 다시 만드는 대신 패턴별 컴파일된 정규식을 메모이즈합니다(큰 ignore 파일을 가진 깊은 트리에서 탐색 속도 향상). 동작 의미는 동일합니다.
37
+
38
+ ---
39
+
40
+ ## 0.6.6 새 소식
41
+
42
+ 업스트림 v0.8.37–v0.8.40(및 #1362까지 리포트된 이슈)에 대한 graphify-패리티 감사: 32개 후보를 "검증 후 적대적 반박" 방식으로 훑어 **실제 버그 6개**를 확정했습니다. 핵심 — 프레임워크 추출기 3개가 조용히 *아무것도* 만들지 못하고 있었습니다.
43
+
44
+ - **TypeScript Express / Koa / Fastify 앱이 이제 라우트를 추출** — `express.scm`이 JavaScript의 클래스 이름 노드 타입을 하드코딩했는데, 이는 TypeScript grammar에서 "Impossible pattern"이라 쿼리 전체가 컴파일에 실패하고 그 에러가 묻혔습니다: **TS Express 앱은 라우트 0개**. (JavaScript 앱은 정상이었고 테스트 픽스처가 `.js`뿐이라 발각되지 않았습니다.) 동일한 근본 원인이 `vue.scm`에도 있었습니다(JS `<script>` Vue SFC → 컴포넌트 0개). 둘 다 JS·TS 모두에서 컴파일되는 grammar 중립 노드 와일드카드로 수정했습니다.
45
+ - **Spring 프로젝트의 Kotlin 파일이 더 이상 에러를 내지 않음** — `spring_boot.scm`은 Java grammar 쿼리인데 Kotlin에 대해 실행이 허용되어 `Invalid node type: marker_annotation`를 내며 모든 `.kt` 파일을 버렸습니다. 이제 Kotlin은 깨끗하게 차단됩니다(Kotlin Spring Boot는 별도 쿼리가 필요).
46
+ - **tree-sitter grammar에 상한 핀 추가** — `pyproject.toml`이 grammar를 상한 없이(`>=0.23`) 핀해서, AST 노드 타입을 개명하는 향후 grammar 릴리스가 쿼리를 조용히 다시 깨뜨릴 수 있었습니다. 이제 모든 grammar에 호환 범위 상한이 있고, 모든 출고 `.scm`이 자신이 지원한다고 선언한 모든 grammar에 대해 컴파일되는지 검증하는 테스트가 추가되었습니다.
47
+ - **추출 캐시에 버전 도장** — codebeacon 업그레이드 후 증분 `--update`가 변경되지 않은 파일에 대해 *이전* 버전이 추출한 결과를 재사용할 수 있었습니다(콘텐츠 해시는 추출기 자체가 바뀐 것을 감지하지 못함). 이제 캐시에 codebeacon 버전이 찍히고 버전 불일치 시 폐기됩니다.
48
+ - **악센트 / 비-ASCII 이름이 macOS에서 해석됨** — `codebeacon query` / `path` / MCP와 `affected`가 라벨과 경로를 Unicode NFC로 정규화하므로, macOS 파일명에서 복사한 이름(NFD로 저장)이 그래프의 NFC 라벨과 매칭됩니다(예: `Auditoría`).
49
+ - 추가로: 손상된 추출 `cache.json`을 조용히 리셋한 뒤 덮어쓰는 대신 백업하고 재생성합니다.
50
+
51
+ ---
52
+
30
53
  ## 0.6.5 새 소식
31
54
 
32
55
  `codebeacon upgrade` 가 이제 어떤 환경에서도 동작합니다 — 이전에는 일반 pip 설치를 가정해서, 그렇지 않은 머신에서는 아무것도 못 하고 조용히 실패했습니다.
@@ -25,6 +25,29 @@
25
25
 
26
26
  ---
27
27
 
28
+ ## What's new in 0.6.7
29
+
30
+ Follow-ups to the 0.6.6 graphify-parity audit: grammar drift now fails loudly instead of silently, and ignore-file negations no longer slow scans down.
31
+
32
+ - **Grammar drift is a loud failure, not a silent empty graph** — when a tree-sitter query can't compile against a grammar it's *supposed* to support (e.g. a node-type rename in a future grammar bump), `run_query` now raises and the file is recorded as an `ExtractionFailure` instead of silently extracting nothing. Together with the 0.6.6 upper-bound pins and the "every query compiles against every grammar it claims" test, drift is now caught three independent ways.
33
+ - **A single `!` negation in `.codebeaconignore` no longer forces a full-tree walk** — one negation rule anywhere used to disable directory pruning *everywhere*, so the scanner descended into every excluded directory (`node_modules`, `build`, …) even when the negation couldn't possibly rescue anything there. Each ignored directory is now kept only if a negation could actually re-include a file beneath *it*; unrelated `!` rules cost nothing.
34
+ - **Ignore globs compile once** — the gitignore-style matcher memoizes each pattern's compiled regex instead of rebuilding it on every path check, speeding up discovery on deep trees with large ignore files. (Semantics unchanged.)
35
+
36
+ ---
37
+
38
+ ## What's new in 0.6.6
39
+
40
+ A graphify-parity audit of upstream v0.8.37–v0.8.40 (and reported issues through #1362): a verify-then-adversarially-refute sweep of 32 candidates confirmed **6 real bugs**. The headline — three framework extractors were silently producing *nothing*.
41
+
42
+ - **TypeScript Express / Koa / Fastify apps now extract routes** — `express.scm` hard-coded the JavaScript class-name node type, which is an "Impossible pattern" under the TypeScript grammar, so the whole query failed to compile and the error was swallowed: **TS Express apps extracted 0 routes**. (JavaScript apps worked, and the only test fixture was `.js`, so it went unnoticed.) The identical root cause hit `vue.scm` (Vue SFCs with a plain `<script>` → 0 components). Both now use a grammar-neutral node wildcard that compiles under JS and TS.
43
+ - **Kotlin files in Spring projects no longer error** — `spring_boot.scm` is a Java-grammar query but was allowed to run against Kotlin, emitting `Invalid node type: marker_annotation` and dropping every `.kt` file. Kotlin is now gated off cleanly (Kotlin Spring Boot would need its own query).
44
+ - **Tree-sitter grammars are pinned with upper bounds** — `pyproject.toml` pinned grammars open-ended (`>=0.23`), so a future grammar release that renames AST node types could silently re-break the queries. Every grammar now has a compatible-range ceiling, and a new test asserts every shipped `.scm` compiles against every grammar it claims to support.
45
+ - **The extraction cache is versioned** — after upgrading codebeacon, an incremental `--update` could reuse results extracted by the *old* version for unchanged files (a content hash can't detect that the extractor itself changed). The cache is now stamped with the codebeacon version and discarded on mismatch.
46
+ - **Accented / non-ASCII names resolve on macOS** — `codebeacon query` / `path` / MCP and `affected` now Unicode-NFC-normalise labels and paths, so a name copied from a macOS filename (stored as NFD) matches the NFC label in the graph (e.g. `Auditoría`).
47
+ - Plus: a corrupt extraction `cache.json` is backed up and rebuilt instead of being silently reset and then overwritten.
48
+
49
+ ---
50
+
28
51
  ## What's new in 0.6.5
29
52
 
30
53
  `codebeacon upgrade` now works everywhere — it previously assumed a plain pip install and died silently on machines where that wasn't true.
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Novidades na 0.6.7
31
+
32
+ Acompanhamento da auditoria de paridade com graphify da 0.6.6: a deriva de gramática agora falha de forma ruidosa em vez de silenciosa, e as negações no arquivo de ignore não deixam mais os scans lentos.
33
+
34
+ - **A deriva de gramática é uma falha ruidosa, não um grafo vazio silencioso** — quando uma query do tree-sitter não compila contra uma gramática que *deveria* suportar (p. ex. um nó renomeado em uma futura atualização de gramática), `run_query` agora lança uma exceção e o arquivo é registrado como `ExtractionFailure` em vez de extrair silenciosamente nada. Junto com os limites superiores da 0.6.6 e o teste "cada query compila contra cada gramática que declara suportar", a deriva agora é detectada de três formas independentes.
35
+ - **Uma única negação `!` no `.codebeaconignore` não força mais um percurso completo da árvore** — uma regra de negação em qualquer lugar desativava o pruning de diretórios *em todo lugar*, então o scanner descia em cada diretório excluído (`node_modules`, `build`, …) mesmo quando a negação não podia resgatar nada ali. Agora um diretório ignorado só é mantido se uma negação realmente puder reincluir um arquivo *abaixo dele*; regras `!` não relacionadas não custam nada.
36
+ - **Globs de ignore são compilados uma única vez** — o matcher no estilo gitignore memoiza a regex compilada por padrão em vez de reconstruí-la a cada verificação de caminho (descoberta mais rápida em árvores profundas com arquivos de ignore grandes). Semântica inalterada.
37
+
38
+ ---
39
+
40
+ ## Novidades na 0.6.6
41
+
42
+ Uma auditoria de paridade com graphify do upstream v0.8.37–v0.8.40 (e issues reportadas até #1362): uma varredura de "verificar e então refutar de forma adversária" sobre 32 candidatos confirmou **6 bugs reais**. O destaque — três extratores de framework produziam silenciosamente *nada*.
43
+
44
+ - **Apps Express/Koa/Fastify em TypeScript agora extraem rotas** — `express.scm` fixava o nó de nome de classe do JavaScript, que é um "Impossible pattern" sob a gramática do TypeScript, então a query inteira não compilava e o erro era engolido: **apps Express em TS extraíam 0 rotas**. (Apps JavaScript funcionavam, e a única fixture de teste era `.js`, então passou despercebido.) A mesma causa atingia `vue.scm` (SFCs Vue com `<script>` simples → 0 componentes). Ambos agora usam um coringa de nó neutro de gramática que compila em JS e TS.
45
+ - **Arquivos Kotlin em projetos Spring não dão mais erro** — `spring_boot.scm` é uma query de gramática Java mas tinha permissão de rodar contra Kotlin, emitindo `Invalid node type: marker_annotation` e descartando cada arquivo `.kt`. Kotlin agora é bloqueado de forma limpa (Kotlin Spring Boot precisaria de sua própria query).
46
+ - **Gramáticas do tree-sitter têm limites superiores fixados** — `pyproject.toml` fixava as gramáticas sem limite superior (`>=0.23`), então um futuro release de gramática que renomeasse nós da AST poderia quebrar as queries silenciosamente de novo. Cada gramática agora tem um teto de faixa compatível, e um novo teste verifica que cada `.scm` distribuída compila contra cada gramática que declara suportar.
47
+ - **O cache de extração é versionado** — após atualizar o codebeacon, um `--update` incremental podia reutilizar resultados extraídos pela versão *antiga* para arquivos inalterados (um hash de conteúdo não detecta que o extrator mudou). O cache agora carrega a versão do codebeacon e é descartado em caso de divergência.
48
+ - **Nomes acentuados / não ASCII resolvem no macOS** — `codebeacon query` / `path` / MCP e `affected` agora normalizam rótulos e caminhos para Unicode NFC, de modo que um nome copiado de um nome de arquivo do macOS (armazenado como NFD) corresponde ao rótulo NFC no grafo (p. ex. `Auditoría`).
49
+ - Além disso: um `cache.json` corrompido é salvo em backup e reconstruído em vez de ser silenciosamente resetado e sobrescrito.
50
+
51
+ ---
52
+
30
53
  ## Novidades na 0.6.5
31
54
 
32
55
  `codebeacon upgrade` agora funciona em qualquer ambiente — antes ele presumia uma instalação pip comum e falhava silenciosamente em máquinas onde não era o caso.
@@ -27,6 +27,29 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## 0.6.7 新功能
31
+
32
+ 对 0.6.6 graphify 对齐审计的后续:grammar 漂移现在会明确报错而非静默掩盖,ignore 文件中的否定规则也不再拖慢扫描。
33
+
34
+ - **grammar 漂移是明确的失败,而非静默的空图** — 当 tree-sitter 查询无法对它*应当*支持的 grammar 编译时(例如未来 grammar 升级中重命名了节点类型),`run_query` 现在会抛出异常,该文件被记录为 `ExtractionFailure`,而不是静默地抽取出 0 项。配合 0.6.6 的上限固定以及"每个查询都能对它声明支持的每个 grammar 编译"的测试,漂移现在通过三种独立方式被发现。
35
+ - **`.codebeaconignore` 中单个 `!` 否定不再强制全树遍历** — 任意位置的一条否定规则会*全局*禁用目录剪枝,导致扫描器即使该否定无法在其中救回任何文件,也会进入每个被排除的目录(`node_modules`、`build` 等)。现在只有当某条否定确实可能重新包含其*下方*的文件时,被忽略的目录才会被遍历;无关的 `!` 规则不再有任何开销。
36
+ - **ignore glob 只编译一次** — gitignore 风格的匹配器会按模式缓存已编译的正则,而不是在每次路径检查时重建(在带有大型 ignore 文件的深层目录树上发现更快)。语义不变。
37
+
38
+ ---
39
+
40
+ ## 0.6.6 新功能
41
+
42
+ 对上游 v0.8.37–v0.8.40(以及截至 #1362 的报告 issue)的 graphify 对齐审计:以"先验证再对抗性反驳"的方式排查 32 个候选,确认了 **6 个真实 bug**。重点 — 三个框架抽取器在静默地*什么都不*产出。
43
+
44
+ - **TypeScript 的 Express/Koa/Fastify 应用现在能抽取路由** — `express.scm` 硬编码了 JavaScript 的类名节点类型,这在 TypeScript grammar 下是 "Impossible pattern",于是整个查询无法编译且错误被吞掉:**TS 的 Express 应用抽取出 0 条路由**。(JavaScript 应用正常,而唯一的测试夹具是 `.js`,所以一直没被发现。)同样的根因也出现在 `vue.scm`(带普通 `<script>` 的 Vue SFC → 0 个组件)。两者现在都改用在 JS 与 TS 下都能编译的 grammar 中立节点通配符。
45
+ - **Spring 项目中的 Kotlin 文件不再报错** — `spring_boot.scm` 是 Java grammar 查询,却被允许对 Kotlin 运行,抛出 `Invalid node type: marker_annotation` 并丢弃每个 `.kt` 文件。Kotlin 现在被干净地拦截(Kotlin Spring Boot 需要它自己的查询)。
46
+ - **tree-sitter grammar 加上了版本上限** — `pyproject.toml` 此前对 grammar 不设上限地固定(`>=0.23`),因此未来某个重命名 AST 节点类型的 grammar 发布可能再次静默破坏查询。现在每个 grammar 都有一个兼容范围上限,并新增了一个测试,验证每个随附的 `.scm` 都能对它声明支持的每个 grammar 编译。
47
+ - **抽取缓存带上了版本** — 升级 codebeacon 后,增量 `--update` 可能对未改动的文件复用*旧*版本抽取的结果(内容哈希无法察觉抽取器本身变了)。缓存现在带有 codebeacon 版本,版本不匹配时被丢弃。
48
+ - **带重音 / 非 ASCII 的名称在 macOS 上能解析** — `codebeacon query` / `path` / MCP 与 `affected` 现在会把标签和路径做 Unicode NFC 规范化,因此从 macOS 文件名(以 NFD 存储)复制的名称能匹配图中的 NFC 标签(例如 `Auditoría`)。
49
+ - 此外:损坏的 `cache.json` 会被备份并重建,而不是被静默重置后覆盖。
50
+
51
+ ---
52
+
30
53
  ## 0.6.5 新功能
31
54
 
32
55
  `codebeacon upgrade` 现在在任何环境下都能工作 — 此前它假定是普通 pip 安装,在并非如此的机器上会静默失败、什么也不做。
@@ -0,0 +1 @@
1
+ __version__ = "0.6.7"
@@ -17,6 +17,7 @@ Mirrors graphify #e44e6e9 ("v8 affected").
17
17
  from __future__ import annotations
18
18
 
19
19
  import subprocess
20
+ import unicodedata
20
21
  from dataclasses import dataclass, field
21
22
  from pathlib import Path
22
23
  from typing import Iterable
@@ -94,8 +95,10 @@ def affected_from_paths(
94
95
 
95
96
  # Normalise seed paths to POSIX with forward slashes; node source_file may
96
97
  # be absolute or repo-relative depending on how it was extracted, so we
97
- # match by suffix (a relative suffix of the absolute node path).
98
- seeds = [str(p).replace("\\", "/") for p in changed_paths]
98
+ # match by suffix (a relative suffix of the absolute node path). NFC-
99
+ # normalise so a macOS git-diff path (NFD) with accented filenames still
100
+ # matches the NFC source_file stored in beacon.json (#1338).
101
+ seeds = [unicodedata.normalize("NFC", str(p)).replace("\\", "/") for p in changed_paths]
99
102
 
100
103
  def _suffix_match(a: str, b: str) -> bool:
101
104
  # True when one path is a *path-segment-aligned* suffix of the other,
@@ -108,7 +111,7 @@ def affected_from_paths(
108
111
 
109
112
  seed_node_ids: list[str] = []
110
113
  for node_id, data in G.nodes(data=True):
111
- src = (data.get("source_file") or "").replace("\\", "/")
114
+ src = unicodedata.normalize("NFC", data.get("source_file") or "").replace("\\", "/")
112
115
  if not src:
113
116
  continue
114
117
  if any(_suffix_match(src, seed) for seed in seeds):
@@ -33,6 +33,7 @@ from __future__ import annotations
33
33
  import hashlib
34
34
  import json
35
35
  import os
36
+ import sys
36
37
  import threading
37
38
  import time
38
39
  from pathlib import Path
@@ -94,15 +95,52 @@ class Cache:
94
95
  def load(self) -> None:
95
96
  """Load cache from disk. Safe to call even if the cache file doesn't exist.
96
97
 
97
- When ``project_root`` is set, any legacy absolute-path keys that
98
- sit inside the project are rewritten to relative form. The first
99
- save afterwards persists the migration.
98
+ On-disk format is a version-stamped wrapper::
99
+
100
+ {"_cb_version": "0.6.6", "entries": {key: entry, ...}}
101
+
102
+ A cache written by a *different* codebeacon version is discarded: the
103
+ extractor logic and the ``.scm`` queries change between releases, so
104
+ reusing an older version's result for an *unchanged* source file would
105
+ serve silently-stale output. The content hash can't catch this — the
106
+ file didn't change, the extractor did. (Mirrors graphify #1252.) A
107
+ legacy *flat* cache (pre-0.6.6, no wrapper) is unversioned and dropped
108
+ for the same reason.
109
+
110
+ When ``project_root`` is set, surviving absolute-path keys are rewritten
111
+ to relative form (graphify #777); the first save persists the migration.
100
112
  """
113
+ from codebeacon import __version__
114
+
115
+ raw: Any = None
101
116
  try:
102
117
  if self._cache_file.exists():
103
- self._data = json.loads(self._cache_file.read_text(encoding="utf-8"))
118
+ raw = json.loads(self._cache_file.read_text(encoding="utf-8"))
104
119
  except (json.JSONDecodeError, OSError):
120
+ # Corrupt cache.json — preserve it (don't let the next save silently
121
+ # overwrite and destroy it) and rebuild from scratch. Mirrors the
122
+ # graphify v0.8.39 "manifest data-loss on corrupt JSON" fix.
123
+ self._backup_corrupt()
124
+ raw = None
125
+
126
+ if isinstance(raw, dict) and "_cb_version" in raw and "entries" in raw:
127
+ stored_version = raw.get("_cb_version")
128
+ entries = raw.get("entries") or {}
129
+ elif isinstance(raw, dict):
130
+ stored_version, entries = None, raw # legacy flat (unversioned)
131
+ else:
132
+ stored_version, entries = __version__, {} # nothing on disk
133
+
134
+ if stored_version != __version__:
135
+ # Discard a foreign-version / unversioned cache exactly once. Mark
136
+ # dirty only when there was something to drop, so the fresh,
137
+ # version-stamped cache is written on save().
105
138
  self._data = {}
139
+ if entries:
140
+ self._dirty = True
141
+ return
142
+
143
+ self._data = entries if isinstance(entries, dict) else {}
106
144
  if self._root is None or not self._data:
107
145
  return
108
146
  migrated: dict[str, dict] = {}
@@ -116,13 +154,33 @@ class Cache:
116
154
  self._data = migrated
117
155
  self._dirty = True
118
156
 
157
+ def _backup_corrupt(self) -> None:
158
+ """Move a corrupt ``cache.json`` aside so the next ``save()`` can't
159
+ silently overwrite it. Best-effort; the cache is reproducible."""
160
+ try:
161
+ ts = time.strftime("%Y%m%d-%H%M%S")
162
+ backup = self._cache_file.with_name(f"{self._cache_file.name}.{ts}.corrupt")
163
+ self._cache_file.replace(backup)
164
+ print(
165
+ f"codebeacon: {self._cache_file} was corrupt; preserved as "
166
+ f"{backup.name} and rebuilt the cache.",
167
+ file=sys.stderr,
168
+ )
169
+ except OSError:
170
+ pass
171
+
119
172
  def save(self) -> None:
120
173
  """Persist cache to disk. No-op if nothing has changed."""
121
174
  if not self._dirty:
122
175
  return
176
+ from codebeacon import __version__
177
+
123
178
  self._cache_dir.mkdir(parents=True, exist_ok=True)
124
179
  self._cache_file.write_text(
125
- json.dumps(self._data, ensure_ascii=False, indent=2),
180
+ json.dumps(
181
+ {"_cb_version": __version__, "entries": self._data},
182
+ ensure_ascii=False, indent=2,
183
+ ),
126
184
  encoding="utf-8",
127
185
  )
128
186
  self._dirty = False