@event4u/agent-config 17.2.1 → 17.4.0

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 (225) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/.claude-plugin/skills/install-agent-config/SKILL.md +9 -9
  5. package/CHANGELOG.md +167 -193
  6. package/CREDITS.md +2 -2
  7. package/README.md +1 -1
  8. package/dist/agent-src/skills/code-intelligence/SKILL.md +16 -12
  9. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  10. package/dist/agent-src/templates/scripts/check_memory.ts +1 -1
  11. package/dist/cli/commands/recordTriggerEval.js +1 -1
  12. package/dist/cli/registry.js +2 -2
  13. package/dist/cli-delegate/{chunk-KR5ZIFPO.js → chunk-5AYJGUA3.js} +1 -3
  14. package/dist/cli-delegate/chunk-DIFZBP4G.js +1336 -0
  15. package/dist/cli-delegate/chunk-E7ACTJTJ.js +6792 -0
  16. package/dist/cli-delegate/{chunk-VYZGJLCA.js → chunk-RKNT4LIK.js} +22 -1286
  17. package/dist/cli-delegate/{chunk-BZJ4Y3CI.js → chunk-WY5ZZPGL.js} +2 -2
  18. package/dist/cli-delegate/{chunk-CMZEKSNN.js → chunk-X6LZ7W5A.js} +73 -10
  19. package/dist/cli-delegate/cmd_conformance.js +190 -192
  20. package/dist/cli-delegate/cmd_doctor.js +5 -5
  21. package/dist/cli-delegate/cmd_doctor_neighbours.js +3 -3
  22. package/dist/cli-delegate/cmd_explain.js +1 -1
  23. package/dist/cli-delegate/cmd_export.js +2 -2
  24. package/dist/cli-delegate/cmd_handoff.js +2 -2
  25. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  26. package/dist/cli-delegate/cmd_prune.js +2 -2
  27. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  28. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  29. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  30. package/dist/cli-delegate/cmd_settings_set.js +8 -4
  31. package/dist/cli-delegate/cmd_sync.js +2 -2
  32. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  33. package/dist/cli-delegate/cmd_update.js +24 -13
  34. package/dist/cli-delegate/cmd_upgrade.js +2 -2
  35. package/dist/cli-delegate/cmd_validate.js +2 -2
  36. package/dist/cli-delegate/dist-25AZAHOP.js +3240 -0
  37. package/dist/cli-delegate/dist-P7MFPMF3.js +411 -0
  38. package/dist/cli-delegate/dist-XAS5ZLQB.js +6 -0
  39. package/dist/cli-delegate/{upgrade_unattended-VVL6JLSX.js → upgrade_unattended-QCWOKTUN.js} +178 -163
  40. package/dist/discovery/deprecation-report.md +1 -1
  41. package/dist/discovery/discovery-manifest.json +3 -3
  42. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  43. package/dist/discovery/discovery-manifest.summary.md +1 -1
  44. package/dist/discovery/dormancy-report.md +1 -1
  45. package/dist/discovery/orphan-report.md +1 -1
  46. package/dist/discovery/packs.json +2 -2
  47. package/dist/discovery/trust-report.md +1 -1
  48. package/dist/discovery/workspaces.json +2 -2
  49. package/dist/hooks/code_graph_refresh.chunk.cjs +443 -0
  50. package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -0
  51. package/dist/hooks/dispatch.js +1001 -938
  52. package/dist/hooks/dispatch.sha256 +1 -1
  53. package/dist/hooks/egress_engine.chunk.cjs +57 -7
  54. package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
  55. package/dist/hooks/update_notice.chunk.cjs +914 -0
  56. package/dist/hooks/update_notice.chunk.cjs.sha256 +1 -0
  57. package/dist/install/install.mjs +6974 -6037
  58. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -0
  59. package/dist/mcp/registry-manifest.json +3 -2
  60. package/dist/mcp/server.json +13 -4
  61. package/dist/mcp/server.mjs +71202 -63282
  62. package/dist/scripts/_lib/egress_rules.js +8 -2
  63. package/dist/scripts/condense.js +18 -15
  64. package/dist/server/routes/discovery.js +1 -1
  65. package/dist/server/routes/install.js +1 -1
  66. package/dist/server/routes/ping.js +1 -1
  67. package/dist/server/routes/schema.js +4 -4
  68. package/dist/server/routes/settings.js +2 -6
  69. package/dist/server/routes/wizard.js +6 -6
  70. package/dist/server/schemas/settings.js +42 -42
  71. package/dist/shared/settingsSurface.js +1 -1
  72. package/dist/shared/userMd/schema.js +1 -1
  73. package/dist/shared/zodJsonSchema.js +47 -0
  74. package/dist/ui/assets/index-B0bcIgi0.js +77 -0
  75. package/dist/ui/index.html +1 -1
  76. package/package.json +6 -3
  77. package/src/agent-src/templates/scripts/check_memory.ts +1 -1
  78. package/src/config/ci-local-parity.yml +11 -0
  79. package/src/config/continuity-surface.json +9 -2
  80. package/src/config/evaluator-budgets.json +9 -8
  81. package/src/config/gate-violation-baselines.json +3 -3
  82. package/src/config/gitignore-block.txt +6 -5
  83. package/src/config/hook-bundle-budget.json +46 -6
  84. package/src/config/pack-size-budget.json +12 -1
  85. package/src/config/packed-binary-manifest.json +18 -0
  86. package/src/config/sigstore/npm_keys.json +50 -0
  87. package/src/config/sigstore/trust-material.json +9 -0
  88. package/src/config/sigstore/trusted_root.json +126 -0
  89. package/src/config/skill-body-budget.json +7 -1
  90. package/src/scripts/_cli/cmd_settings_set.ts +3 -2
  91. package/src/scripts/_cli/cmd_update.ts +48 -20
  92. package/src/scripts/_cli/cmd_upgrade.ts +7 -1
  93. package/src/scripts/_cli/upgrade_unattended.ts +90 -76
  94. package/src/scripts/_dispatch.bash +4 -3
  95. package/src/scripts/_lib/confusables.ts +0 -19
  96. package/src/scripts/_lib/egress_normalize.ts +53 -23
  97. package/src/scripts/_lib/egress_rules.ts +8 -2
  98. package/src/scripts/_lib/egress_text.ts +5 -3
  99. package/src/scripts/_lib/egress_token.ts +7 -5
  100. package/src/scripts/_lib/gate_collector_experimental.ts +633 -0
  101. package/src/scripts/_lib/git_common_dir.ts +10 -0
  102. package/src/scripts/_lib/graph_feeder_record.ts +3 -2
  103. package/src/scripts/_lib/install_reach_checks.ts +32 -2
  104. package/src/scripts/_lib/lineage_backfill.ts +136 -0
  105. package/src/scripts/_lib/lineage_ledger.ts +211 -0
  106. package/src/scripts/_lib/lineage_map.ts +55 -0
  107. package/src/scripts/_lib/lineage_query.ts +140 -0
  108. package/src/scripts/_lib/lineage_seed.ts +116 -0
  109. package/src/scripts/_lib/lineage_watch.ts +249 -0
  110. package/src/scripts/_lib/link_crypto.ts +8 -4
  111. package/src/scripts/_lib/listing_manifests.ts +85 -0
  112. package/src/scripts/_lib/neighbour_census.ts +1 -0
  113. package/src/scripts/_lib/production_reach.ts +3 -3
  114. package/src/scripts/_lib/project_registry.ts +227 -0
  115. package/src/scripts/_lib/provenance_verify.ts +14 -242
  116. package/src/scripts/_lib/release_settings_surface.ts +2 -6
  117. package/src/scripts/_lib/sigstore_provenance.ts +177 -0
  118. package/src/scripts/_lib/source_id.ts +48 -0
  119. package/src/scripts/_lib/source_shape.ts +4 -0
  120. package/src/scripts/_lib/unattended_gates.ts +6 -1
  121. package/src/scripts/adoption_status.ts +231 -27
  122. package/src/scripts/advisories.ts +26 -10
  123. package/src/scripts/ai_council/council_egress.ts +5 -6
  124. package/src/scripts/bench_graph_feeder_latency.ts +10 -3
  125. package/src/scripts/build_mcp_registry_manifest.ts +23 -12
  126. package/src/scripts/check_dependency_floors.ts +14 -0
  127. package/src/scripts/check_egress_mutations.ts +1 -0
  128. package/src/scripts/check_release_manifest_content.ts +17 -2
  129. package/src/scripts/check_release_pr_shape.ts +5 -0
  130. package/src/scripts/code_graph/build.ts +145 -45
  131. package/src/scripts/code_graph/cli.ts +382 -35
  132. package/src/scripts/code_graph/conventions_default.ts +247 -0
  133. package/src/scripts/code_graph/detect.ts +5 -5
  134. package/src/scripts/code_graph/estate_cli.ts +135 -0
  135. package/src/scripts/code_graph/estate_index.ts +394 -0
  136. package/src/scripts/code_graph/estate_manifests.ts +223 -0
  137. package/src/scripts/code_graph/estate_map.ts +141 -0
  138. package/src/scripts/code_graph/extract.ts +8 -2
  139. package/src/scripts/code_graph/extract_more.ts +241 -2
  140. package/src/scripts/code_graph/graph_budget.ts +158 -0
  141. package/src/scripts/code_graph/graph_home.ts +80 -0
  142. package/src/scripts/code_graph/lineage_cli.ts +315 -0
  143. package/src/scripts/code_graph/loader.ts +10 -2
  144. package/src/scripts/code_graph/map.ts +195 -53
  145. package/src/scripts/code_graph/map_model.ts +113 -20
  146. package/src/scripts/code_graph/map_verify.ts +80 -5
  147. package/src/scripts/code_graph/map_viewer/viewer.css +18 -0
  148. package/src/scripts/code_graph/map_viewer/viewer.js +146 -15
  149. package/src/scripts/code_graph/passes.ts +173 -0
  150. package/src/scripts/code_graph/project_ref.ts +25 -0
  151. package/src/scripts/code_graph/projects_cli.ts +254 -0
  152. package/src/scripts/code_graph/providers/angular.ts +55 -0
  153. package/src/scripts/code_graph/providers/aspnetcore.ts +182 -0
  154. package/src/scripts/code_graph/providers/catalog.ts +299 -0
  155. package/src/scripts/code_graph/providers/django.ts +206 -0
  156. package/src/scripts/code_graph/providers/express.ts +25 -0
  157. package/src/scripts/code_graph/providers/fastapi.ts +49 -0
  158. package/src/scripts/code_graph/providers/flask.ts +99 -0
  159. package/src/scripts/code_graph/providers/go_http.ts +121 -0
  160. package/src/scripts/code_graph/providers/index.ts +103 -0
  161. package/src/scripts/code_graph/{laravel_overlay.ts → providers/laravel.ts} +113 -7
  162. package/src/scripts/code_graph/providers/nestjs.ts +34 -0
  163. package/src/scripts/code_graph/providers/nextjs.ts +91 -0
  164. package/src/scripts/code_graph/providers/nuxt.ts +55 -0
  165. package/src/scripts/code_graph/providers/rails.ts +204 -0
  166. package/src/scripts/code_graph/providers/react-router.ts +58 -0
  167. package/src/scripts/code_graph/providers/shared/detect.ts +78 -0
  168. package/src/scripts/code_graph/providers/shared/extractors.ts +249 -0
  169. package/src/scripts/code_graph/providers/shared/js.ts +252 -0
  170. package/src/scripts/code_graph/providers/shared/kit.ts +210 -0
  171. package/src/scripts/code_graph/providers/shared/php.ts +64 -0
  172. package/src/scripts/code_graph/providers/shared/python.ts +223 -0
  173. package/src/scripts/code_graph/providers/spring.ts +175 -0
  174. package/src/scripts/code_graph/providers/sqlalchemy.ts +62 -0
  175. package/src/scripts/code_graph/providers/sveltekit.ts +59 -0
  176. package/src/scripts/code_graph/providers/symfony.ts +142 -0
  177. package/src/scripts/code_graph/providers/typeorm.ts +58 -0
  178. package/src/scripts/code_graph/providers/types.ts +56 -0
  179. package/src/scripts/code_graph/providers/vue-router.ts +21 -0
  180. package/src/scripts/code_graph/providers/wordpress.ts +128 -0
  181. package/src/scripts/code_graph/query.ts +9 -9
  182. package/src/scripts/code_graph/refresh_cli.ts +81 -0
  183. package/src/scripts/code_graph/refresh_lock.ts +197 -0
  184. package/src/scripts/code_graph/repo_identity.ts +169 -0
  185. package/src/scripts/code_graph/resolution_tiers.ts +19 -7
  186. package/src/scripts/code_graph/scip_reader.ts +2 -2
  187. package/src/scripts/code_graph/sqlite_store.ts +31 -28
  188. package/src/scripts/code_graph/types.ts +79 -41
  189. package/src/scripts/code_graph/validate.ts +6 -8
  190. package/src/scripts/condense.ts +15 -15
  191. package/src/scripts/generate_settings_reference.ts +2 -6
  192. package/src/scripts/hook_chunks.mjs +11 -0
  193. package/src/scripts/hook_manifest.json +1 -1
  194. package/src/scripts/hook_manifest.yaml +43 -23
  195. package/src/scripts/hooks/code_graph_refresh_hook.ts +163 -0
  196. package/src/scripts/hooks/dispatch_hook.ts +6 -7
  197. package/src/scripts/hooks/host_semantics.ts +72 -0
  198. package/src/scripts/hooks/lazy_concern.ts +8 -0
  199. package/src/scripts/hooks/user_notice.ts +105 -0
  200. package/src/scripts/install-hooks.sh +54 -33
  201. package/src/scripts/install.ts +2 -6
  202. package/src/scripts/lint_harvest_provenance.ts +137 -11
  203. package/src/scripts/lint_hook_manifest.ts +2 -0
  204. package/src/scripts/lint_marketplace.ts +33 -0
  205. package/src/scripts/lint_mcp_registry_manifest.ts +36 -0
  206. package/src/scripts/mcp_server/consumer_tool_catalog.json +70 -15
  207. package/src/scripts/mcp_server/graph_tools.ts +165 -41
  208. package/src/scripts/network_sources.ts +24 -6
  209. package/src/scripts/release.ts +2 -5
  210. package/src/scripts/release_drill.ts +4 -1
  211. package/src/scripts/release_env.ts +17 -0
  212. package/src/scripts/release_mix_taxonomy.json +3 -0
  213. package/src/scripts/schemas/provenance-mechanism.schema.json +46 -0
  214. package/src/scripts/update_notice_hook.ts +75 -7
  215. package/src/server/schemas/settings.ts +42 -42
  216. package/src/shared/settingsSurface.ts +1 -1
  217. package/src/shared/userMd/schema.ts +1 -1
  218. package/src/shared/zodJsonSchema.ts +41 -0
  219. package/src/vendor/grammars/README.md +8 -6
  220. package/src/vendor/grammars/tree-sitter-c_sharp.wasm +0 -0
  221. package/src/vendor/grammars/tree-sitter-ruby.wasm +0 -0
  222. package/dist/cli-delegate/chunk-XIAPY44N.js +0 -4587
  223. package/dist/ui/assets/index-w70Ado09.js +0 -23
  224. /package/dist/cli-delegate/{chunk-FKYZOWVD.js → chunk-QBAFP67O.js} +0 -0
  225. /package/dist/cli-delegate/{chunk-BMGUGSY6.js → chunk-TQG6UMPC.js} +0 -0
