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.
- {codebeacon-0.6.7 → codebeacon-0.6.8}/PKG-INFO +15 -1
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.de.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.es.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.fr.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.ja.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.ko.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.pt-BR.md +14 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/README.zh-CN.md +14 -0
- codebeacon-0.6.8/codebeacon/__init__.py +1 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/cli.py +5 -5
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/safety.py +32 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/scanner.py +24 -12
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/mcp.py +5 -2
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/obsidian.py +71 -4
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/components.py +4 -1
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/react.scm +57 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/write.py +112 -28
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/pipeline.py +43 -12
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/generator.py +101 -12
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/templates.py +5 -1
- {codebeacon-0.6.7 → codebeacon-0.6.8}/pyproject.toml +1 -1
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_discover.py +8 -4
- codebeacon-0.6.8/tests/test_graphify_parity_0_6_8.py +491 -0
- codebeacon-0.6.7/codebeacon/__init__.py +0 -1
- {codebeacon-0.6.7 → codebeacon-0.6.8}/.cursorrules +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/CODEOWNERS +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/dependabot.yml +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/workflows/ci.yml +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/.github/workflows/release.yml +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/.gitignore +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/AGENTS.md +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/CLAUDE.md +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/LICENSE +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/__main__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/affected.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/cache.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/filters.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/symbols.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/common/types.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/config.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/contextmap/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/contextmap/generator.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/diagnostics.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/detector.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/discover/ignore.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/callflow_html.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/hooks.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/merge.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/export/tree_html.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/base.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/dependencies.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/dotnet.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/entities.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/README.md +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/actix.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/angular.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/aspnet.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/django.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/express.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/fastapi.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/flask.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/gin.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/ktor.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/laravel.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/nestjs.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/rails.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/spring_boot.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/svelte.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/tauri.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/vapor.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/queries/vue.scm +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/routes.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/semantic.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/extract/services.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/analyze.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/build.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/cluster.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/graph/enrich.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/knowledge/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/knowledge/generator.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/plugins/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/plugins/githooks.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/plugins/skills.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/semantic_pipeline.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/skill/SKILL.md +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wave.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon/wiki/index.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/codebeacon.yaml.example +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/docs/TRANSLATION_STATUS.md +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/public-plan.md +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/skill/install.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/conftest.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/actix/main.rs +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/angular/app.component.ts +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/aspnet/UserController.cs +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/django/views.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/express/userRouter.js +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/fastapi/main.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/flask/app.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/gin/main.go +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/pyproject.toml +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/src/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/src/main.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/api-python/src/services.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/web/package.json +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/integration_workspace/web/src/UserPage.tsx +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/ktor/UserRoutes.kt +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/laravel/UserController.php +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/nestjs/user.controller.ts +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/rails/users_controller.rb +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/react/UserPage.tsx +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/spring_boot/UserController.java +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/sveltekit/+page.svelte +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/vapor/routes.swift +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/fixtures/vue/UserList.vue +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/integration/__init__.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/integration/test_full_pipeline.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_affected.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_affected_wiki.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_audit_bugfixes.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_cli_dispatch.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_cli_upgrade.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_contextmap_paths.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_deep_dive_grouping.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_dependencies.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_diagnostics.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_dotnet.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_entities.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_filters.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graph.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_0_6_3.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_0_6_6.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_0_6_7.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_graphify_parity_fixes.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_independent_audit_fixes.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_knowledge.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_known_bugs.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_mcp_and_semantic.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_optional_grammars.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_pipeline_module.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_plugins.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_resolve.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_routes.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_safety_and_writes.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_scanner_sensitive.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_semantic.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_semantic_hardening.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_semantic_stats.py +0 -0
- {codebeacon-0.6.7 → codebeacon-0.6.8}/tests/test_services.py +0 -0
- {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.
|
|
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
|
|
136
|
+
"""Return the raw lines of the project's ignore rules (no filtering).
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
161
|
+
lines.extend((root_path / ".gitignore").read_text(encoding="utf-8").splitlines())
|
|
157
162
|
except (FileNotFoundError, OSError):
|
|
158
163
|
pass
|
|
159
|
-
|
|
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 =
|
|
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
|