codebeacon 0.6.7__tar.gz → 0.6.8__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.7 → codebeacon-0.6.8}/PKG-INFO +15 -1
  2. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.de.md +14 -0
  3. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.es.md +14 -0
  4. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.fr.md +14 -0
  5. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.ja.md +14 -0
  6. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.ko.md +14 -0
  7. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.md +14 -0
  8. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.pt-BR.md +14 -0
  9. {codebeacon-0.6.7 → codebeacon-0.6.8}/README.zh-CN.md +14 -0
  10. codebeacon-0.6.8/codebeacon/__init__.py +1 -0
  11. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/cli.py +5 -5
  12. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/safety.py +32 -0
  13. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/scanner.py +24 -12
  14. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/mcp.py +5 -2
  15. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/obsidian.py +71 -4
  16. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/components.py +4 -1
  17. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/react.scm +57 -0
  18. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/write.py +112 -28
  19. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/pipeline.py +43 -12
  20. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/generator.py +101 -12
  21. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/templates.py +5 -1
  22. {codebeacon-0.6.7 → codebeacon-0.6.8}/pyproject.toml +1 -1
  23. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_discover.py +8 -4
  24. codebeacon-0.6.8/tests/test_graphify_parity_0_6_8.py +491 -0
  25. codebeacon-0.6.7/codebeacon/__init__.py +0 -1
  26. {codebeacon-0.6.7 → codebeacon-0.6.8}/.cursorrules +0 -0
  27. {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/CODEOWNERS +0 -0
  28. {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/dependabot.yml +0 -0
  29. {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/workflows/ci.yml +0 -0
  30. {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/workflows/release.yml +0 -0
  31. {codebeacon-0.6.7 → codebeacon-0.6.8}/.gitignore +0 -0
  32. {codebeacon-0.6.7 → codebeacon-0.6.8}/AGENTS.md +0 -0
  33. {codebeacon-0.6.7 → codebeacon-0.6.8}/CLAUDE.md +0 -0
  34. {codebeacon-0.6.7 → codebeacon-0.6.8}/LICENSE +0 -0
  35. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/__main__.py +0 -0
  36. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/affected.py +0 -0
  37. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/cache.py +0 -0
  38. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/__init__.py +0 -0
  39. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/filters.py +0 -0
  40. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/symbols.py +0 -0
  41. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/types.py +0 -0
  42. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/config.py +0 -0
  43. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/contextmap/__init__.py +0 -0
  44. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/contextmap/generator.py +0 -0
  45. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/diagnostics.py +0 -0
  46. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/__init__.py +0 -0
  47. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/detector.py +0 -0
  48. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/ignore.py +0 -0
  49. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/__init__.py +0 -0
  50. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/callflow_html.py +0 -0
  51. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/hooks.py +0 -0
  52. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/merge.py +0 -0
  53. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/tree_html.py +0 -0
  54. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/__init__.py +0 -0
  55. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/base.py +0 -0
  56. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/dependencies.py +0 -0
  57. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/dotnet.py +0 -0
  58. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/entities.py +0 -0
  59. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/README.md +0 -0
  60. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/actix.scm +0 -0
  61. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/angular.scm +0 -0
  62. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/aspnet.scm +0 -0
  63. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/django.scm +0 -0
  64. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/express.scm +0 -0
  65. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/fastapi.scm +0 -0
  66. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/flask.scm +0 -0
  67. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/gin.scm +0 -0
  68. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/ktor.scm +0 -0
  69. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/laravel.scm +0 -0
  70. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/nestjs.scm +0 -0
  71. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/rails.scm +0 -0
  72. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/spring_boot.scm +0 -0
  73. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/svelte.scm +0 -0
  74. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/tauri.scm +0 -0
  75. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/vapor.scm +0 -0
  76. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/vue.scm +0 -0
  77. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/routes.py +0 -0
  78. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/semantic.py +0 -0
  79. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/services.py +0 -0
  80. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/__init__.py +0 -0
  81. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/analyze.py +0 -0
  82. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/build.py +0 -0
  83. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/cluster.py +0 -0
  84. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/enrich.py +0 -0
  85. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/knowledge/__init__.py +0 -0
  86. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/knowledge/generator.py +0 -0
  87. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/plugins/__init__.py +0 -0
  88. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/plugins/githooks.py +0 -0
  89. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/plugins/skills.py +0 -0
  90. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/semantic_pipeline.py +0 -0
  91. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/skill/SKILL.md +0 -0
  92. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wave.py +0 -0
  93. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/__init__.py +0 -0
  94. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/index.py +0 -0
  95. {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon.yaml.example +0 -0
  96. {codebeacon-0.6.7 → codebeacon-0.6.8}/docs/TRANSLATION_STATUS.md +0 -0
  97. {codebeacon-0.6.7 → codebeacon-0.6.8}/public-plan.md +0 -0
  98. {codebeacon-0.6.7 → codebeacon-0.6.8}/skill/install.py +0 -0
  99. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/__init__.py +0 -0
  100. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/conftest.py +0 -0
  101. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/actix/main.rs +0 -0
  102. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/angular/app.component.ts +0 -0
  103. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/aspnet/UserController.cs +0 -0
  104. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/django/views.py +0 -0
  105. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/express/userRouter.js +0 -0
  106. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/fastapi/main.py +0 -0
  107. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/flask/app.py +0 -0
  108. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/gin/main.go +0 -0
  109. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/pyproject.toml +0 -0
  110. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/src/__init__.py +0 -0
  111. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/src/main.py +0 -0
  112. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/src/services.py +0 -0
  113. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/web/package.json +0 -0
  114. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/web/src/UserPage.tsx +0 -0
  115. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/ktor/UserRoutes.kt +0 -0
  116. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/laravel/UserController.php +0 -0
  117. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/nestjs/user.controller.ts +0 -0
  118. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/rails/users_controller.rb +0 -0
  119. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/react/UserPage.tsx +0 -0
  120. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/spring_boot/UserController.java +0 -0
  121. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/sveltekit/+page.svelte +0 -0
  122. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/vapor/routes.swift +0 -0
  123. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/vue/UserList.vue +0 -0
  124. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/integration/__init__.py +0 -0
  125. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/integration/test_full_pipeline.py +0 -0
  126. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_affected.py +0 -0
  127. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_affected_wiki.py +0 -0
  128. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_audit_bugfixes.py +0 -0
  129. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_cli_dispatch.py +0 -0
  130. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_cli_upgrade.py +0 -0
  131. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_contextmap_paths.py +0 -0
  132. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_deep_dive_grouping.py +0 -0
  133. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_dependencies.py +0 -0
  134. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_diagnostics.py +0 -0
  135. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_dotnet.py +0 -0
  136. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_entities.py +0 -0
  137. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_filters.py +0 -0
  138. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graph.py +0 -0
  139. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_0_6_3.py +0 -0
  140. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_0_6_6.py +0 -0
  141. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_0_6_7.py +0 -0
  142. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_fixes.py +0 -0
  143. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_independent_audit_fixes.py +0 -0
  144. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_knowledge.py +0 -0
  145. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_known_bugs.py +0 -0
  146. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_mcp_and_semantic.py +0 -0
  147. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_optional_grammars.py +0 -0
  148. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_pipeline_module.py +0 -0
  149. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_plugins.py +0 -0
  150. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_resolve.py +0 -0
  151. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_routes.py +0 -0
  152. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_safety_and_writes.py +0 -0
  153. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_scanner_sensitive.py +0 -0
  154. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_semantic.py +0 -0
  155. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_semantic_hardening.py +0 -0
  156. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_semantic_stats.py +0 -0
  157. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_services.py +0 -0
  158. {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_wiki.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codebeacon
3
- Version: 0.6.7
3
+ Version: 0.6.8
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
@@ -118,6 +118,20 @@ Description-Content-Type: text/markdown
118
118
 
119
119
  ---
120
120
 
121
+ ## What's new in 0.6.8
122
+
123
+ A graphify-parity audit of upstream v0.8.41–v0.9.3 (reported issues through #1568). Every candidate was reproduced against codebeacon before fixing and re-checked by an adversarial review pass; **7 real bugs** confirmed, headlined by a data-loss trap and a privacy leak.
124
+
125
+ - **`--obsidian-dir` can no longer delete your notes** — pointed at an existing Obsidian vault, the export swept *every* `.md` under it before regenerating, so it could wipe a real vault. codebeacon now refuses any directory it doesn't own (only a genuinely empty dir, or one carrying its `.codebeacon-vault.json` marker, is adopted) and skips the export with a clear message instead of deleting.
126
+ - **`.gitignore` is no longer silently disabled by `.codebeaconignore`** — adding a `.codebeaconignore` used to *replace* the repo's `.gitignore`, so a file excluded only by `.gitignore` (a neutrally-named `prod-dump.sql`, `customer-data.*`) would get indexed into the committed `.codebeacon/` artifacts. The two are now merged (`.codebeaconignore` wins on conflict); adding it can only ever exclude *more*.
127
+ - **No machine-absolute paths in committed artifacts** — edge/link `source_file` values (the bulk of `beacon.json`) and the `Source:` lines in wiki/obsidian notes kept absolute `/Users/you/...` paths, so the committed index wasn't portable and leaked local paths. All are now project-relative (edges included, and cross-project `shares_db_entity` files too).
128
+ - **Same-named symbols in different directories no longer overwrite each other's notes** — wiki/obsidian filenames were derived from the label with no case-folding, so on macOS/Windows `UserService` and `userService` collided and one note was silently lost. Filenames are now collision-salted and case-folded; punctuation-only labels (`@`) fall back to `unnamed` instead of a broken `@.md`.
129
+ - **A corrupt `beacon.json` no longer crashes** — `codebeacon affected`, the MCP server, and `--wiki-only` runs now back up a corrupt/truncated graph and report a clear "re-run scan" message instead of a raw traceback.
130
+ - **More React components are captured** — `react.scm` missed function-expression components (`const X = function() {…}`), bare-imported HOCs (`const X = forwardRef(…)` without the `React.` prefix), and non-exported `function X()` components. All three are now extracted.
131
+ - **Wiki links never dangle** — a link to a page that was never written is downgraded to plain text, and a link to an article in a sibling bucket (a service → its entity) is repaired to the correct relative path instead of pointing at a missing file.
132
+
133
+ ---
134
+
121
135
  ## What's new in 0.6.7
122
136
 
123
137
  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.
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Neu in 0.6.8
31
+
32
+ Ein graphify-Parity-Audit von Upstream v0.8.41–v0.9.3 (gemeldete Issues bis #1568). Jeder Kandidat wurde vor der Behebung gegen codebeacon reproduziert und durch eine adversariale Review-Runde erneut geprüft; **7 echte Bugs** bestätigt, angeführt von einer Datenverlust-Falle und einem Privacy-Leak.
33
+
34
+ - **`--obsidian-dir` kann keine Notizen mehr löschen** — bei einem bestehenden Obsidian-Vault fegte der Export vor der Neugenerierung *jede* `.md` darunter weg und konnte so ein echtes Vault leeren. codebeacon verweigert jetzt jedes Verzeichnis, das es nicht besitzt (nur ein wirklich leeres Verzeichnis oder eines mit seinem `.codebeacon-vault.json`-Marker wird übernommen) und überspringt den Export mit einer klaren Meldung statt zu löschen.
35
+ - **`.gitignore` wird nicht mehr still durch `.codebeaconignore` deaktiviert** — das Hinzufügen einer `.codebeaconignore` *ersetzte* bisher die `.gitignore` des Repos, sodass eine nur durch `.gitignore` ausgeschlossene Datei (ein neutral benanntes `prod-dump.sql`, `customer-data.*`) in die committeten `.codebeacon/`-Artefakte indexiert werden konnte. Beide werden jetzt zusammengeführt (`.codebeaconignore` gewinnt bei Konflikten); das Hinzufügen kann nur *mehr* ausschließen.
36
+ - **Keine maschinenabsoluten Pfade mehr in committeten Artefakten** — `source_file`-Werte an Edges/Links (der Großteil von `beacon.json`) und die `Source:`-Zeilen in Wiki-/Obsidian-Notizen behielten absolute `/Users/du/...`-Pfade, sodass der committete Index nicht portabel war und lokale Pfade preisgab. Alle sind jetzt projekt-relativ (inklusive Edges und projektübergreifender `shares_db_entity`-Dateien).
37
+ - **Gleichnamige Symbole in unterschiedlichen Verzeichnissen überschreiben sich nicht mehr** — Wiki-/Obsidian-Dateinamen wurden ohne Groß-/Kleinschreibungs-Faltung aus dem Label abgeleitet, sodass unter macOS/Windows `UserService` und `userService` kollidierten und eine Notiz still verloren ging. Dateinamen sind jetzt kollisions-gesalzen und case-gefaltet; Labels aus reiner Interpunktion (`@`) fallen auf `unnamed` zurück statt auf ein kaputtes `@.md`.
38
+ - **Ein beschädigtes `beacon.json` stürzt nicht mehr ab** — `codebeacon affected`, der MCP-Server und `--wiki-only`-Läufe sichern jetzt einen beschädigten/abgeschnittenen Graphen und melden eine klare „Scan erneut ausführen"-Meldung statt eines rohen Tracebacks.
39
+ - **Mehr React-Komponenten werden erfasst** — `react.scm` übersah Function-Expression-Komponenten (`const X = function() {…}`), bare-importierte HOCs (`const X = forwardRef(…)` ohne `React.`-Präfix) und nicht exportierte `function X()`-Komponenten. Alle drei werden jetzt extrahiert.
40
+ - **Wiki-Links laufen nie ins Leere** — ein Link auf eine nie geschriebene Seite wird zu reinem Text herabgestuft, und ein Link auf einen Artikel in einem Nachbar-Bucket (ein Service → seine Entity) wird auf den korrekten relativen Pfad repariert, statt auf eine fehlende Datei zu zeigen.
41
+
42
+ ---
43
+
30
44
  ## Neu in 0.6.7
31
45
 
32
46
  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.
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Novedades en 0.6.8
31
+
32
+ Una auditoría de paridad con graphify del upstream v0.8.41–v0.9.3 (issues reportados hasta el #1568). Cada candidato se reprodujo contra codebeacon antes de corregirlo y se volvió a comprobar con una ronda de revisión adversarial; se confirmaron **7 bugs reales**, encabezados por una trampa de pérdida de datos y una fuga de privacidad.
33
+
34
+ - **`--obsidian-dir` ya no puede borrar tus notas** — apuntando a un vault de Obsidian existente, la exportación barría *todos* los `.md` debajo antes de regenerar, pudiendo vaciar un vault real. codebeacon ahora rechaza cualquier directorio que no posea (solo se adopta un directorio genuinamente vacío, o uno que lleve su marcador `.codebeacon-vault.json`) y omite la exportación con un mensaje claro en vez de borrar.
35
+ - **`.gitignore` ya no queda deshabilitado en silencio por `.codebeaconignore`** — añadir un `.codebeaconignore` solía *reemplazar* el `.gitignore` del repo, así que un archivo excluido solo por `.gitignore` (un `prod-dump.sql`, `customer-data.*` de nombre neutro) podía terminar indexado en los artefactos `.codebeacon/` que se commitean. Ahora ambos se fusionan (`.codebeaconignore` gana en conflicto); añadirlo solo puede excluir *más*.
36
+ - **Sin rutas absolutas de máquina en los artefactos commiteados** — los valores `source_file` de edges/links (el grueso de `beacon.json`) y las líneas `Source:` en las notas de wiki/obsidian conservaban rutas absolutas `/Users/tu/...`, así que el índice commiteado no era portable y filtraba rutas locales. Ahora todas son relativas al proyecto (edges incluidos, y también los archivos `shares_db_entity` entre proyectos).
37
+ - **Símbolos con el mismo nombre en directorios distintos ya no se sobrescriben las notas** — los nombres de archivo de wiki/obsidian se derivaban de la etiqueta sin normalizar mayúsculas/minúsculas, así que en macOS/Windows `UserService` y `userService` colisionaban y una nota se perdía en silencio. Ahora los nombres de archivo llevan sal anti-colisión y normalización de mayúsculas; las etiquetas solo de puntuación (`@`) recurren a `unnamed` en vez de un `@.md` roto.
38
+ - **Un `beacon.json` corrupto ya no provoca un cuelgue** — `codebeacon affected`, el servidor MCP y las ejecuciones `--wiki-only` ahora respaldan un grafo corrupto/truncado y muestran un mensaje claro de "vuelve a ejecutar scan" en vez de un traceback en crudo.
39
+ - **Se capturan más componentes de React** — `react.scm` pasaba por alto los componentes de expresión de función (`const X = function() {…}`), los HOC importados sin calificar (`const X = forwardRef(…)` sin el prefijo `React.`) y los componentes `function X()` no exportados. Los tres se extraen ahora.
40
+ - **Los enlaces del wiki nunca quedan rotos** — un enlace a una página que nunca se escribió se degrada a texto plano, y un enlace a un artículo en un bucket hermano (un servicio → su entidad) se repara a la ruta relativa correcta en vez de apuntar a un archivo inexistente.
41
+
42
+ ---
43
+
30
44
  ## Novedades en 0.6.7
31
45
 
32
46
  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.
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Nouveautés en 0.6.8
31
+
32
+ Un audit de parité graphify de l'upstream v0.8.41–v0.9.3 (issues signalées jusqu'au #1568). Chaque candidat a été reproduit sur codebeacon avant correction, puis revérifié par une passe de revue adversariale ; **7 bugs réels** confirmés, avec en tête un piège à perte de données et une fuite de confidentialité.
33
+
34
+ - **`--obsidian-dir` ne peut plus supprimer vos notes** — pointé vers un vault Obsidian existant, l'export balayait *tous* les `.md` en dessous avant de régénérer, pouvant ainsi vider un vrai vault. codebeacon refuse désormais tout répertoire qu'il ne possède pas (seul un répertoire réellement vide, ou portant son marqueur `.codebeacon-vault.json`, est adopté) et saute l'export avec un message clair au lieu de supprimer.
35
+ - **`.gitignore` n'est plus désactivé silencieusement par `.codebeaconignore`** — ajouter un `.codebeaconignore` *remplaçait* auparavant le `.gitignore` du dépôt, si bien qu'un fichier exclu uniquement par `.gitignore` (un `prod-dump.sql`, `customer-data.*` au nom neutre) pouvait être indexé dans les artefacts `.codebeacon/` commités. Les deux sont désormais fusionnés (`.codebeaconignore` l'emporte en cas de conflit) ; l'ajouter ne peut que exclure *davantage*.
36
+ - **Plus aucun chemin absolu machine dans les artefacts commités** — les valeurs `source_file` des edges/links (l'essentiel de `beacon.json`) et les lignes `Source:` des notes wiki/obsidian conservaient des chemins absolus `/Users/vous/...`, rendant l'index commité non portable et divulguant des chemins locaux. Tout est désormais relatif au projet (edges compris, ainsi que les fichiers `shares_db_entity` inter-projets).
37
+ - **Des symboles homonymes dans des répertoires différents n'écrasent plus les notes l'un de l'autre** — les noms de fichiers wiki/obsidian étaient dérivés du label sans normalisation de casse, si bien que sur macOS/Windows `UserService` et `userService` entraient en collision et une note disparaissait silencieusement. Les noms de fichiers sont désormais salés anti-collision et normalisés en casse ; les labels tout en ponctuation (`@`) retombent sur `unnamed` au lieu d'un `@.md` cassé.
38
+ - **Un `beacon.json` corrompu ne plante plus** — `codebeacon affected`, le serveur MCP et les exécutions `--wiki-only` sauvegardent désormais un graphe corrompu/tronqué et affichent un message clair « relancez scan » au lieu d'une trace brute.
39
+ - **Davantage de composants React sont capturés** — `react.scm` manquait les composants en expression de fonction (`const X = function() {…}`), les HOC importés nus (`const X = forwardRef(…)` sans préfixe `React.`) et les composants `function X()` non exportés. Les trois sont désormais extraits.
40
+ - **Les liens du wiki ne pointent plus jamais dans le vide** — un lien vers une page jamais écrite est rétrogradé en texte brut, et un lien vers un article dans un bucket voisin (un service → son entité) est réparé vers le bon chemin relatif au lieu de pointer vers un fichier manquant.
41
+
42
+ ---
43
+
30
44
  ## Nouveautés en 0.6.7
31
45
 
32
46
  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.
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## 0.6.8 の新機能
31
+
32
+ アップストリーム v0.8.41–v0.9.3(報告された issue は #1568 まで)の graphify パリティ監査です。各候補は修正前に codebeacon 上で実際に再現し、敵対的レビューパスで再確認しました。**実バグ7件**を確認、データ損失トラップとプライバシー漏洩が目玉です。
33
+
34
+ - **`--obsidian-dir` がノートを削除しなくなりました** — 既存の Obsidian vault を指定すると、再生成前にその配下の*すべて*の `.md` を一掃していたため、実際の vault を空にしてしまう可能性がありました。codebeacon は所有していないディレクトリを拒否するようになり(完全に空のディレクトリ、または `.codebeacon-vault.json` マーカーを持つディレクトリのみ採用)、削除する代わりに明確なメッセージとともにエクスポートをスキップします。
35
+ - **`.codebeaconignore` によって `.gitignore` が静かに無効化されなくなりました** — `.codebeaconignore` を追加すると以前はリポジトリの `.gitignore` を*置き換えて*いたため、`.gitignore` だけで除外されているファイル(中立的な名前の `prod-dump.sql`、`customer-data.*`)がコミットされる `.codebeacon/` 成果物にインデックスされる可能性がありました。両者は今やマージされます(競合時は `.codebeaconignore` が優先);追加しても除外が*増える*だけになりました。
36
+ - **コミットされる成果物にマシン絶対パスが含まれなくなりました** — エッジ/リンクの `source_file` 値(`beacon.json` の大半)と wiki/obsidian ノートの `Source:` 行が絶対パス `/Users/you/...` を保持していたため、コミットされたインデックスに可搬性がなく、ローカルパスが漏洩していました。すべてプロジェクト相対になりました(エッジ、およびプロジェクト横断の `shares_db_entity` ファイルも含む)。
37
+ - **異なるディレクトリの同名シンボルがノートを上書きしなくなりました** — wiki/obsidian のファイル名は大文字小文字を畳み込まずにラベルから生成されていたため、macOS/Windows で `UserService` と `userService` が衝突し、片方のノートが静かに失われていました。ファイル名は衝突耐性のソルトと大文字小文字の畳み込みを行うようになり、記号のみのラベル(`@`)は壊れた `@.md` の代わりに `unnamed` にフォールバックします。
38
+ - **破損した `beacon.json` でクラッシュしなくなりました** — `codebeacon affected`、MCP サーバー、`--wiki-only` 実行は、破損/切り詰められたグラフをバックアップし、生のトレースバックの代わりに明確な「scan を再実行してください」メッセージを報告するようになりました。
39
+ - **より多くの React コンポーネントを捕捉するようになりました** — `react.scm` は関数式コンポーネント(`const X = function() {…}`)、`React.` プレフィックスなしの bare import HOC(`const X = forwardRef(…)`)、および非エクスポートの `function X()` コンポーネントを見落としていました。この3つすべてが抽出されるようになりました。
40
+ - **wiki のリンクがリンク切れにならなくなりました** — 一度も書き込まれなかったページへのリンクはプレーンテキストに格下げされ、隣接バケット内の記事(サービス→そのエンティティ)へのリンクは、存在しないファイルを指す代わりに正しい相対パスに修復されます。
41
+
42
+ ---
43
+
30
44
  ## 0.6.7 の新機能
31
45
 
32
46
  0.6.6 の graphify パリティ監査のフォローアップ: grammar ドリフトが静かに埋もれず明示的に失敗するようになり、ignore ファイルの否定ルールがスキャンを遅くしなくなりました。
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## 0.6.8 새 소식
31
+
32
+ 업스트림 v0.8.41–v0.9.3(보고된 이슈 #1568까지)에 대한 graphify-패리티 감사입니다. 모든 후보를 수정 전에 codebeacon에서 실제로 재현하고 적대적 리뷰 패스로 재검증했으며, **7개의 실제 버그**를 확인했습니다 — 데이터 손실 함정과 프라이버시 유출이 핵심입니다.
33
+
34
+ - **`--obsidian-dir`가 더 이상 사용자 노트를 삭제하지 않음** — 기존 Obsidian 볼트를 가리키면 재생성 전에 그 아래 *모든* `.md`를 지워서 실제 볼트를 통째로 날릴 수 있었습니다. 이제 codebeacon이 소유하지 않은 디렉터리는 거부하고(완전히 비어 있거나 `.codebeacon-vault.json` 마커를 가진 디렉터리만 채택), 삭제 대신 명확한 메시지와 함께 내보내기를 건너뜁니다.
35
+ - **`.codebeaconignore`가 `.gitignore`를 조용히 무력화하지 않음** — `.codebeaconignore`를 추가하면 저장소의 `.gitignore`를 *대체*해서, `.gitignore`로만 제외된 파일(중립적 이름의 `prod-dump.sql`, `customer-data.*`)이 커밋되는 `.codebeacon/` 산출물에 인덱싱될 수 있었습니다. 이제 둘을 병합하며(충돌 시 `.codebeaconignore` 우선), 추가해도 *더 많이* 제외할 수만 있습니다.
36
+ - **커밋되는 산출물에 머신 절대 경로 없음** — 엣지/링크의 `source_file`(`beacon.json`의 대부분)과 wiki/obsidian 노트의 `Source:` 줄이 절대 경로 `/Users/you/...`를 유지해서 인덱스가 이식성이 없고 로컬 경로가 유출됐습니다. 이제 모두 프로젝트 상대 경로입니다(엣지 포함, 교차 프로젝트 `shares_db_entity` 파일도).
37
+ - **다른 디렉터리의 동일 이름 심볼이 서로의 노트를 덮어쓰지 않음** — wiki/obsidian 파일명이 대소문자 구분 없이 라벨에서 생성돼, macOS/Windows에서 `UserService`와 `userService`가 충돌해 한쪽 노트가 조용히 사라졌습니다. 이제 파일명이 충돌-솔팅 + 대소문자 폴딩되며, 구두점만 있는 라벨(`@`)은 깨진 `@.md` 대신 `unnamed`로 대체됩니다.
38
+ - **손상된 `beacon.json`이 더 이상 크래시를 내지 않음** — `codebeacon affected`, MCP 서버, `--wiki-only` 실행이 이제 손상/절단된 그래프를 백업하고 원시 트레이스백 대신 명확한 "scan 재실행" 메시지를 보여줍니다.
39
+ - **더 많은 React 컴포넌트 캡처** — `react.scm`이 함수식 컴포넌트(`const X = function() {…}`), bare-import HOC(`React.` 접두 없는 `const X = forwardRef(…)`), 비-export `function X()` 컴포넌트를 놓쳤습니다. 이제 셋 다 추출됩니다.
40
+ - **wiki 링크가 깨지지 않음** — 작성되지 않은 페이지로의 링크는 일반 텍스트로 강등되고, 형제 버킷의 문서로 가는 링크(서비스 → 엔티티)는 없는 파일을 가리키는 대신 올바른 상대 경로로 복구됩니다.
41
+
42
+ ---
43
+
30
44
  ## 0.6.7 새 소식
31
45
 
32
46
  0.6.6 graphify-패리티 감사의 후속 작업: grammar 드리프트가 이제 조용히 묻히지 않고 명시적으로 실패하며, ignore 파일의 부정 규칙이 더 이상 스캔을 느리게 하지 않습니다.
@@ -25,6 +25,20 @@
25
25
 
26
26
  ---
27
27
 
28
+ ## What's new in 0.6.8
29
+
30
+ A graphify-parity audit of upstream v0.8.41–v0.9.3 (reported issues through #1568). Every candidate was reproduced against codebeacon before fixing and re-checked by an adversarial review pass; **7 real bugs** confirmed, headlined by a data-loss trap and a privacy leak.
31
+
32
+ - **`--obsidian-dir` can no longer delete your notes** — pointed at an existing Obsidian vault, the export swept *every* `.md` under it before regenerating, so it could wipe a real vault. codebeacon now refuses any directory it doesn't own (only a genuinely empty dir, or one carrying its `.codebeacon-vault.json` marker, is adopted) and skips the export with a clear message instead of deleting.
33
+ - **`.gitignore` is no longer silently disabled by `.codebeaconignore`** — adding a `.codebeaconignore` used to *replace* the repo's `.gitignore`, so a file excluded only by `.gitignore` (a neutrally-named `prod-dump.sql`, `customer-data.*`) would get indexed into the committed `.codebeacon/` artifacts. The two are now merged (`.codebeaconignore` wins on conflict); adding it can only ever exclude *more*.
34
+ - **No machine-absolute paths in committed artifacts** — edge/link `source_file` values (the bulk of `beacon.json`) and the `Source:` lines in wiki/obsidian notes kept absolute `/Users/you/...` paths, so the committed index wasn't portable and leaked local paths. All are now project-relative (edges included, and cross-project `shares_db_entity` files too).
35
+ - **Same-named symbols in different directories no longer overwrite each other's notes** — wiki/obsidian filenames were derived from the label with no case-folding, so on macOS/Windows `UserService` and `userService` collided and one note was silently lost. Filenames are now collision-salted and case-folded; punctuation-only labels (`@`) fall back to `unnamed` instead of a broken `@.md`.
36
+ - **A corrupt `beacon.json` no longer crashes** — `codebeacon affected`, the MCP server, and `--wiki-only` runs now back up a corrupt/truncated graph and report a clear "re-run scan" message instead of a raw traceback.
37
+ - **More React components are captured** — `react.scm` missed function-expression components (`const X = function() {…}`), bare-imported HOCs (`const X = forwardRef(…)` without the `React.` prefix), and non-exported `function X()` components. All three are now extracted.
38
+ - **Wiki links never dangle** — a link to a page that was never written is downgraded to plain text, and a link to an article in a sibling bucket (a service → its entity) is repaired to the correct relative path instead of pointing at a missing file.
39
+
40
+ ---
41
+
28
42
  ## What's new in 0.6.7
29
43
 
30
44
  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.
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## Novidades na 0.6.8
31
+
32
+ Uma auditoria de paridade com graphify do upstream v0.8.41–v0.9.3 (issues reportadas até a #1568). Cada candidato foi reproduzido contra o codebeacon antes de ser corrigido e reverificado por uma rodada de revisão adversarial; **7 bugs reais** confirmados, com destaque para uma armadilha de perda de dados e um vazamento de privacidade.
33
+
34
+ - **`--obsidian-dir` não consegue mais apagar suas notas** — apontado para um vault do Obsidian existente, a exportação varria *todo* `.md` abaixo dele antes de regenerar, podendo esvaziar um vault real. O codebeacon agora recusa qualquer diretório que não possua (só um diretório genuinamente vazio, ou um com o marcador `.codebeacon-vault.json`, é adotado) e pula a exportação com uma mensagem clara em vez de apagar.
35
+ - **`.gitignore` não é mais desativado silenciosamente pelo `.codebeaconignore`** — adicionar um `.codebeaconignore` costumava *substituir* o `.gitignore` do repositório, então um arquivo excluído só pelo `.gitignore` (um `prod-dump.sql`, `customer-data.*` de nome neutro) podia acabar indexado nos artefatos `.codebeacon/` commitados. Agora os dois são mesclados (`.codebeaconignore` vence em conflito); adicioná-lo só pode excluir *mais*.
36
+ - **Nenhum caminho absoluto de máquina nos artefatos commitados** — os valores `source_file` de edges/links (a maior parte do `beacon.json`) e as linhas `Source:` nas notas de wiki/obsidian mantinham caminhos absolutos `/Users/voce/...`, então o índice commitado não era portátil e vazava caminhos locais. Agora todos são relativos ao projeto (edges incluídos, e também os arquivos `shares_db_entity` entre projetos).
37
+ - **Símbolos com o mesmo nome em diretórios diferentes não sobrescrevem mais as notas um do outro** — os nomes de arquivo de wiki/obsidian eram derivados do label sem normalização de maiúsculas/minúsculas, então no macOS/Windows `UserService` e `userService` colidiam e uma nota era silenciosamente perdida. Os nomes de arquivo agora recebem sal anticolisão e normalização de caixa; labels só com pontuação (`@`) caem para `unnamed` em vez de um `@.md` quebrado.
38
+ - **Um `beacon.json` corrompido não trava mais** — `codebeacon affected`, o servidor MCP e execuções `--wiki-only` agora fazem backup de um grafo corrompido/truncado e mostram uma mensagem clara de "execute o scan novamente" em vez de um traceback bruto.
39
+ - **Mais componentes React são capturados** — `react.scm` deixava passar componentes de expressão de função (`const X = function() {…}`), HOCs importados sem qualificação (`const X = forwardRef(…)` sem o prefixo `React.`) e componentes `function X()` não exportados. Os três agora são extraídos.
40
+ - **Links do wiki nunca ficam quebrados** — um link para uma página que nunca foi escrita é rebaixado a texto simples, e um link para um artigo em um bucket irmão (um serviço → sua entidade) é reparado para o caminho relativo correto em vez de apontar para um arquivo inexistente.
41
+
42
+ ---
43
+
30
44
  ## Novidades na 0.6.7
31
45
 
32
46
  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.
@@ -27,6 +27,20 @@
27
27
 
28
28
  ---
29
29
 
30
+ ## 0.6.8 新功能
31
+
32
+ 对上游 v0.8.41–v0.9.3 的 graphify 对齐审计(涵盖已报告的 issue,直到 #1568)。每个候选项在修复前都在 codebeacon 上实际复现,并通过对抗式复核再次确认;确认了 **7 个真实 bug**,以一个数据丢失陷阱和一个隐私泄露为首。
33
+
34
+ - **`--obsidian-dir` 不会再删除你的笔记** — 指向一个已有的 Obsidian vault 时,导出会在重新生成前清空其下*所有* `.md` 文件,可能把一个真实的 vault 清空。codebeacon 现在会拒绝任何它不拥有的目录(只有真正为空的目录,或带有 `.codebeacon-vault.json` 标记的目录才会被采用),并以清晰的提示跳过导出,而不是删除。
35
+ - **`.codebeaconignore` 不会再悄悄禁用 `.gitignore`** — 添加 `.codebeaconignore` 以前会*替换*仓库的 `.gitignore`,导致仅被 `.gitignore` 排除的文件(名称中立的 `prod-dump.sql`、`customer-data.*`)可能被索引进提交的 `.codebeacon/` 产物中。现在两者会合并(冲突时 `.codebeaconignore` 优先);添加它只会排除*更多*内容。
36
+ - **提交的产物中不再有本机绝对路径** — 边/链接的 `source_file` 值(`beacon.json` 的绝大部分)以及 wiki/obsidian 笔记中的 `Source:` 行此前保留绝对路径 `/Users/you/...`,导致提交的索引不可移植且泄露本地路径。现在全部改为项目相对路径(包括边,以及跨项目的 `shares_db_entity` 文件)。
37
+ - **不同目录下的同名符号不再互相覆盖笔记** — wiki/obsidian 的文件名此前直接由标签生成、不做大小写折叠,导致在 macOS/Windows 上 `UserService` 与 `userService` 冲突,一个笔记被静默丢失。文件名现在会做防冲突加盐和大小写折叠;仅由标点组成的标签(`@`)会回退为 `unnamed`,而不是生成损坏的 `@.md`。
38
+ - **损坏的 `beacon.json` 不再导致崩溃** — `codebeacon affected`、MCP 服务器以及 `--wiki-only` 运行现在会备份损坏/截断的图,并给出清晰的"请重新运行 scan"提示,而不是抛出原始堆栈跟踪。
39
+ - **捕获更多 React 组件** — `react.scm` 此前遗漏了函数表达式组件(`const X = function() {…}`)、未加 `React.` 前缀直接导入的 HOC(`const X = forwardRef(…)`),以及未导出的 `function X()` 组件。现在这三种都能被提取。
40
+ - **wiki 链接不再指向空页面** — 指向从未写入的页面的链接会降级为纯文本,指向相邻分类目录中文章的链接(例如 service → 其 entity)会被修复为正确的相对路径,而不是指向一个不存在的文件。
41
+
42
+ ---
43
+
30
44
  ## 0.6.7 新功能
31
45
 
32
46
  对 0.6.6 graphify 对齐审计的后续:grammar 漂移现在会明确报错而非静默掩盖,ignore 文件中的否定规则也不再拖慢扫描。
@@ -0,0 +1 @@
1
+ __version__ = "0.6.8"
@@ -195,7 +195,7 @@ def _cmd_query(args: argparse.Namespace) -> int:
195
195
  idx = BeaconIndex(beacon_dir)
196
196
  try:
197
197
  idx.load()
198
- except FileNotFoundError as e:
198
+ except (FileNotFoundError, ValueError) as e:
199
199
  print(f"Error: {e}", file=sys.stderr)
200
200
  return 1
201
201
  print(tool_beacon_query(idx, {"term": args.term, "limit": int(getattr(args, "limit", 20))}))
@@ -210,7 +210,7 @@ def _cmd_path(args: argparse.Namespace) -> int:
210
210
  idx = BeaconIndex(beacon_dir)
211
211
  try:
212
212
  idx.load()
213
- except FileNotFoundError as e:
213
+ except (FileNotFoundError, ValueError) as e:
214
214
  print(f"Error: {e}", file=sys.stderr)
215
215
  return 1
216
216
  print(tool_beacon_path(idx, {"source": args.source, "target": args.target}))
@@ -503,7 +503,7 @@ def _cmd_semantic_prepare(args: argparse.Namespace) -> int:
503
503
  max_tasks=args.max_tasks,
504
504
  chunk_size=args.chunk_size,
505
505
  )
506
- except FileNotFoundError as e:
506
+ except (FileNotFoundError, ValueError) as e:
507
507
  print(f"Error: {e}", file=sys.stderr)
508
508
  return 1
509
509
  print(
@@ -546,7 +546,7 @@ def _cmd_affected(args: argparse.Namespace) -> int:
546
546
  limit=int(getattr(args, "limit", 100)),
547
547
  include_wiki_paths=(output_format == "wiki"),
548
548
  )
549
- except FileNotFoundError as e:
549
+ except (FileNotFoundError, ValueError) as e:
550
550
  print(f"Error: {e}", file=sys.stderr)
551
551
  return 1
552
552
 
@@ -574,7 +574,7 @@ def _cmd_semantic_apply(args: argparse.Namespace) -> int:
574
574
  min_conf = DEFAULT_MIN_CONFIDENCE_SCORE
575
575
  try:
576
576
  result = apply(beacon_dir, min_confidence=min_conf)
577
- except FileNotFoundError as e:
577
+ except (FileNotFoundError, ValueError) as e:
578
578
  print(f"Error: {e}", file=sys.stderr)
579
579
  return 1
580
580
  print(
@@ -92,7 +92,13 @@ def cap_filename(name: str, limit: int = 200) -> str:
92
92
  Truncation slices the UTF-8 byte string and decodes with ``"ignore"`` so a
93
93
  multi-byte character straddling the cut is dropped rather than producing
94
94
  mojibake. Short names are returned untouched. Mirrors graphify 690b4e5.
95
+
96
+ A stem with no alphanumeric character (empty, whitespace-only, or all
97
+ punctuation such as ``"@"`` or ``"***"``) would produce a broken or hidden
98
+ filename (``@.md``, ``.md``); it falls back to ``"unnamed"`` (graphify #1409).
95
99
  """
100
+ if not any(c.isalnum() for c in name):
101
+ return "unnamed"
96
102
  encoded = name.encode("utf-8")
97
103
  if len(encoded) <= limit:
98
104
  return name
@@ -102,6 +108,32 @@ def cap_filename(name: str, limit: int = 200) -> str:
102
108
  return f"{truncated}_{digest}"
103
109
 
104
110
 
111
+ def dedup_stem(stem: str, node_id: str, claimed: dict[str, str], scope: str = "") -> str:
112
+ """Return a filesystem-unique, case-folded stem for ``stem``.
113
+
114
+ ``claimed`` maps a lowercased ``"<scope>/<stem>"`` key → the node_id that owns
115
+ it, and is mutated in place. When a DIFFERENT node would map to an
116
+ already-claimed key (a collision on a case-insensitive filesystem — macOS
117
+ APFS, Windows NTFS — where ``UserService`` and ``userService`` are the same
118
+ file), the stem is salted with a short stable hash of ``node_id`` so both
119
+ notes survive as distinct files instead of one silently overwriting the
120
+ other (graphify #1453/#1504/#1522).
121
+
122
+ The salt is prefixed ``_h`` so the suffix never looks like the ``_<digits>``
123
+ dedup markers other export steps strip. ``scope`` namespaces the claim (e.g.
124
+ the wiki subdirectory) so files in different directories never false-collide.
125
+ """
126
+ key = f"{scope}/{stem.lower()}" if scope else stem.lower()
127
+ owner = claimed.get(key)
128
+ if owner is None or owner == node_id:
129
+ claimed[key] = node_id
130
+ return stem
131
+ salted = f"{stem}_h{hashlib.sha1(node_id.encode('utf-8')).hexdigest()[:6]}"
132
+ salted_key = f"{scope}/{salted.lower()}" if scope else salted.lower()
133
+ claimed[salted_key] = node_id
134
+ return salted
135
+
136
+
105
137
  def safe_wiki_filename(label: str) -> str:
106
138
  """Filename stem for a wiki article: filesystem-safe and byte-capped.
107
139
 
@@ -133,30 +133,42 @@ def read_ignore_file(
133
133
  *,
134
134
  gitignore_fallback: bool = True,
135
135
  ) -> list[str]:
136
- """Return the raw lines of the project's ignore file (no filtering).
136
+ """Return the raw lines of the project's ignore rules (no filtering).
137
137
 
138
- Looks for ``.codebeaconignore`` first; if absent and ``gitignore_fallback``
139
- is True, falls back to the repo's ``.gitignore`` so users don't have to
140
- duplicate already-curated patterns.
138
+ Reads ``.gitignore`` first (when ``gitignore_fallback``) and appends
139
+ ``.codebeaconignore`` last, so the two are **merged** rather than one
140
+ replacing the other. Because :class:`~codebeacon.discover.ignore.IgnoreMatcher`
141
+ uses gitignore last-match-wins semantics, ``.codebeaconignore`` patterns win
142
+ on conflict (including ``!`` negations), yet adding a ``.codebeaconignore``
143
+ can only ever exclude *more* — it never silently drops the repo's existing
144
+ ``.gitignore`` exclusions.
145
+
146
+ This matters for privacy: a file excluded only by ``.gitignore`` (e.g. a
147
+ neutrally-named ``prod-dump.sql`` or ``customer-data.*``) must keep being
148
+ skipped even after a ``.codebeaconignore`` is added, or it would get indexed
149
+ into the committed ``.codebeacon/`` artifacts (graphify #1363).
141
150
 
142
151
  Pattern parsing is delegated to :class:`codebeacon.discover.ignore.IgnoreMatcher`,
143
152
  which implements gitignore semantics (negation, dir-only, anchored, trailing
144
153
  whitespace handling).
145
154
  """
146
155
  root_path = Path(root)
147
- primary = root_path / filename
148
- try:
149
- return primary.read_text(encoding="utf-8").splitlines()
150
- except (FileNotFoundError, OSError):
151
- pass
156
+ lines: list[str] = []
152
157
 
158
+ # .gitignore first (lower precedence).
153
159
  if gitignore_fallback:
154
- gi = root_path / ".gitignore"
155
160
  try:
156
- return gi.read_text(encoding="utf-8").splitlines()
161
+ lines.extend((root_path / ".gitignore").read_text(encoding="utf-8").splitlines())
157
162
  except (FileNotFoundError, OSError):
158
163
  pass
159
- return []
164
+
165
+ # .codebeaconignore last, so its patterns win on conflict and only ever add.
166
+ try:
167
+ lines.extend((root_path / filename).read_text(encoding="utf-8").splitlines())
168
+ except (FileNotFoundError, OSError):
169
+ pass
170
+
171
+ return lines
160
172
 
161
173
 
162
174
  def _should_ignore_dir(name: str) -> bool:
@@ -597,9 +597,12 @@ def serve(beacon_dir: str | Path) -> None:
597
597
 
598
598
  try:
599
599
  idx.load()
600
- except FileNotFoundError as e:
600
+ except (FileNotFoundError, ValueError) as e:
601
+ # FileNotFoundError: no beacon.json yet. ValueError: corrupt/truncated
602
+ # beacon.json (load_beacon backs it up + raises). Either way, still start
603
+ # the server so the client can connect and tools explain the error rather
604
+ # than crashing at startup with a traceback (graphify #1536).
601
605
  print(f"[codebeacon-mcp] {e}", file=sys.stderr)
602
- # Still start server so MCP client can connect — tools will explain the error
603
606
 
604
607
  print(f"[codebeacon-mcp] serving from {beacon_dir}", file=sys.stderr)
605
608
 
@@ -33,7 +33,8 @@ from typing import Any
33
33
 
34
34
  import networkx as nx
35
35
 
36
- from codebeacon.common.safety import cap_filename, escape_frontmatter_value, sanitize_label
36
+ from codebeacon.common.safety import cap_filename, dedup_stem, escape_frontmatter_value, sanitize_label
37
+ from codebeacon.graph.write import relativize_source_file
37
38
 
38
39
 
39
40
  # ── Regexes ────────────────────────────────────────────────────────────────────
@@ -53,6 +54,40 @@ _KEEP_RELS = frozenset({"calls_api", "shares_db_entity"}) # always preserved
53
54
  _JAVA_EXTS = frozenset({".java", ".kt"})
54
55
  _TS_EXTS = frozenset({".ts", ".tsx", ".js", ".jsx"})
55
56
 
57
+ # Ownership marker dropped into a vault codebeacon manages, so a later run knows
58
+ # it may safely clear/regenerate the vault (graphify #1506).
59
+ _VAULT_MARKER = ".codebeacon-vault.json"
60
+
61
+
62
+ class VaultNotOwnedError(ValueError):
63
+ """Raised when ``--obsidian-dir`` points at a directory codebeacon does not
64
+ own (non-empty, no marker). Subclasses ValueError so existing broad handlers
65
+ still catch it, while callers that want to skip *only* the ownership refusal
66
+ (not any other ValueError from the 12-step export) can catch this precisely.
67
+ """
68
+
69
+
70
+ def _is_codebeacon_owned_vault(vault: Path, is_default: bool) -> bool:
71
+ """True when it is safe for codebeacon to wipe & regenerate ``vault``.
72
+
73
+ Owned when it is the default ``output_dir/obsidian`` (we created it), when a
74
+ prior run left the ownership marker, or when it is GENUINELY EMPTY (nothing
75
+ but, at most, our marker) — a brand-new directory is safe to adopt. A
76
+ directory that merely holds no ``.md`` yet is NOT safe: a real Obsidian vault
77
+ starts with a ``.obsidian/`` config folder (and possibly ``.canvas`` /
78
+ attachments) and zero notes, so a ``.md``-count probe would wrongly adopt it,
79
+ stamp the marker, and delete the notes the user later authors. Probing errors
80
+ fail closed (refuse) — when in doubt, do not delete (graphify #1506).
81
+ """
82
+ if is_default:
83
+ return True
84
+ if (vault / _VAULT_MARKER).exists():
85
+ return True
86
+ try:
87
+ return not any(p.name != _VAULT_MARKER for p in vault.iterdir())
88
+ except OSError:
89
+ return False # cannot prove it is empty → refuse rather than wipe
90
+
56
91
 
57
92
  # ── Public entry point ─────────────────────────────────────────────────────────
58
93
 
@@ -61,6 +96,7 @@ def generate_obsidian_vault(
61
96
  communities: dict[str, int],
62
97
  output_dir: str | Path,
63
98
  obsidian_dir: str | Path | None = None,
99
+ project_roots: dict[str, str] | None = None,
64
100
  ) -> int:
65
101
  """Generate a fully post-processed Obsidian vault.
66
102
 
@@ -73,9 +109,32 @@ def generate_obsidian_vault(
73
109
  Returns:
74
110
  Total number of notes written.
75
111
  """
112
+ is_default = obsidian_dir is None
76
113
  vault = Path(obsidian_dir) if obsidian_dir else Path(output_dir) / "obsidian"
77
114
  vault.mkdir(parents=True, exist_ok=True)
78
115
 
116
+ # Guard: never wipe a directory codebeacon does not own (graphify #1506).
117
+ # The sweep below deletes every *.md under `vault`; if a user points
118
+ # `--obsidian-dir` at their real Obsidian vault, that destroys all their
119
+ # notes. We only sweep a vault we created: the default `output_dir/obsidian`,
120
+ # a directory that still carries our ownership marker from a prior run, or a
121
+ # directory with no notes yet (safe to adopt). Anything else is refused
122
+ # loudly *before* a single unlink, so the user's data is untouched.
123
+ if not _is_codebeacon_owned_vault(vault, is_default):
124
+ raise VaultNotOwnedError(
125
+ f"Refusing to write the Obsidian vault into {vault}: it already "
126
+ f"contains .md notes but no {_VAULT_MARKER} marker, so it looks like "
127
+ f"a user-managed vault. codebeacon would delete every .md under it. "
128
+ f"Point --obsidian-dir at an empty or codebeacon-managed directory, "
129
+ f"or drop the flag to use the default .codebeacon/obsidian."
130
+ )
131
+ # Stamp the ownership marker so subsequent runs recognise this vault as ours.
132
+ # It is a dot-file, so the sweep below (which skips dot-paths) preserves it.
133
+ (vault / _VAULT_MARKER).write_text(
134
+ json.dumps({"tool": "codebeacon", "owns": "obsidian-vault"}),
135
+ encoding="utf-8",
136
+ )
137
+
79
138
  # Clear stale notes from previous runs so step 1 regenerates cleanly. The
80
139
  # whole vault is generated from the graph, so we sweep every *.md anywhere
81
140
  # under it — not just one level inside service folders. Notes left at the
@@ -88,7 +147,7 @@ def generate_obsidian_vault(
88
147
  md.unlink()
89
148
 
90
149
  # Step 1 — basic note generation
91
- _step1_generate_notes(G, communities, vault)
150
+ _step1_generate_notes(G, communities, vault, project_roots)
92
151
 
93
152
  # Step 2 — broken wikilink normalisation
94
153
  _step2_fix_wikilinks(vault)
@@ -133,6 +192,7 @@ def _step1_generate_notes(
133
192
  G: nx.DiGraph,
134
193
  communities: dict[str, int],
135
194
  vault: Path,
195
+ project_roots: dict[str, str] | None = None,
136
196
  ) -> None:
137
197
  """One Obsidian note per graph node (skipping external stubs)."""
138
198
 
@@ -145,6 +205,11 @@ def _step1_generate_notes(
145
205
  out_edges[src].append((tgt, data))
146
206
  in_edges[tgt].append((src, data))
147
207
 
208
+ # Lowercased-stem → node_id, so two labels that differ only by case (or two
209
+ # same-labelled nodes across projects in the combined vault) don't overwrite
210
+ # each other's note on a case-insensitive filesystem (graphify #1453/#1504).
211
+ claimed: dict[str, str] = {}
212
+
148
213
  for node_id, data in G.nodes(data=True):
149
214
  ntype = data.get("type", "unknown")
150
215
  if ntype == "external":
@@ -152,11 +217,13 @@ def _step1_generate_notes(
152
217
 
153
218
  project = data.get("project", "_unknown")
154
219
  label = data.get("label", node_id)
155
- source_file = data.get("source_file", "")
220
+ source_file = relativize_source_file(
221
+ data.get("source_file", ""), (project_roots or {}).get(project)
222
+ )
156
223
  framework = data.get("framework", "")
157
224
  community_id = communities.get(node_id, -1)
158
225
 
159
- note_name = _safe_note_name(label)
226
+ note_name = dedup_stem(_safe_note_name(label), node_id, claimed)
160
227
  content = _build_note(
161
228
  node_id = node_id,
162
229
  label = label,
@@ -173,7 +173,10 @@ def _interpret_react(
173
173
  parent_key = None
174
174
  for k in ("component.export_func", "component.export_func_upper",
175
175
  "component.export_default_func", "component.export_arrow",
176
- "component.local_arrow", "component.hoc"):
176
+ "component.local_arrow", "component.hoc",
177
+ "component.hoc_local", "component.hoc_bare_export",
178
+ "component.hoc_bare_local", "component.export_fnexpr",
179
+ "component.local_fnexpr", "component.local_func"):
177
180
  if k in caps:
178
181
  parent_key = k
179
182
  break
@@ -96,6 +96,63 @@
96
96
  )
97
97
  ) @component.hoc_local
98
98
 
99
+ ; ── Bare-imported HOC: import { forwardRef, memo } from 'react' ───────────────
100
+ ; const Button = forwardRef((props, ref) => ...) — callee is a bare identifier,
101
+ ; not a React.* member_expression, so the patterns above miss it (graphify #1322).
102
+
103
+ (export_statement
104
+ (lexical_declaration
105
+ (variable_declarator
106
+ name: (identifier) @component.memo_name
107
+ value: (call_expression
108
+ function: (identifier) @_hoc_bare
109
+ (#match? @_hoc_bare "^(memo|forwardRef|lazy)$")
110
+ )
111
+ )
112
+ )
113
+ ) @component.hoc_bare_export
114
+
115
+ (lexical_declaration
116
+ (variable_declarator
117
+ name: (identifier) @component.memo_name
118
+ (#match? @component.memo_name "^[A-Z]")
119
+ value: (call_expression
120
+ function: (identifier) @_hoc_bare_l
121
+ (#match? @_hoc_bare_l "^(memo|forwardRef|lazy)$")
122
+ )
123
+ )
124
+ ) @component.hoc_bare_local
125
+
126
+ ; ── Function-expression component: const Foo = function () { return <jsx/> } ──
127
+ ; (arrow patterns above only match value: (arrow_function); graphify #1322)
128
+
129
+ (export_statement
130
+ (lexical_declaration
131
+ (variable_declarator
132
+ name: (identifier) @component.arrow_name
133
+ (#match? @component.arrow_name "^[A-Z]")
134
+ value: (function_expression)
135
+ )
136
+ )
137
+ ) @component.export_fnexpr
138
+
139
+ (lexical_declaration
140
+ (variable_declarator
141
+ name: (identifier) @component.arrow_name
142
+ (#match? @component.arrow_name "^[A-Z]")
143
+ value: (function_expression)
144
+ )
145
+ ) @component.local_fnexpr
146
+
147
+ ; ── Non-exported function-declaration component: function Foo () { ... } ──────
148
+ ; The function_declaration patterns above are all export_statement-wrapped, so a
149
+ ; local (non-exported) uppercase function component was never captured.
150
+
151
+ (function_declaration
152
+ name: (identifier) @component.func_name
153
+ (#match? @component.func_name "^[A-Z]")
154
+ ) @component.local_func
155
+
99
156
  ; ── Hook usage ────────────────────────────────────────────────────────────────
100
157
 
101
158
  (call_expression