@@ -0,0 +1,299 @@
1
+ /**
2
+ * The coverage catalog (ADR-298 § 6): for every provider, what it resolves and
3
+ * how far that is proven — so nothing reads as supported because it is
4
+ * detected. Stacks not covered yet stand here as `planned`, never silently
5
+ * absent.
6
+ *
7
+ * detected markers recognised, no relation resolved
8
+ * partial relations resolved and proven by synthetic positive and negative oracles
9
+ * verified partial, plus a reading on a real repository
10
+ * deep verified across every relation the framework has (none yet)
11
+ * planned on the coverage list, not built; the generic default still runs
12
+ */
13
+ import type { Relation } from '../types.js';
14
+
15
+ export type CoverageStatus = 'detected' | 'partial' | 'verified' | 'deep' | 'planned';
16
+
17
+ export interface CatalogRow {
18
+ /** The registered provider id, or a planned id. */
19
+ id: string;
20
+ /** What a person calls it. */
21
+ name: string;
22
+ language: string;
23
+ /** How it is detected (manifest entries or marker files). */
24
+ markers: string;
25
+ relations: readonly Relation[];
26
+ status: CoverageStatus;
27
+ /** The synthetic fixture its oracles run on. */
28
+ fixture: string | null;
29
+ /** The usage reading that put it on the coverage list. */
30
+ usage: string;
31
+ /** What it does not follow, said plainly. */
32
+ limits?: string;
33
+ }
34
+
35
+ const SO = 'Stack Overflow Developer Survey, web frameworks (year as recorded in the supplied plans; not re-read in this run)';
36
+
37
+ export const CATALOG: readonly CatalogRow[] = [
38
+ {
39
+ id: 'laravel',
40
+ name: 'Laravel',
41
+ language: 'PHP',
42
+ markers: 'composer.json requires laravel/framework or an illuminate/* package; an artisan file',
43
+ relations: ['routes-to', 'renders', 'includes', 'reads', 'migrates', 'persists', 'relates', 'uses'],
44
+ status: 'verified',
45
+ fixture: 'tests/scripts/code_graph_provider_laravel.test.ts (inline tree)',
46
+ usage: SO,
47
+ limits: 'Routes only under the package root routes/ directory; closures and string handlers get no target.',
48
+ },
49
+ {
50
+ id: 'express',
51
+ name: 'Express',
52
+ language: 'JavaScript / TypeScript',
53
+ markers: 'package.json depends on express',
54
+ relations: ['routes-to'],
55
+ status: 'partial',
56
+ fixture: 'tests/fixtures/code-graph/express',
57
+ usage: SO,
58
+ limits: 'A router mounted under a prefix in another file (app.use) is not followed.',
59
+ },
60
+ {
61
+ id: 'spring',
62
+ name: 'Spring Boot',
63
+ language: 'Java',
64
+ markers: 'pom.xml / build.gradle names spring-boot',
65
+ relations: ['routes-to', 'persists', 'relates', 'injects', 'renders'],
66
+ status: 'partial',
67
+ fixture: 'tests/fixtures/code-graph/spring',
68
+ usage: SO,
69
+ limits: '@Entity(name) is not read; a method @RequestMapping without method= is skipped; a view name built at runtime or held outside the file is not followed; Kotlin is not covered.',
70
+ },
71
+ {
72
+ id: 'go-http',
73
+ name: 'Go routers (net/http, Gin, Echo, chi)',
74
+ language: 'Go',
75
+ markers: 'a go.mod',
76
+ relations: ['routes-to'],
77
+ status: 'partial',
78
+ fixture: 'tests/fixtures/code-graph/go-http',
79
+ usage: 'Go is in the top ten languages of GitHub Octoverse; its routers are not in the web-framework survey',
80
+ limits: 'chi r.Route nesting, http.HandlerFunc(f) and handler variables are not followed.',
81
+ },
82
+ {
83
+ id: 'symfony',
84
+ name: 'Symfony',
85
+ language: 'PHP',
86
+ markers: 'composer.json requires symfony/framework-bundle or symfony/symfony',
87
+ relations: ['routes-to', 'renders', 'persists', 'migrates', 'relates', 'injects'],
88
+ status: 'partial',
89
+ fixture: 'tests/fixtures/code-graph/symfony',
90
+ usage: 'Below the survey top 20; the second PHP framework on the owner stack (Decision E4)',
91
+ limits: 'Twig includes are not read (templates are not code files); a route with several methods is declared once.',
92
+ },
93
+ {
94
+ id: 'wordpress',
95
+ name: 'WordPress',
96
+ language: 'PHP',
97
+ markers: 'wp-config.php; composer core packages or a wordpress-plugin / -theme package type',
98
+ relations: ['routes-to', 'renders'],
99
+ status: 'partial',
100
+ fixture: 'tests/fixtures/code-graph/wordpress',
101
+ usage: SO,
102
+ limits: 'Hooks are entry nodes HOOK <name> / SHORTCODE <name>; a plugin or theme with no composer.json is not detected.',
103
+ },
104
+ {
105
+ id: 'fastapi',
106
+ name: 'FastAPI',
107
+ language: 'Python',
108
+ markers: 'fastapi in pyproject.toml or requirements',
109
+ relations: ['routes-to', 'injects'],
110
+ status: 'partial',
111
+ fixture: 'tests/fixtures/code-graph/fastapi',
112
+ usage: SO,
113
+ limits: 'include_router(prefix=) from another file and add_api_route are not followed.',
114
+ },
115
+ {
116
+ id: 'flask',
117
+ name: 'Flask',
118
+ language: 'Python',
119
+ markers: 'flask in pyproject.toml or requirements',
120
+ relations: ['routes-to', 'renders'],
121
+ status: 'partial',
122
+ fixture: 'tests/fixtures/code-graph/flask',
123
+ usage: SO,
124
+ limits: 'add_url_rule and blueprints nested in blueprints are not followed.',
125
+ },
126
+ {
127
+ id: 'django',
128
+ name: 'Django',
129
+ language: 'Python',
130
+ markers: 'django in a manifest, or manage.py',
131
+ relations: ['routes-to', 'renders', 'persists', 'relates', 'migrates'],
132
+ status: 'partial',
133
+ fixture: 'tests/fixtures/code-graph/django',
134
+ usage: SO,
135
+ limits: 'Class-based view template_name and include() of an attribute (admin.site.urls) are not followed.',
136
+ },
137
+ {
138
+ id: 'sqlalchemy',
139
+ name: 'SQLAlchemy / SQLModel',
140
+ language: 'Python',
141
+ markers: 'sqlalchemy, flask-sqlalchemy or sqlmodel',
142
+ relations: ['persists', 'relates'],
143
+ status: 'partial',
144
+ fixture: 'tests/fixtures/code-graph/sqlalchemy',
145
+ usage: 'ORM of the Python providers above; not a web framework',
146
+ limits: 'Imperative Table(...), implicit Flask-SQLAlchemy names and relationship(lambda) are not drawn.',
147
+ },
148
+ {
149
+ id: 'react-router',
150
+ name: 'React Router',
151
+ language: 'JavaScript / TypeScript',
152
+ markers: 'package.json depends on react-router or react-router-dom',
153
+ relations: ['routes-to'],
154
+ status: 'partial',
155
+ fixture: 'tests/fixtures/code-graph/react-router',
156
+ usage: SO,
157
+ limits: 'Nested children paths are not joined to the parent path.',
158
+ },
159
+ {
160
+ id: 'nextjs',
161
+ name: 'Next.js',
162
+ language: 'JavaScript / TypeScript',
163
+ markers: 'package.json depends on next',
164
+ relations: ['routes-to', 'includes'],
165
+ status: 'partial',
166
+ fixture: 'tests/fixtures/code-graph/nextjs',
167
+ usage: SO,
168
+ limits: 'export { h as GET } re-exports are not followed.',
169
+ },
170
+ {
171
+ id: 'nestjs',
172
+ name: 'NestJS',
173
+ language: 'JavaScript / TypeScript',
174
+ markers: 'package.json depends on @nestjs/core',
175
+ relations: ['routes-to', 'injects'],
176
+ status: 'partial',
177
+ fixture: 'tests/fixtures/code-graph/nestjs',
178
+ usage: SO,
179
+ limits: '@Controller({ path }), path arrays, @Inject(token) and a global prefix are not followed.',
180
+ },
181
+ {
182
+ id: 'angular',
183
+ name: 'Angular',
184
+ language: 'JavaScript / TypeScript',
185
+ markers: 'package.json depends on @angular/core',
186
+ relations: ['routes-to', 'renders', 'injects'],
187
+ status: 'partial',
188
+ fixture: 'tests/fixtures/code-graph/angular',
189
+ usage: SO,
190
+ limits: 'loadChildren, child path joining and inline templates are not followed.',
191
+ },
192
+ {
193
+ id: 'vue-router',
194
+ name: 'Vue Router',
195
+ language: 'JavaScript / TypeScript',
196
+ markers: 'package.json depends on vue-router',
197
+ relations: ['routes-to'],
198
+ status: 'partial',
199
+ fixture: 'tests/fixtures/code-graph/vue-router',
200
+ usage: SO,
201
+ limits: 'Named views, alias imports and child paths are not followed.',
202
+ },
203
+ {
204
+ id: 'nuxt',
205
+ name: 'Nuxt',
206
+ language: 'JavaScript / TypeScript',
207
+ markers: 'package.json depends on nuxt',
208
+ relations: ['routes-to'],
209
+ status: 'partial',
210
+ fixture: 'tests/fixtures/code-graph/nuxt',
211
+ usage: SO,
212
+ limits: 'definePageMeta path overrides are not read.',
213
+ },
214
+ {
215
+ id: 'sveltekit',
216
+ name: 'SvelteKit',
217
+ language: 'JavaScript / TypeScript',
218
+ markers: 'package.json depends on @sveltejs/kit',
219
+ relations: ['routes-to', 'includes'],
220
+ status: 'partial',
221
+ fixture: 'tests/fixtures/code-graph/sveltekit',
222
+ usage: SO,
223
+ limits: '+page.server.ts, layout resets and a custom routes directory are not followed.',
224
+ },
225
+ {
226
+ id: 'typeorm',
227
+ name: 'TypeORM',
228
+ language: 'TypeScript',
229
+ markers: 'package.json depends on typeorm',
230
+ relations: ['persists', 'relates', 'migrates'],
231
+ status: 'partial',
232
+ fixture: 'tests/fixtures/code-graph/typeorm',
233
+ usage: 'ORM of the TypeScript providers above; not a web framework',
234
+ limits: 'Custom naming strategies, EntitySchema and string relation targets are not followed.',
235
+ },
236
+ {
237
+ id: 'aspnetcore',
238
+ name: 'ASP.NET Core',
239
+ language: 'C#',
240
+ markers: 'a *.csproj with the Web SDK or a Microsoft.AspNetCore package reference',
241
+ relations: ['routes-to', 'persists', 'renders', 'injects'],
242
+ status: 'partial',
243
+ fixture: 'tests/fixtures/code-graph/aspnetcore',
244
+ usage: SO,
245
+ limits: 'MapControllerRoute tables, a view name held in a variable and primary constructors are not followed.',
246
+ },
247
+ {
248
+ id: 'rails',
249
+ name: 'Ruby on Rails',
250
+ language: 'Ruby',
251
+ markers: 'a Gemfile declaring the rails gem',
252
+ relations: ['routes-to', 'persists', 'relates', 'migrates', 'renders'],
253
+ status: 'partial',
254
+ fixture: 'tests/fixtures/code-graph/rails',
255
+ usage: SO,
256
+ limits: 'resource (singular), nested resources, scope/concerns, an explicit render, config/initializers/inflections and models outside app/models are not followed.',
257
+ },
258
+ ...(
259
+ [
260
+ ['kotlin', 'Kotlin', 'Kotlin'],
261
+ ['rust', 'Rust', 'Rust'],
262
+ ['c-cpp', 'C / C++', 'C / C++'],
263
+ ['swift', 'Swift', 'Swift'],
264
+ ['dart', 'Dart', 'Dart'],
265
+ ['sql', 'SQL schema files', 'SQL'],
266
+ ['react-native', 'React Native / Expo', 'JavaScript / TypeScript'],
267
+ ['flutter', 'Flutter', 'Dart'],
268
+ ['swiftui', 'SwiftUI', 'Swift'],
269
+ ['jetpack-compose', 'Jetpack Compose', 'Kotlin'],
270
+ ['blazor', 'Blazor', 'C#'],
271
+ ['astro', 'Astro', 'JavaScript / TypeScript'],
272
+ ['fastify', 'Fastify', 'JavaScript / TypeScript'],
273
+ ['hono', 'Hono', 'JavaScript / TypeScript'],
274
+ ['remix', 'Remix', 'JavaScript / TypeScript'],
275
+ ['drupal', 'Drupal', 'PHP'],
276
+ ['quarkus', 'Quarkus', 'Java'],
277
+ ['micronaut', 'Micronaut', 'Java'],
278
+ ['prisma', 'Prisma', 'Prisma schema'],
279
+ ['drizzle', 'Drizzle', 'TypeScript'],
280
+ ['infrastructure', 'Docker, Kubernetes, Terraform, CI', 'YAML / HCL'],
281
+ ] as const
282
+ ).map(
283
+ ([id, name, language]): CatalogRow => ({
284
+ id,
285
+ name,
286
+ language,
287
+ markers: '—',
288
+ relations: [],
289
+ status: 'planned',
290
+ fixture: null,
291
+ usage: 'coverage list in road-to-graph-providers-for-every-stack (kill register K7)',
292
+ }),
293
+ ),
294
+ ];
295
+
296
+ /** The catalog row for a provider id, if any. */
297
+ export function catalogRow(id: string): CatalogRow | undefined {
298
+ return CATALOG.find((r) => r.id === id);
299
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Django: the links a Django project makes by convention.
3
+ *
4
+ * urls.py path("x/", views.fn) · re_path(r"^x/$", V.as_view()) route --routes-to--> the view function or class
5
+ * any view render(request, "app/x.html") --renders--> templates/ or <app>/templates/
6
+ * models.py class X(models.Model) --persists--> Meta.db_table, else <app>_<x>
7
+ * models.py ForeignKey · ManyToManyField · OneToOneField --relates--> the model named
8
+ * migrations migrations.CreateModel(name="X") --migrates--> the same table
9
+ *
10
+ * A view is resolved the way the urls file names it: a same-file or imported
11
+ * symbol, or `module.member` through a module the file imports. `include(...)`
12
+ * of a dotted string, a `(module, namespace)` tuple or an imported module is
13
+ * followed: an included module's routes carry every prefix chain that reaches
14
+ * it from a URLconf nothing includes, and appear under no other path. An
15
+ * inline lambda gets a route with no target. A template found in two template
16
+ * folders, or built at runtime, draws nothing. The app label is the model file's
17
+ * directory (its parent for a `models/` package) unless `Meta.app_label` says
18
+ * otherwise; abstract models get no table; a `"self"` relation draws no edge.
19
+ */
20
+ import * as path from 'node:path';
21
+
22
+ import type { CodeNode } from '../types.js';
23
+ import { markerRoots, pythonRoots, union } from './shared/detect.js';
24
+ import { joinRoute, templateRoots } from './shared/extractors.js';
25
+ import { lineIndex, makeKit } from './shared/kit.js';
26
+ import { balanced, classBases, importedModules, kwargString, resolveModule, spanText, splitArgs, stringLiteral } from './shared/python.js';
27
+ import type { ConventionProvider } from './types.js';
28
+
29
+ const RELATION = /\b(?:models\.)?(?:ForeignKey|ManyToManyField|OneToOneField)\s*\(/g;
30
+
31
+ function appLabelOf(file: string): string {
32
+ const dir = path.posix.dirname(file);
33
+ const base = path.posix.basename(dir);
34
+ return base === 'models' ? path.posix.basename(path.posix.dirname(dir)) : base;
35
+ }
36
+
37
+ interface Model {
38
+ node: CodeNode;
39
+ app: string;
40
+ table: string | null;
41
+ }
42
+
43
+ export const djangoProvider: ConventionProvider = {
44
+ id: 'django',
45
+ markers: ['manage.py'],
46
+ detect: (probe) => union(pythonRoots(probe, ['django']), markerRoots(probe, 'manage.py')),
47
+ build: (input) => {
48
+ const kit = makeKit(input);
49
+ const inventory = new Set(kit.paths);
50
+ const py = kit.filesWithExt('.py');
51
+
52
+ // Routes. Every URLconf is read once; `include()` links a parent to the
53
+ // module it names, and each route is emitted once per prefix chain that
54
+ // reaches its file from a URLconf nothing includes.
55
+ interface UrlRoute { path: string; view: string; line: number }
56
+ interface UrlConf { file: string; routes: UrlRoute[]; includes: Array<{ path: string; child: string }> }
57
+ const confs = new Map<string, UrlConf & { resolve: (expr: string) => string | null }>();
58
+ const pattern = (literal: string, re: boolean): string => (re ? literal.replace(/^\^/, '').replace(/\$$/, '') : literal);
59
+ for (const f of py.filter((x) => path.posix.basename(x.path) === 'urls.py' || /^urlpatterns\s*[:=]/m.test(x.source))) {
60
+ const at = lineIndex(f.source);
61
+ const modules = importedModules(f.path, f.source, kit.roots, inventory);
62
+ const resolve = (expr: string): string | null => {
63
+ const parts = expr.replace(/\.as_view\s*\([\s\S]*\)$/, '').split('.');
64
+ if (!parts.every((p) => /^[A-Za-z_]\w*$/.test(p))) return null;
65
+ if (parts.length === 1) return kit.resolveName(f.path, parts[0] as string);
66
+ if (parts.length !== 2) return null;
67
+ const [head, member] = parts as [string, string];
68
+ const mod = modules.get(head);
69
+ if (mod && kit.nodeById.has(`${mod}#${member}`)) return `${mod}#${member}`;
70
+ const cls = kit.resolveName(f.path, head);
71
+ return cls ? kit.methodOf(cls, member) : null;
72
+ };
73
+ /** The module an `include(...)` argument names: `"a.urls"`, `("a.urls", "ns")`, or an imported module. */
74
+ const includeTarget = (view: string): string | null => {
75
+ const open = view.indexOf('(');
76
+ let first = splitArgs(balanced(view, open))[0] ?? '';
77
+ if (first.startsWith('(') || first.startsWith('[')) first = splitArgs(balanced(first, 0))[0] ?? '';
78
+ const dotted = stringLiteral(first);
79
+ if (dotted !== null) return resolveModule(f.path, dotted, kit.roots, inventory);
80
+ return /^\w+$/.test(first) ? (modules.get(first) ?? null) : null;
81
+ };
82
+ const conf: UrlConf & { resolve: typeof resolve } = { file: f.path, routes: [], includes: [], resolve };
83
+ for (const m of f.source.matchAll(/\b(path|re_path)\s*\(/g)) {
84
+ const args = splitArgs(balanced(f.source, (m.index as number) + m[0].length - 1));
85
+ const literal = stringLiteral(args[0] ?? '');
86
+ const view = args[1];
87
+ if (literal === null || view === undefined) continue;
88
+ const routePath = pattern(literal, m[1] === 're_path');
89
+ if (/^include\s*\(/.test(view)) {
90
+ const child = includeTarget(view);
91
+ if (child) conf.includes.push({ path: routePath, child });
92
+ continue;
93
+ }
94
+ conf.routes.push({ path: routePath, view, line: at(m.index as number) });
95
+ }
96
+ confs.set(f.path, conf);
97
+ }
98
+ const included = new Set([...confs.values()].flatMap((c) => c.includes.map((i) => i.child)));
99
+ const prefixes = new Map<string, Set<string>>();
100
+ const walk = (file: string, prefix: string, stack: readonly string[]): void => {
101
+ const conf = confs.get(file);
102
+ if (!conf || stack.includes(file)) return;
103
+ const seen = prefixes.get(file) ?? new Set<string>();
104
+ if (seen.has(prefix)) return;
105
+ seen.add(prefix);
106
+ prefixes.set(file, seen);
107
+ for (const inc of conf.includes) walk(inc.child, joinRoute(prefix, inc.path), [...stack, file]);
108
+ };
109
+ for (const file of confs.keys()) if (!included.has(file)) walk(file, '', []);
110
+ for (const conf of confs.values()) {
111
+ for (const prefix of prefixes.get(conf.file) ?? []) {
112
+ for (const r of conf.routes) {
113
+ const id = kit.route(conf.file, 'ANY', joinRoute(prefix, r.path), r.line);
114
+ const target = conf.resolve(r.view);
115
+ if (target) kit.emit(id, target, 'routes-to', 'route-table');
116
+ }
117
+ }
118
+ }
119
+
120
+ // Templates: the project-level folder and every app's own.
121
+ const tplDirs = new Set<string>(['templates']);
122
+ for (const p of kit.paths) {
123
+ for (const r of kit.roots) {
124
+ const rel = r === '' ? p : p.startsWith(`${r}/`) ? p.slice(r.length + 1) : null;
125
+ const hit = rel === null ? null : /^([^/]+)\/templates\//.exec(rel);
126
+ if (hit) tplDirs.add(`${hit[1]}/templates`);
127
+ }
128
+ }
129
+ templateRoots(kit, {
130
+ exts: ['.py'],
131
+ call: /\b(?:render\(\s*\w+\s*,|render_to_string\(|get_template\()\s*(['"])(?<name>[^'"]+)\1/g,
132
+ roots: [...tplDirs].sort(),
133
+ extensions: [],
134
+ });
135
+
136
+ // Models.
137
+ const models = new Map<string, Model>();
138
+ const isDjangoFile = (src: string): boolean => /\bfrom\s+django\b|\bimport\s+django\b/.test(src);
139
+ for (let changed = true; changed; ) {
140
+ changed = false;
141
+ for (const f of py) {
142
+ if (!isDjangoFile(f.source)) continue;
143
+ for (const n of kit.nodesInFile(f.path)) {
144
+ if (n.kind !== 'class' || models.has(n.id)) continue;
145
+ const bases = classBases(f.source, n);
146
+ const isModel = bases.some((b) => {
147
+ if (/^(?:models\.)?Model$/.test(b)) return true;
148
+ const r = /^\w+$/.test(b) ? kit.resolveName(f.path, b) : null;
149
+ return r !== null && models.has(r);
150
+ });
151
+ if (!isModel) continue;
152
+ const region = spanText(f.source, n);
153
+ const app = /\bapp_label\s*=\s*(['"])(\w+)\1/.exec(region)?.[2] ?? appLabelOf(f.path);
154
+ const abstract = /\babstract\s*=\s*True\b/.test(region);
155
+ const table = abstract ? null : (/\bdb_table\s*=\s*[rbu]?(['"])([^'"]+)\1/.exec(region)?.[2] ?? `${app}_${n.label.toLowerCase()}`);
156
+ models.set(n.id, { node: n, app, table });
157
+ changed = true;
158
+ }
159
+ }
160
+ }
161
+ const sources = new Map(py.map((f) => [f.path, f.source] as const));
162
+ for (const m of models.values()) {
163
+ if (m.table) kit.emit(m.node.id, kit.table(m.table, m.node.source_file, m.node.source_location[0] as number), 'persists', 'schema-call');
164
+ }
165
+ const modelNamed = (app: string | null, label: string, file: string): string | null => {
166
+ const same = [...models.values()].filter((x) => x.node.label === label && x.node.source_file === file);
167
+ if (same.length === 1) return (same[0] as Model).node.id;
168
+ const inApp = [...models.values()].filter((x) => x.node.label === label && (app === null || x.app === app));
169
+ return inApp.length === 1 ? (inApp[0] as Model).node.id : null;
170
+ };
171
+ for (const m of models.values()) {
172
+ const src = sources.get(m.node.source_file) as string;
173
+ const region = spanText(src, m.node);
174
+ for (const r of region.matchAll(RELATION)) {
175
+ const first = splitArgs(balanced(region, (r.index as number) + r[0].length - 1))[0] ?? '';
176
+ const lit = stringLiteral(first);
177
+ let target: string | null = null;
178
+ if (lit !== null) {
179
+ if (lit === 'self') continue;
180
+ const dot = lit.lastIndexOf('.');
181
+ target = dot >= 0 ? modelNamed(lit.slice(0, dot), lit.slice(dot + 1), m.node.source_file) : modelNamed(m.app, lit, m.node.source_file);
182
+ } else if (/^\w+$/.test(first)) {
183
+ const resolved = kit.resolveName(m.node.source_file, first);
184
+ target = resolved && models.has(resolved) ? resolved : null;
185
+ }
186
+ if (target && target !== m.node.id) kit.emit(m.node.id, target, 'relates', 'orm-relation');
187
+ }
188
+ }
189
+
190
+ // Migrations.
191
+ for (const f of py.filter((x) => /(^|\/)migrations\/[^/]+\.py$/.test(x.path))) {
192
+ const at = lineIndex(f.source);
193
+ const app = path.posix.basename(path.posix.dirname(path.posix.dirname(f.path)));
194
+ for (const c of f.source.matchAll(/\bmigrations\.CreateModel\s*\(/g)) {
195
+ const args = balanced(f.source, (c.index as number) + c[0].length - 1);
196
+ const name = kwargString(args, 'name') ?? stringLiteral(splitArgs(args)[0] ?? '');
197
+ if (!name) continue;
198
+ const explicit = /(['"])db_table\1\s*:\s*(['"])([^'"]+)\2/.exec(args)?.[3];
199
+ const model = [...models.values()].find((x) => x.app === app && x.node.label === name);
200
+ const table = explicit ?? model?.table ?? `${app}_${name.toLowerCase()}`;
201
+ kit.emit(f.path, kit.table(table, f.path, at(c.index as number)), 'migrates', 'schema-call');
202
+ }
203
+ }
204
+ return kit.result();
205
+ },
206
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Express: `app.get('/x', handler)` and `router.post('/x', mw, handler)` →
3
+ * route --routes-to--> the handler the file names (same file or imported).
4
+ * A receiver must read as an app or a router, so `cache.get('/x')` is no route;
5
+ * an inline handler gets a route with no target. Mounting a router under a
6
+ * prefix in another file (`app.use('/api', router)`) is not followed.
7
+ */
8
+ import { npmRoots } from './shared/detect.js';
9
+ import { callRoutes } from './shared/extractors.js';
10
+ import { makeKit } from './shared/kit.js';
11
+ import type { ConventionProvider } from './types.js';
12
+
13
+ export const expressProvider: ConventionProvider = {
14
+ id: 'express',
15
+ detect: (probe) => npmRoots(probe, ['express']),
16
+ build: (input) => {
17
+ const kit = makeKit(input);
18
+ callRoutes(kit, {
19
+ exts: ['.ts', '.js', '.mjs', '.cjs'],
20
+ call: /\b(?:app|router|server|api|\w+Router)\s*\.\s*(?<verb>get|post|put|patch|delete|options|head|all)\(\s*(['"`])(?<path>\/[^'"`]*)\2\s*,(?=(?<rest>[\s\S]{0,300}))/g,
21
+ verb: (m) => (m.groups?.['verb'] === 'all' ? 'ANY' : (m.groups?.['verb'] as string)),
22
+ });
23
+ return kit.result();
24
+ },
25
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * FastAPI: `@app.get("/x")` / `@router.post("/x")` → route --routes-to--> the
3
+ * decorated function, where the receiver is a `FastAPI(...)` or `APIRouter(...)`
4
+ * instance assigned in the same file (so `@cache.get("/x")` is no route). An
5
+ * `APIRouter(prefix="/api")` prefix applies to that router's routes.
6
+ *
7
+ * `Depends(fn)` in a parameter default → the function --injects--> fn, resolved
8
+ * the way the file names it (same file, then imports); `Depends(lambda: …)` and
9
+ * a bare `Depends()` get no edge.
10
+ *
11
+ * Not followed: `app.include_router(router, prefix=...)` mounting a router from
12
+ * another file, and routes registered by `app.add_api_route(...)`.
13
+ */
14
+ import { pythonRoots } from './shared/detect.js';
15
+ import { joinRoute } from './shared/extractors.js';
16
+ import { makeKit } from './shared/kit.js';
17
+ import { assignedCalls, kwargString, paramsOf, routeDecorators } from './shared/python.js';
18
+ import type { ConventionProvider } from './types.js';
19
+
20
+ const VERBS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'api_route', 'websocket'] as const;
21
+
22
+ export const fastapiProvider: ConventionProvider = {
23
+ id: 'fastapi',
24
+ detect: (probe) => pythonRoots(probe, ['fastapi']),
25
+ build: (input) => {
26
+ const kit = makeKit(input);
27
+ for (const f of kit.filesWithExt('.py')) {
28
+ const instances = assignedCalls(f.source, ['FastAPI', 'APIRouter']);
29
+ if (instances.size > 0) {
30
+ for (const d of routeDecorators(kit, f, VERBS)) {
31
+ const inst = instances.get(d.receiver);
32
+ if (!inst || d.path === null) continue;
33
+ const verb = d.method === 'api_route' ? 'ANY' : d.method === 'websocket' ? 'WS' : d.method.toUpperCase();
34
+ const id = kit.route(f.path, verb, joinRoute(kwargString(inst.args, 'prefix'), d.path), d.line);
35
+ if (d.handler) kit.emit(id, d.handler.id, 'routes-to', 'route-table');
36
+ }
37
+ }
38
+ if (!f.source.includes('Depends(')) continue;
39
+ for (const n of kit.nodesInFile(f.path)) {
40
+ if (n.kind !== 'function' && n.kind !== 'method') continue;
41
+ for (const m of paramsOf(f.source, n).matchAll(/\bDepends\(\s*([A-Za-z_]\w*)\s*[,)]/g)) {
42
+ const target = kit.resolveName(f.path, m[1] as string);
43
+ if (target && target !== n.id) kit.emit(n.id, target, 'injects', 'di-binding');
44
+ }
45
+ }
46
+ }
47
+ return kit.result();
48
+ },
49
+ };