@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.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/skills/install-agent-config/SKILL.md +9 -9
- package/CHANGELOG.md +167 -193
- package/CREDITS.md +2 -2
- package/README.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +16 -12
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/check_memory.ts +1 -1
- package/dist/cli/commands/recordTriggerEval.js +1 -1
- package/dist/cli/registry.js +2 -2
- package/dist/cli-delegate/{chunk-KR5ZIFPO.js → chunk-5AYJGUA3.js} +1 -3
- package/dist/cli-delegate/chunk-DIFZBP4G.js +1336 -0
- package/dist/cli-delegate/chunk-E7ACTJTJ.js +6792 -0
- package/dist/cli-delegate/{chunk-VYZGJLCA.js → chunk-RKNT4LIK.js} +22 -1286
- package/dist/cli-delegate/{chunk-BZJ4Y3CI.js → chunk-WY5ZZPGL.js} +2 -2
- package/dist/cli-delegate/{chunk-CMZEKSNN.js → chunk-X6LZ7W5A.js} +73 -10
- package/dist/cli-delegate/cmd_conformance.js +190 -192
- package/dist/cli-delegate/cmd_doctor.js +5 -5
- package/dist/cli-delegate/cmd_doctor_neighbours.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +8 -4
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_update.js +24 -13
- package/dist/cli-delegate/cmd_upgrade.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dist-25AZAHOP.js +3240 -0
- package/dist/cli-delegate/dist-P7MFPMF3.js +411 -0
- package/dist/cli-delegate/dist-XAS5ZLQB.js +6 -0
- package/dist/cli-delegate/{upgrade_unattended-VVL6JLSX.js → upgrade_unattended-QCWOKTUN.js} +178 -163
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +3 -3
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/code_graph_refresh.chunk.cjs +443 -0
- package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -0
- package/dist/hooks/dispatch.js +1001 -938
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/egress_engine.chunk.cjs +57 -7
- package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
- package/dist/hooks/update_notice.chunk.cjs +914 -0
- package/dist/hooks/update_notice.chunk.cjs.sha256 +1 -0
- package/dist/install/install.mjs +6974 -6037
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -0
- package/dist/mcp/registry-manifest.json +3 -2
- package/dist/mcp/server.json +13 -4
- package/dist/mcp/server.mjs +71202 -63282
- package/dist/scripts/_lib/egress_rules.js +8 -2
- package/dist/scripts/condense.js +18 -15
- package/dist/server/routes/discovery.js +1 -1
- package/dist/server/routes/install.js +1 -1
- package/dist/server/routes/ping.js +1 -1
- package/dist/server/routes/schema.js +4 -4
- package/dist/server/routes/settings.js +2 -6
- package/dist/server/routes/wizard.js +6 -6
- package/dist/server/schemas/settings.js +42 -42
- package/dist/shared/settingsSurface.js +1 -1
- package/dist/shared/userMd/schema.js +1 -1
- package/dist/shared/zodJsonSchema.js +47 -0
- package/dist/ui/assets/index-B0bcIgi0.js +77 -0
- package/dist/ui/index.html +1 -1
- package/package.json +6 -3
- package/src/agent-src/templates/scripts/check_memory.ts +1 -1
- package/src/config/ci-local-parity.yml +11 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +9 -8
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/gitignore-block.txt +6 -5
- package/src/config/hook-bundle-budget.json +46 -6
- package/src/config/pack-size-budget.json +12 -1
- package/src/config/packed-binary-manifest.json +18 -0
- package/src/config/sigstore/npm_keys.json +50 -0
- package/src/config/sigstore/trust-material.json +9 -0
- package/src/config/sigstore/trusted_root.json +126 -0
- package/src/config/skill-body-budget.json +7 -1
- package/src/scripts/_cli/cmd_settings_set.ts +3 -2
- package/src/scripts/_cli/cmd_update.ts +48 -20
- package/src/scripts/_cli/cmd_upgrade.ts +7 -1
- package/src/scripts/_cli/upgrade_unattended.ts +90 -76
- package/src/scripts/_dispatch.bash +4 -3
- package/src/scripts/_lib/confusables.ts +0 -19
- package/src/scripts/_lib/egress_normalize.ts +53 -23
- package/src/scripts/_lib/egress_rules.ts +8 -2
- package/src/scripts/_lib/egress_text.ts +5 -3
- package/src/scripts/_lib/egress_token.ts +7 -5
- package/src/scripts/_lib/gate_collector_experimental.ts +633 -0
- package/src/scripts/_lib/git_common_dir.ts +10 -0
- package/src/scripts/_lib/graph_feeder_record.ts +3 -2
- package/src/scripts/_lib/install_reach_checks.ts +32 -2
- package/src/scripts/_lib/lineage_backfill.ts +136 -0
- package/src/scripts/_lib/lineage_ledger.ts +211 -0
- package/src/scripts/_lib/lineage_map.ts +55 -0
- package/src/scripts/_lib/lineage_query.ts +140 -0
- package/src/scripts/_lib/lineage_seed.ts +116 -0
- package/src/scripts/_lib/lineage_watch.ts +249 -0
- package/src/scripts/_lib/link_crypto.ts +8 -4
- package/src/scripts/_lib/listing_manifests.ts +85 -0
- package/src/scripts/_lib/neighbour_census.ts +1 -0
- package/src/scripts/_lib/production_reach.ts +3 -3
- package/src/scripts/_lib/project_registry.ts +227 -0
- package/src/scripts/_lib/provenance_verify.ts +14 -242
- package/src/scripts/_lib/release_settings_surface.ts +2 -6
- package/src/scripts/_lib/sigstore_provenance.ts +177 -0
- package/src/scripts/_lib/source_id.ts +48 -0
- package/src/scripts/_lib/source_shape.ts +4 -0
- package/src/scripts/_lib/unattended_gates.ts +6 -1
- package/src/scripts/adoption_status.ts +231 -27
- package/src/scripts/advisories.ts +26 -10
- package/src/scripts/ai_council/council_egress.ts +5 -6
- package/src/scripts/bench_graph_feeder_latency.ts +10 -3
- package/src/scripts/build_mcp_registry_manifest.ts +23 -12
- package/src/scripts/check_dependency_floors.ts +14 -0
- package/src/scripts/check_egress_mutations.ts +1 -0
- package/src/scripts/check_release_manifest_content.ts +17 -2
- package/src/scripts/check_release_pr_shape.ts +5 -0
- package/src/scripts/code_graph/build.ts +145 -45
- package/src/scripts/code_graph/cli.ts +382 -35
- package/src/scripts/code_graph/conventions_default.ts +247 -0
- package/src/scripts/code_graph/detect.ts +5 -5
- package/src/scripts/code_graph/estate_cli.ts +135 -0
- package/src/scripts/code_graph/estate_index.ts +394 -0
- package/src/scripts/code_graph/estate_manifests.ts +223 -0
- package/src/scripts/code_graph/estate_map.ts +141 -0
- package/src/scripts/code_graph/extract.ts +8 -2
- package/src/scripts/code_graph/extract_more.ts +241 -2
- package/src/scripts/code_graph/graph_budget.ts +158 -0
- package/src/scripts/code_graph/graph_home.ts +80 -0
- package/src/scripts/code_graph/lineage_cli.ts +315 -0
- package/src/scripts/code_graph/loader.ts +10 -2
- package/src/scripts/code_graph/map.ts +195 -53
- package/src/scripts/code_graph/map_model.ts +113 -20
- package/src/scripts/code_graph/map_verify.ts +80 -5
- package/src/scripts/code_graph/map_viewer/viewer.css +18 -0
- package/src/scripts/code_graph/map_viewer/viewer.js +146 -15
- package/src/scripts/code_graph/passes.ts +173 -0
- package/src/scripts/code_graph/project_ref.ts +25 -0
- package/src/scripts/code_graph/projects_cli.ts +254 -0
- package/src/scripts/code_graph/providers/angular.ts +55 -0
- package/src/scripts/code_graph/providers/aspnetcore.ts +182 -0
- package/src/scripts/code_graph/providers/catalog.ts +299 -0
- package/src/scripts/code_graph/providers/django.ts +206 -0
- package/src/scripts/code_graph/providers/express.ts +25 -0
- package/src/scripts/code_graph/providers/fastapi.ts +49 -0
- package/src/scripts/code_graph/providers/flask.ts +99 -0
- package/src/scripts/code_graph/providers/go_http.ts +121 -0
- package/src/scripts/code_graph/providers/index.ts +103 -0
- package/src/scripts/code_graph/{laravel_overlay.ts → providers/laravel.ts} +113 -7
- package/src/scripts/code_graph/providers/nestjs.ts +34 -0
- package/src/scripts/code_graph/providers/nextjs.ts +91 -0
- package/src/scripts/code_graph/providers/nuxt.ts +55 -0
- package/src/scripts/code_graph/providers/rails.ts +204 -0
- package/src/scripts/code_graph/providers/react-router.ts +58 -0
- package/src/scripts/code_graph/providers/shared/detect.ts +78 -0
- package/src/scripts/code_graph/providers/shared/extractors.ts +249 -0
- package/src/scripts/code_graph/providers/shared/js.ts +252 -0
- package/src/scripts/code_graph/providers/shared/kit.ts +210 -0
- package/src/scripts/code_graph/providers/shared/php.ts +64 -0
- package/src/scripts/code_graph/providers/shared/python.ts +223 -0
- package/src/scripts/code_graph/providers/spring.ts +175 -0
- package/src/scripts/code_graph/providers/sqlalchemy.ts +62 -0
- package/src/scripts/code_graph/providers/sveltekit.ts +59 -0
- package/src/scripts/code_graph/providers/symfony.ts +142 -0
- package/src/scripts/code_graph/providers/typeorm.ts +58 -0
- package/src/scripts/code_graph/providers/types.ts +56 -0
- package/src/scripts/code_graph/providers/vue-router.ts +21 -0
- package/src/scripts/code_graph/providers/wordpress.ts +128 -0
- package/src/scripts/code_graph/query.ts +9 -9
- package/src/scripts/code_graph/refresh_cli.ts +81 -0
- package/src/scripts/code_graph/refresh_lock.ts +197 -0
- package/src/scripts/code_graph/repo_identity.ts +169 -0
- package/src/scripts/code_graph/resolution_tiers.ts +19 -7
- package/src/scripts/code_graph/scip_reader.ts +2 -2
- package/src/scripts/code_graph/sqlite_store.ts +31 -28
- package/src/scripts/code_graph/types.ts +79 -41
- package/src/scripts/code_graph/validate.ts +6 -8
- package/src/scripts/condense.ts +15 -15
- package/src/scripts/generate_settings_reference.ts +2 -6
- package/src/scripts/hook_chunks.mjs +11 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +43 -23
- package/src/scripts/hooks/code_graph_refresh_hook.ts +163 -0
- package/src/scripts/hooks/dispatch_hook.ts +6 -7
- package/src/scripts/hooks/host_semantics.ts +72 -0
- package/src/scripts/hooks/lazy_concern.ts +8 -0
- package/src/scripts/hooks/user_notice.ts +105 -0
- package/src/scripts/install-hooks.sh +54 -33
- package/src/scripts/install.ts +2 -6
- package/src/scripts/lint_harvest_provenance.ts +137 -11
- package/src/scripts/lint_hook_manifest.ts +2 -0
- package/src/scripts/lint_marketplace.ts +33 -0
- package/src/scripts/lint_mcp_registry_manifest.ts +36 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +70 -15
- package/src/scripts/mcp_server/graph_tools.ts +165 -41
- package/src/scripts/network_sources.ts +24 -6
- package/src/scripts/release.ts +2 -5
- package/src/scripts/release_drill.ts +4 -1
- package/src/scripts/release_env.ts +17 -0
- package/src/scripts/release_mix_taxonomy.json +3 -0
- package/src/scripts/schemas/provenance-mechanism.schema.json +46 -0
- package/src/scripts/update_notice_hook.ts +75 -7
- package/src/server/schemas/settings.ts +42 -42
- package/src/shared/settingsSurface.ts +1 -1
- package/src/shared/userMd/schema.ts +1 -1
- package/src/shared/zodJsonSchema.ts +41 -0
- package/src/vendor/grammars/README.md +8 -6
- package/src/vendor/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/cli-delegate/chunk-XIAPY44N.js +0 -4587
- package/dist/ui/assets/index-w70Ado09.js +0 -23
- /package/dist/cli-delegate/{chunk-FKYZOWVD.js → chunk-QBAFP67O.js} +0 -0
- /package/dist/cli-delegate/{chunk-BMGUGSY6.js → chunk-TQG6UMPC.js} +0 -0
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
emptyLedger,
|
|
64
64
|
validateAcceptance,
|
|
65
65
|
} from './_lib/advisory_verdicts.js';
|
|
66
|
+
import { readRegistry, recordProject } from './_lib/project_registry.js';
|
|
66
67
|
import { event4u_root } from './_lib/user_global_paths.js';
|
|
67
68
|
import {
|
|
68
69
|
advance,
|
|
@@ -363,25 +364,40 @@ export function markOwnActionsPrivate<T extends { entries: Array<{ ecosystem: st
|
|
|
363
364
|
return inv;
|
|
364
365
|
}
|
|
365
366
|
|
|
366
|
-
/**
|
|
367
|
+
/**
|
|
368
|
+
* The project list is the shared user-global registry (`_lib/project_registry`,
|
|
369
|
+
* ADR-299): a sibling of the advisories machine dir, so an injected dir keeps
|
|
370
|
+
* its registry beside it. Roots and fingerprints only (5.4).
|
|
371
|
+
*/
|
|
372
|
+
function registryDirFor(mdir: string): string {
|
|
373
|
+
return path.join(path.dirname(mdir), 'projects');
|
|
374
|
+
}
|
|
375
|
+
|
|
367
376
|
function recordProjects(mdir: string, rows: Array<{ root: string; fingerprint: string }>, now: Date): void {
|
|
377
|
+
for (const r of rows) recordProject(r.root, { fingerprint: r.fingerprint }, { dir: registryDirFor(mdir), now });
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** The pre-registry `projects.json`, imported once and then moved aside. */
|
|
381
|
+
function migrateLegacyProjects(mdir: string): void {
|
|
368
382
|
const file = path.join(mdir, 'projects.json');
|
|
369
|
-
let doc: Record<string, { fingerprint
|
|
383
|
+
let doc: Record<string, { fingerprint?: string; at?: string }>;
|
|
370
384
|
try {
|
|
371
385
|
doc = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
372
386
|
} catch {
|
|
373
|
-
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
for (const [root, e] of Object.entries(doc ?? {})) {
|
|
390
|
+
const at = typeof e?.at === 'string' && !Number.isNaN(Date.parse(e.at)) ? new Date(e.at) : new Date();
|
|
391
|
+
recordProject(root, { fingerprint: typeof e?.fingerprint === 'string' ? e.fingerprint : null }, { dir: registryDirFor(mdir), now: at });
|
|
374
392
|
}
|
|
375
|
-
|
|
376
|
-
writeJson(file, doc);
|
|
393
|
+
fs.renameSync(file, `${file}.migrated`);
|
|
377
394
|
}
|
|
378
395
|
|
|
379
396
|
export function knownProjects(mdir: string = machineDir()): string[] {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
397
|
+
migrateLegacyProjects(mdir);
|
|
398
|
+
return Object.values(readRegistry({ dir: registryDirFor(mdir) }).projects)
|
|
399
|
+
.map((e) => e.root)
|
|
400
|
+
.filter((r) => fs.existsSync(r));
|
|
385
401
|
}
|
|
386
402
|
|
|
387
403
|
// ---------------------------------------------------------------------------
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* decision D8, which this module does not take.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { detectNormalized, detectWithExtent } from '../_lib/egress_normalize.js';
|
|
20
21
|
import {
|
|
21
|
-
detect,
|
|
22
22
|
EGRESS_RULES,
|
|
23
23
|
type EgressRule,
|
|
24
24
|
type EgressSpan,
|
|
@@ -26,8 +26,6 @@ import {
|
|
|
26
26
|
prefixesOf,
|
|
27
27
|
rawKeyPrefixPattern,
|
|
28
28
|
replaceSpans,
|
|
29
|
-
resolveOverlaps,
|
|
30
|
-
findSpans,
|
|
31
29
|
REVIEW_CREDENTIAL_ROWS,
|
|
32
30
|
} from '../_lib/egress_rules.js';
|
|
33
31
|
import { parseTokens, TokenScope } from '../_lib/egress_token.js';
|
|
@@ -88,9 +86,10 @@ export class CouncilEgress {
|
|
|
88
86
|
* proven is replaced through the nearest END marker, as the line masks did.
|
|
89
87
|
*/
|
|
90
88
|
replace(text: string): string {
|
|
91
|
-
const
|
|
89
|
+
const at = detectWithExtent(text, BUNDLE_RULES);
|
|
90
|
+
const spans = at.spans
|
|
92
91
|
.filter((s) => s.class === 'credential' || this.opts.identifiers)
|
|
93
|
-
.map((s) => ({ ...s, end:
|
|
92
|
+
.map((s) => ({ ...s, end: at.end(s, maskingEnd) ?? s.end }));
|
|
94
93
|
spans.sort((a, b) => a.start - b.start);
|
|
95
94
|
const merged: EgressSpan[] = [];
|
|
96
95
|
for (const s of spans) {
|
|
@@ -124,7 +123,7 @@ export class CouncilEgress {
|
|
|
124
123
|
for (const value of this.replaced) {
|
|
125
124
|
if (body.includes(value)) return { reason: 'replaced-value-present', what: 'value replaced in this run' };
|
|
126
125
|
}
|
|
127
|
-
for (const s of
|
|
126
|
+
for (const s of detectNormalized(body)) {
|
|
128
127
|
const high = s.class === 'credential' ? s.rule !== 'generic-assignment' : s.validated && this.opts.identifiers;
|
|
129
128
|
if (high) return { reason: 'high-confidence-finding', what: s.rule };
|
|
130
129
|
}
|
|
@@ -66,7 +66,8 @@ import { gitEnv } from './_lib/git_env.js';
|
|
|
66
66
|
import { graphUntestedVerdict } from './_lib/graph_feeder_record.js';
|
|
67
67
|
import { LexicalIndex } from './_lib/lexical_index.js';
|
|
68
68
|
import { buildFromRepo } from './code_graph/build.js';
|
|
69
|
-
import { graphState
|
|
69
|
+
import { graphState } from './code_graph/detect.js';
|
|
70
|
+
import { graphHome } from './code_graph/graph_home.js';
|
|
70
71
|
import { loadGraph } from './code_graph/query.js';
|
|
71
72
|
import { emitSqliteTwin, sqliteTwinPath } from './code_graph/sqlite_store.js';
|
|
72
73
|
import type { CodeEdge, CodeGraph } from './code_graph/types.js';
|
|
@@ -159,7 +160,13 @@ export async function makeFixture(root: string, files: number, withGraph: boolea
|
|
|
159
160
|
git(dir, ['init', '-q']);
|
|
160
161
|
git(dir, ['add', '-A']);
|
|
161
162
|
git(dir, ['commit', '-q', '-m', 'fixture']);
|
|
162
|
-
if (withGraph)
|
|
163
|
+
if (withGraph) {
|
|
164
|
+
// A project home, as in every repository the feeder serves: the hooks the
|
|
165
|
+
// bench spawns run under their own user-global root, where a global
|
|
166
|
+
// graph home would not be found.
|
|
167
|
+
fs.mkdirSync(path.join(dir, 'agents'));
|
|
168
|
+
await buildFromRepo(dir, graphHome(dir).cachePath);
|
|
169
|
+
}
|
|
163
170
|
fs.writeFileSync(path.join(dir, 'src', 'service.ts'), 'export function handle(x: string): string {\n return x.trim();\n}\n');
|
|
164
171
|
return dir;
|
|
165
172
|
}
|
|
@@ -404,7 +411,7 @@ export function benchLoadSplit(opts: { graph: string; runs?: number | undefined
|
|
|
404
411
|
if (!v.ok) throw new Error(`invalid graph at ${opts.graph}: ${v.errors.slice(0, 3).join('; ')}`);
|
|
405
412
|
const graph = parsed as CodeGraph;
|
|
406
413
|
const t3 = performance.now();
|
|
407
|
-
const wrote = emitSqliteTwin(graph,
|
|
414
|
+
const wrote = emitSqliteTwin(graph, copy);
|
|
408
415
|
const t4 = performance.now();
|
|
409
416
|
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
410
417
|
const outM = new Map<string, CodeEdge[]>();
|
|
@@ -208,31 +208,40 @@ function _render_cf_json(m: JsonObject): string {
|
|
|
208
208
|
return pyJsonDumps(payload, { indent: 2, sortKeys: true }) + '\n';
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
/** The registry schema the payload is validated against; its `description` caps at 100 characters. */
|
|
212
|
+
export const SERVER_SCHEMA_URL = 'https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json';
|
|
213
|
+
export const REGISTRY_DESCRIPTION_MAX = 100;
|
|
214
|
+
|
|
211
215
|
/**
|
|
212
216
|
* Official MCP Registry server.json (registry.modelcontextprotocol.io).
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
+
* The name matches package.json `mcpName` + the README `mcp-name:` marker; the
|
|
218
|
+
* description is `agentConfig.registryDescription`, because the npm description
|
|
219
|
+
* is longer than the registry accepts. The positional `mcp-server` argument is
|
|
220
|
+
* what makes a registry client start the server instead of the bare CLI.
|
|
221
|
+
* Version- and length-equality are asserted by lint_mcp_registry_manifest (CI).
|
|
217
222
|
*/
|
|
218
|
-
function
|
|
219
|
-
const pkg = m['package'] as JsonObject;
|
|
223
|
+
export function renderServerJson(pkg: JsonObject): string {
|
|
220
224
|
const version = pkg['version'] as string;
|
|
225
|
+
const agentConfig = (pkg['agentConfig'] as JsonObject | undefined) ?? {};
|
|
221
226
|
const payload: JsonObject = {
|
|
222
|
-
$schema:
|
|
223
|
-
name: '
|
|
224
|
-
|
|
227
|
+
$schema: SERVER_SCHEMA_URL,
|
|
228
|
+
name: pkg['mcpName'] as string,
|
|
229
|
+
title: 'agent-config',
|
|
230
|
+
description: (agentConfig['registryDescription'] as string | undefined) ?? '',
|
|
231
|
+
websiteUrl: pkg['homepage'] as string,
|
|
225
232
|
repository: {
|
|
226
|
-
url: pkg
|
|
233
|
+
url: _repo_url(pkg),
|
|
227
234
|
source: 'github',
|
|
228
235
|
},
|
|
229
236
|
version,
|
|
230
237
|
packages: [
|
|
231
238
|
{
|
|
232
239
|
registryType: 'npm',
|
|
233
|
-
identifier: '
|
|
240
|
+
identifier: pkg['name'] as string,
|
|
234
241
|
version,
|
|
242
|
+
runtimeHint: 'npx',
|
|
235
243
|
transport: { type: 'stdio' },
|
|
244
|
+
packageArguments: [{ type: 'positional', value: 'mcp-server' }],
|
|
236
245
|
},
|
|
237
246
|
],
|
|
238
247
|
};
|
|
@@ -280,7 +289,9 @@ export function main(argv: readonly string[]): number {
|
|
|
280
289
|
const manifest_text = pyJsonDumps(manifest, { indent: 2, sortKeys: true }) + '\n';
|
|
281
290
|
const row_md = _render_row_md(manifest);
|
|
282
291
|
const cf_json = _render_cf_json(manifest);
|
|
283
|
-
const server_json =
|
|
292
|
+
const server_json = renderServerJson(
|
|
293
|
+
JSON.parse(fs.readFileSync(_config.PKG_FILE, 'utf-8')) as JsonObject,
|
|
294
|
+
);
|
|
284
295
|
|
|
285
296
|
const outputs: Array<[string, string]> = [
|
|
286
297
|
[_config.OUT_MANIFEST, manifest_text],
|
|
@@ -60,6 +60,13 @@ export const EXACT_PIN_EXCEPTIONS: Readonly<Record<string, string>> = {
|
|
|
60
60
|
'which would make every grammar load throw rather than degrade — so the runtime ' +
|
|
61
61
|
'and its grammars move together, in one reviewed change, or not at all. ' +
|
|
62
62
|
'Refresh procedure: src/vendor/grammars/README.md.',
|
|
63
|
+
// The three sigstore modules are the trusted computing base of the
|
|
64
|
+
// unattended upgrade's provenance check (sigstore_provenance.ts): a
|
|
65
|
+
// patch release of any of them changes what "verified" means, so each
|
|
66
|
+
// version is reviewed rather than resolved. They move together.
|
|
67
|
+
'@sigstore/verify': 'Trusted computing base of the release provenance check; moves only in a reviewed change, with its two siblings.',
|
|
68
|
+
'@sigstore/bundle': 'Parses the bundle the provenance check verifies; pinned with @sigstore/verify for the same reason.',
|
|
69
|
+
'@sigstore/protobuf-specs': 'Decodes the pinned trust root and npm keys; pinned with @sigstore/verify for the same reason.',
|
|
63
70
|
};
|
|
64
71
|
|
|
65
72
|
/**
|
|
@@ -75,6 +82,13 @@ export const SECURITY_FLOOR_EXCEPTIONS: Readonly<Record<string, { required: stri
|
|
|
75
82
|
'high-severity route-guard bypass via path traversal (CVSS 7.5) affects <=10.1.1; ' +
|
|
76
83
|
'10.1.2 is the fix. Verified with npm audit on a clean 10.1.0 install, 2026-07-31.',
|
|
77
84
|
},
|
|
85
|
+
zod: {
|
|
86
|
+
required: '^4.6.6',
|
|
87
|
+
reason:
|
|
88
|
+
'high-severity memory exhaustion while validating oversized constrained collections ' +
|
|
89
|
+
'(GHSA-mrhx-c7xc-m3w5) affects <4.6.6; 4.6.6 is the fix. Verified with npm audit on ' +
|
|
90
|
+
'clean 4.6.5 (1 high) and 4.6.6 (0) installs, 2026-10-24.',
|
|
91
|
+
},
|
|
78
92
|
};
|
|
79
93
|
|
|
80
94
|
/** A settled caret floor: `^X.Y.0`. */
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `check_release_pr_shape`.
|
|
5
5
|
*
|
|
6
6
|
* The shape check lets a release PR carry `package.json`, `package-lock.json`
|
|
7
|
-
* and the
|
|
7
|
+
* and the plugin and extension manifests, and it reads only their NAMES. Smoke is
|
|
8
8
|
* skipped on release heads (`docs/contracts/release-pr-gating.md`), so an added
|
|
9
9
|
* script, a new dependency or a changed `bin` path could ride a release PR with
|
|
10
10
|
* no gate reading it. This compares each manifest against the base and fails on
|
|
@@ -27,6 +27,7 @@ import * as path from 'node:path';
|
|
|
27
27
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
28
28
|
|
|
29
29
|
import { CONSUMER_IMPACT_VALUES } from './_lib/consumer_impact.js';
|
|
30
|
+
import { PINNED_PACKAGE_ARG_RE } from './_lib/listing_manifests.js';
|
|
30
31
|
import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
|
|
31
32
|
|
|
32
33
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -43,8 +44,17 @@ export const VERSION_FIELDS: Readonly<Record<string, readonly Pattern[]>> = {
|
|
|
43
44
|
'.claude-plugin/marketplace.json': [['version'], ['metadata', 'version'], ['plugins', '*', 'version']],
|
|
44
45
|
'.augment-plugin/plugin.json': [['version']],
|
|
45
46
|
'.augment-plugin/marketplace.json': [['version'], ['metadata', 'version'], ['plugins', '*', 'version']],
|
|
47
|
+
'.github/plugin/plugin.json': [['version'], ['mcpServers', 'agent-config', 'args', '*']],
|
|
48
|
+
'gemini-extension.json': [['version'], ['mcpServers', 'agent-config', 'args', '*']],
|
|
46
49
|
};
|
|
47
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Patterns admitted only as a re-pin: an `npx` argument may move from one
|
|
53
|
+
* `@event4u/agent-config@X.Y.Z` to another, never to any other string — the
|
|
54
|
+
* arguments around it are the command a crawler runs.
|
|
55
|
+
*/
|
|
56
|
+
const PINNED_ARG_PATTERN: Pattern = ['mcpServers', 'agent-config', 'args', '*'];
|
|
57
|
+
|
|
48
58
|
function _matches(p: readonly Segment[], pat: Pattern): boolean {
|
|
49
59
|
if (p.length !== pat.length) return false;
|
|
50
60
|
return pat.every((seg, i) => seg === p[i] || (seg === '*' && /^\d+$/u.test(p[i] ?? '')));
|
|
@@ -137,7 +147,12 @@ export function manifestDifferences(file: string, base: string | null, head: str
|
|
|
137
147
|
before = (before as Record<string, unknown> | undefined)?.[s];
|
|
138
148
|
after = (after as Record<string, unknown> | undefined)?.[s];
|
|
139
149
|
}
|
|
140
|
-
|
|
150
|
+
const pinOnly = _matches(p, PINNED_ARG_PATTERN);
|
|
151
|
+
const inShape =
|
|
152
|
+
typeof before === 'string' &&
|
|
153
|
+
typeof after === 'string' &&
|
|
154
|
+
(!pinOnly || (PINNED_PACKAGE_ARG_RE.test(before) && PINNED_PACKAGE_ARG_RE.test(after)));
|
|
155
|
+
if (atVersion && inShape) continue;
|
|
141
156
|
out.push(`${file}: ${_render(p)} differs outside the version fields`);
|
|
142
157
|
}
|
|
143
158
|
return out;
|
|
@@ -36,6 +36,11 @@ const ALLOWLIST_GLOBS = [
|
|
|
36
36
|
// the tarball (publish-surface.json roots), so a release PR must carry them.
|
|
37
37
|
'.augment-plugin/plugin.json',
|
|
38
38
|
'.augment-plugin/marketplace.json',
|
|
39
|
+
// Listing manifests — version and the `npx` pin rewritten by release.ts
|
|
40
|
+
// set_plugin_manifest_versions; neither ships in the tarball, but a crawler
|
|
41
|
+
// reads them from the default branch.
|
|
42
|
+
'.github/plugin/plugin.json',
|
|
43
|
+
'gemini-extension.json',
|
|
39
44
|
'src/packs/*/pack.yaml',
|
|
40
45
|
'src/packs/*/README.md',
|
|
41
46
|
'src/domains/*/pack.yaml',
|
|
@@ -17,8 +17,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
17
17
|
|
|
18
18
|
import { write_atomic } from '../_lib/fs_atomic.js';
|
|
19
19
|
import { extractFile, type FileExtract, type RawEdge } from './extract.js';
|
|
20
|
-
import { buildLaravelOverlay } from './laravel_overlay.js';
|
|
21
20
|
import { grammarWasmPath } from './loader.js';
|
|
21
|
+
import { type PassReport, runPasses } from './passes.js';
|
|
22
|
+
import { EMPTY_PROBE, PROBE_MARKERS, type ProjectProbe } from './providers/index.js';
|
|
22
23
|
import {
|
|
23
24
|
ALIAS_VIA,
|
|
24
25
|
aliasRulesFrom,
|
|
@@ -30,8 +31,9 @@ import {
|
|
|
30
31
|
resolveAlias,
|
|
31
32
|
resolvePsr4,
|
|
32
33
|
} from './resolution_tiers.js';
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
34
|
+
import { templateBase } from './conventions_default.js';
|
|
35
|
+
import { DOC_EXTS } from './docs_layer.js';
|
|
36
|
+
import { readScipIndex, type ScipDocument } from './scip_reader.js';
|
|
35
37
|
import { emitSqliteTwin } from './sqlite_store.js';
|
|
36
38
|
import {
|
|
37
39
|
type CodeEdge,
|
|
@@ -77,7 +79,21 @@ const CODE_EXTS: ReadonlySet<string> = new Set(Object.keys(EXT_LANG));
|
|
|
77
79
|
/** Generated output under the project root: the engine never reads its own artefacts back. */
|
|
78
80
|
const GENERATED_DIRS: ReadonlySet<string> = new Set(['agents/map', 'agents/runtime']);
|
|
79
81
|
|
|
82
|
+
/** Whether the discovery walk would enter every directory of repo-relative `rel` (posix). */
|
|
83
|
+
export function walkedPath(rel: string): boolean {
|
|
84
|
+
const dirs = rel.split('/').slice(0, -1);
|
|
85
|
+
for (let i = 0; i < dirs.length; i++) {
|
|
86
|
+
const d = dirs[i] as string;
|
|
87
|
+
if (IGNORE_DIRS.has(d) || d.startsWith('.') || GENERATED_DIRS.has(dirs.slice(0, i + 1).join('/'))) return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
export function discoverFiles(root: string, exts: ReadonlySet<string> = CODE_EXTS): string[] {
|
|
93
|
+
return walkFiles(root, (name) => exts.has(path.extname(name).toLowerCase()));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function walkFiles(root: string, accept: (name: string) => boolean): string[] {
|
|
81
97
|
const abs = path.resolve(root);
|
|
82
98
|
// Resolve the root itself first: a repo checked out under a symlinked
|
|
83
99
|
// path (e.g. macOS /tmp → /private/tmp) must still confine correctly —
|
|
@@ -106,8 +122,7 @@ export function discoverFiles(root: string, exts: ReadonlySet<string> = CODE_EXT
|
|
|
106
122
|
continue;
|
|
107
123
|
}
|
|
108
124
|
if (!e.isFile()) continue;
|
|
109
|
-
|
|
110
|
-
if (!exts.has(ext)) continue;
|
|
125
|
+
if (!accept(e.name)) continue;
|
|
111
126
|
let real: string;
|
|
112
127
|
try {
|
|
113
128
|
real = fs.realpathSync(full);
|
|
@@ -122,6 +137,33 @@ export function discoverFiles(root: string, exts: ReadonlySet<string> = CODE_EXT
|
|
|
122
137
|
return out.sort();
|
|
123
138
|
}
|
|
124
139
|
|
|
140
|
+
/** Manifests a provider may read to detect a stack, by basename. */
|
|
141
|
+
export const MANIFEST_NAME = /^(?:composer\.json|package\.json|pyproject\.toml|requirements[\w.-]*\.txt|pom\.xml|build\.gradle(?:\.kts)?|go\.mod|[\w.-]+\.csproj|Gemfile)$/;
|
|
142
|
+
|
|
143
|
+
/** The project's manifests and marker files, for provider detection (ADR-298). */
|
|
144
|
+
export function readProjectProbe(root: string): ProjectProbe {
|
|
145
|
+
const abs = path.resolve(root);
|
|
146
|
+
const manifests = new Map<string, string>();
|
|
147
|
+
const markers = new Set<string>();
|
|
148
|
+
const paths: string[] = [];
|
|
149
|
+
for (const full of walkFiles(abs, () => true)) {
|
|
150
|
+
const rel = toPosixRel(abs, full);
|
|
151
|
+
paths.push(rel);
|
|
152
|
+
const name = path.basename(full);
|
|
153
|
+
if (!MANIFEST_NAME.test(name) && !PROBE_MARKERS.has(name)) continue;
|
|
154
|
+
if (PROBE_MARKERS.has(name)) {
|
|
155
|
+
markers.add(rel);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
manifests.set(rel, fs.readFileSync(full, 'utf-8'));
|
|
160
|
+
} catch {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { manifests, markers, paths };
|
|
165
|
+
}
|
|
166
|
+
|
|
125
167
|
function toPosixRel(root: string, full: string): string {
|
|
126
168
|
return path.relative(root, full).split(path.sep).join('/');
|
|
127
169
|
}
|
|
@@ -234,10 +276,21 @@ function resolveSpecifier(fromFile: string, spec: string, fileIds: ReadonlySet<s
|
|
|
234
276
|
* go `example.com/x/mod` → every non-test `.go` file of the most specific
|
|
235
277
|
* directory whose path ends the import path — a Go import names a
|
|
236
278
|
* package, and a package is all of its files.
|
|
279
|
+
* ruby `require_relative 'x'` → `x.rb` beside the importer; `require 'a/b'`
|
|
280
|
+
* → the one `a/b.rb` under any directory (a load-path root).
|
|
281
|
+
* csharp `using A.B` → the one file declaring namespace `A.B` (`namespaces`).
|
|
282
|
+
* A namespace declared by several files is left to name lookup: it
|
|
283
|
+
* binds no file, since a `using` names no single one.
|
|
237
284
|
*
|
|
238
285
|
* A suffix that matches two files is refused rather than guessed.
|
|
239
286
|
*/
|
|
240
|
-
export function resolveLangModule(
|
|
287
|
+
export function resolveLangModule(
|
|
288
|
+
lang: Lang,
|
|
289
|
+
fromFile: string,
|
|
290
|
+
spec: string,
|
|
291
|
+
fileIds: readonly string[],
|
|
292
|
+
namespaces: ReadonlyMap<string, readonly string[]> = new Map(),
|
|
293
|
+
): string[] {
|
|
241
294
|
const unique = (suffixes: string[]): string[] => {
|
|
242
295
|
const hits = fileIds.filter((f) => suffixes.some((s) => f === s || f.endsWith(`/${s}`)));
|
|
243
296
|
return hits.length === 1 ? hits : [];
|
|
@@ -264,6 +317,16 @@ export function resolveLangModule(lang: Lang, fromFile: string, spec: string, fi
|
|
|
264
317
|
}
|
|
265
318
|
return best ? goFiles.filter((f) => idDirname(f) === best).sort() : [];
|
|
266
319
|
}
|
|
320
|
+
if (lang === 'ruby') {
|
|
321
|
+
if (spec.startsWith('.')) {
|
|
322
|
+
const dir = idDirname(fromFile);
|
|
323
|
+
const joined = path.posix.normalize(dir && dir !== '.' ? `${dir}/${spec}` : spec);
|
|
324
|
+
const c = joined.endsWith('.rb') ? joined : `${joined}.rb`;
|
|
325
|
+
return fileIds.includes(c) ? [c] : [];
|
|
326
|
+
}
|
|
327
|
+
return unique([spec.endsWith('.rb') ? spec : `${spec}.rb`]);
|
|
328
|
+
}
|
|
329
|
+
if (lang === 'csharp') return [...(namespaces.get(spec) ?? [])];
|
|
267
330
|
return [];
|
|
268
331
|
}
|
|
269
332
|
|
|
@@ -309,6 +372,8 @@ export function buildGraph(
|
|
|
309
372
|
resolution: ResolutionConfig = EMPTY_RESOLUTION_CONFIG,
|
|
310
373
|
scip: readonly ScipDocument[] = [],
|
|
311
374
|
docs: readonly SourceFile[] = [],
|
|
375
|
+
probe: ProjectProbe = EMPTY_PROBE,
|
|
376
|
+
report?: { passes?: PassReport },
|
|
312
377
|
): CodeGraph {
|
|
313
378
|
const nodes = extracts.flatMap((e) => e.nodes);
|
|
314
379
|
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
@@ -383,6 +448,21 @@ export function buildGraph(
|
|
|
383
448
|
const fileIdList = [...fileIds].sort();
|
|
384
449
|
/** Go imports name a package of several files; the binding takes the first, these carry the rest. */
|
|
385
450
|
const extraImports: Array<{ source: string; target: string }> = [];
|
|
451
|
+
const namespaceFiles = new Map<string, string[]>();
|
|
452
|
+
for (const ex of extracts) for (const ns of ex.namespaces ?? []) push(namespaceFiles, ns, ex.file);
|
|
453
|
+
// C# name lookup is scoped by namespace, not by file: a type is visible
|
|
454
|
+
// from its own namespace, every enclosing one, and each `using`. Within
|
|
455
|
+
// that set a unique name IS the binding the compiler makes.
|
|
456
|
+
const csTypes = new Map<string, string[]>();
|
|
457
|
+
const csVisible = new Map<string, string[]>();
|
|
458
|
+
for (const ex of extracts) {
|
|
459
|
+
if (ex.lang !== 'csharp') continue;
|
|
460
|
+
for (const [id, ns] of Object.entries(ex.typeNamespaces ?? {})) push(csTypes, `${ns}\0${id.slice(id.indexOf('#') + 1)}`, id);
|
|
461
|
+
const seen = new Set<string>();
|
|
462
|
+
for (const ns of ex.namespaces ?? []) for (let p = ns; p; p = p.includes('.') ? p.slice(0, p.lastIndexOf('.')) : '') seen.add(p);
|
|
463
|
+
for (const r of ex.rawEdges) if (r.relation === 'imports' && r.importKind === 'namespace' && !r.localName && r.moduleSpecifier) seen.add(r.moduleSpecifier);
|
|
464
|
+
csVisible.set(ex.file, [...seen]);
|
|
465
|
+
}
|
|
386
466
|
for (const ex of extracts) {
|
|
387
467
|
const locals = new Map<string, string[]>();
|
|
388
468
|
for (const n of ex.nodes) if (n.kind !== 'file') push(locals, n.label, n.id);
|
|
@@ -419,8 +499,9 @@ export function buildGraph(
|
|
|
419
499
|
if (hit) imports.set(local, { target: hit, exact: false, via: 'name-lookup' });
|
|
420
500
|
continue;
|
|
421
501
|
}
|
|
422
|
-
if (ex.lang === '
|
|
423
|
-
|
|
502
|
+
if (ex.lang === 'csharp' && (namespaceFiles.get(spec)?.length ?? 0) > 1) continue;
|
|
503
|
+
if (ex.lang === 'python' || ex.lang === 'java' || ex.lang === 'go' || ex.lang === 'csharp' || ex.lang === 'ruby') {
|
|
504
|
+
const hits = resolveLangModule(ex.lang, ex.file, spec, fileIdList, namespaceFiles);
|
|
424
505
|
const first = hits[0];
|
|
425
506
|
if (first === undefined) {
|
|
426
507
|
imports.set(local, {
|
|
@@ -502,6 +583,11 @@ export function buildGraph(
|
|
|
502
583
|
// reported as ones.
|
|
503
584
|
via: bound.via,
|
|
504
585
|
};
|
|
586
|
+
if (lang === 'csharp') {
|
|
587
|
+
const ids = new Set((csVisible.get(file) ?? []).flatMap((ns) => csTypes.get(`${ns}\0${name}`) ?? []));
|
|
588
|
+
const fit = [...ids].filter((id) => satisfies(nodeKind.get(id), want));
|
|
589
|
+
if (fit.length === 1) return { target: fit[0] as string, confidence: 'EXTRACTED', via: 'namespace-scope' };
|
|
590
|
+
}
|
|
505
591
|
if (scopeOnly) return null;
|
|
506
592
|
const wide =
|
|
507
593
|
want === 'constructible'
|
|
@@ -607,6 +693,9 @@ export function buildGraph(
|
|
|
607
693
|
// against a same-named declaration elsewhere in the repository is
|
|
608
694
|
// the defect this roadmap removes, applied to a second relation.
|
|
609
695
|
const hit = resolveInScope(ex.file, r.targetName, 'any', ex.lang, true);
|
|
696
|
+
// A C# type reference to a framework type (`List`, `Task`) names no
|
|
697
|
+
// project file, and a `symbol:` edge per mention would only be noise.
|
|
698
|
+
if (!hit && ex.lang === 'csharp') return;
|
|
610
699
|
emit({
|
|
611
700
|
source: r.sourceId,
|
|
612
701
|
target: hit?.target ?? `symbol:${r.targetName}`,
|
|
@@ -670,6 +759,28 @@ export function buildGraph(
|
|
|
670
759
|
}
|
|
671
760
|
return;
|
|
672
761
|
}
|
|
762
|
+
// scoped `C::m()` whose class the file binds by a stated mechanism
|
|
763
|
+
// (an import, PSR-4, a local declaration, a C# namespace): the class
|
|
764
|
+
// is known, so a method it declares or inherits is the target, and the
|
|
765
|
+
// edge is exactly as trustworthy as the class binding.
|
|
766
|
+
if ((r.callStyle === 'scoped' || r.callStyle === 'dynamic') && r.scopeName) {
|
|
767
|
+
const cls = resolveInScope(ex.file, r.scopeName, 'any', ex.lang, true);
|
|
768
|
+
const kind = cls ? nodeKind.get(cls.target) : undefined;
|
|
769
|
+
if (cls && isStatedResolution(cls.via) && (kind === 'class' || kind === 'interface' || kind === 'enum')) {
|
|
770
|
+
const m = resolveHierMethod(cls.target, r.targetName);
|
|
771
|
+
if (m) {
|
|
772
|
+
emit({
|
|
773
|
+
source: r.sourceId,
|
|
774
|
+
target: m,
|
|
775
|
+
relation: 'calls',
|
|
776
|
+
confidence: m.startsWith(`${cls.target}::`) ? cls.confidence : 'INFERRED',
|
|
777
|
+
resolved_via: cls.via,
|
|
778
|
+
provider: 'native',
|
|
779
|
+
});
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
673
784
|
// scoped (Class::m unresolved / facade) or dynamic ($obj->m) → AMBIGUOUS
|
|
674
785
|
const cands = methodCandidates(r.targetName, ex.lang);
|
|
675
786
|
if (r.callStyle === 'dynamic' && cands.length === 0) {
|
|
@@ -754,39 +865,9 @@ export function buildGraph(
|
|
|
754
865
|
});
|
|
755
866
|
}
|
|
756
867
|
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
// unrelated in-repo `middleware()`.
|
|
761
|
-
const overlay = buildLaravelOverlay(files, nodes, resolution);
|
|
762
|
-
let routeBuilderCalls = 0;
|
|
763
|
-
if (overlay.nodes.length > 0 || overlay.edges.length > 0) {
|
|
764
|
-
const kept = edges.filter((e) => !overlay.dropEdge(e));
|
|
765
|
-
routeBuilderCalls = edges.length - kept.length;
|
|
766
|
-
// Element-wise: a spread of ~200k edges overflows the call stack.
|
|
767
|
-
edges.length = 0;
|
|
768
|
-
for (const e of kept) edges.push(e);
|
|
769
|
-
for (const e of overlay.edges) edges.push(e);
|
|
770
|
-
for (const n of overlay.nodes) nodes.push(n);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
if (docs.length > 0) {
|
|
774
|
-
const allFiles = new Set([...files.map((f) => f.path), ...docs.map((d) => d.path)]);
|
|
775
|
-
const layer = docsLayer(docs, allFiles);
|
|
776
|
-
for (const n of layer.nodes) nodes.push(n);
|
|
777
|
-
for (const e of layer.edges) edges.push(e);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// A project's own SCIP index states references the compiler resolved;
|
|
781
|
-
// each one supersedes the native name guess it duplicates.
|
|
782
|
-
if (scip.length > 0) {
|
|
783
|
-
const stated = scipEdges(scip, nodes);
|
|
784
|
-
const dropped = supersededByScip(stated, nodes);
|
|
785
|
-
const kept = edges.filter((e) => !dropped(e));
|
|
786
|
-
edges.length = 0;
|
|
787
|
-
for (const e of kept) edges.push(e);
|
|
788
|
-
for (const e of stated) edges.push(e);
|
|
789
|
-
}
|
|
868
|
+
// Everything after resolution: convention providers, docs, SCIP (passes.ts).
|
|
869
|
+
const passes = runPasses({ files, docs, scip, resolution, probe }, nodes, edges);
|
|
870
|
+
if (report) report.passes = passes;
|
|
790
871
|
|
|
791
872
|
// deterministic ordering
|
|
792
873
|
nodes.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
@@ -800,7 +881,9 @@ export function buildGraph(
|
|
|
800
881
|
for (const e of edges) counts[e.confidence] += 1;
|
|
801
882
|
const suppressed: CodeGraph['suppressed_edge_counts'] = {
|
|
802
883
|
dynamic_no_candidate: suppressedKeys.size,
|
|
803
|
-
...(
|
|
884
|
+
...(Object.keys(passes.suppressed).length > 0
|
|
885
|
+
? { contested_by_provider: Object.fromEntries(Object.entries(passes.suppressed).sort()) }
|
|
886
|
+
: {}),
|
|
804
887
|
};
|
|
805
888
|
|
|
806
889
|
const languages = [...new Set(extracts.map((e) => e.lang))].sort() as Lang[];
|
|
@@ -819,6 +902,18 @@ export function buildGraph(
|
|
|
819
902
|
h.update(createHash('sha256').update(d.source, 'utf-8').digest('hex'));
|
|
820
903
|
h.update('\n');
|
|
821
904
|
}
|
|
905
|
+
// Manifests and markers decide which providers run: a changed manifest is
|
|
906
|
+
// a changed input even when no source file moved.
|
|
907
|
+
for (const [p, text] of [...probe.manifests].sort((a, b) => (a[0] < b[0] ? -1 : 1))) {
|
|
908
|
+
h.update(`manifest\0${p}\0`);
|
|
909
|
+
h.update(createHash('sha256').update(text, 'utf-8').digest('hex'));
|
|
910
|
+
h.update('\n');
|
|
911
|
+
}
|
|
912
|
+
for (const p of [...probe.markers].sort()) h.update(`marker\0${p}\n`);
|
|
913
|
+
// Template files are no source, yet their presence decides `renders` and
|
|
914
|
+
// default-pass edges: they are inputs too. Other non-code paths (caches,
|
|
915
|
+
// logs, assets) are not read and stay out of the identity.
|
|
916
|
+
for (const p of probe.paths) if (templateBase(p) !== null) h.update(`template\0${p}\n`);
|
|
822
917
|
// The SCIP index is an input like any source file: a new index must not
|
|
823
918
|
// read as the same graph.
|
|
824
919
|
for (const d of [...scip].sort((a, b) => (a.path < b.path ? -1 : 1))) {
|
|
@@ -833,6 +928,7 @@ export function buildGraph(
|
|
|
833
928
|
grammar_abi: EXPECTED_GRAMMAR_ABI,
|
|
834
929
|
edge_confidence_counts: counts,
|
|
835
930
|
suppressed_edge_counts: suppressed,
|
|
931
|
+
...(passes.providers.length > 0 ? { providers: passes.providers } : {}),
|
|
836
932
|
nodes,
|
|
837
933
|
edges,
|
|
838
934
|
};
|
|
@@ -908,6 +1004,7 @@ export function serializeGraph(g: CodeGraph): string {
|
|
|
908
1004
|
'grammar_abi',
|
|
909
1005
|
'edge_confidence_counts',
|
|
910
1006
|
'suppressed_edge_counts',
|
|
1007
|
+
'providers',
|
|
911
1008
|
'nodes',
|
|
912
1009
|
'edges',
|
|
913
1010
|
];
|
|
@@ -936,13 +1033,14 @@ export function serializeGraph(g: CodeGraph): string {
|
|
|
936
1033
|
// byte what it was before this field existed.
|
|
937
1034
|
if (e.ambiguity_reason) base['ambiguity_reason'] = e.ambiguity_reason;
|
|
938
1035
|
if (e.candidates) base['candidates'] = e.candidates;
|
|
1036
|
+
if (e.provider_id) base['provider_id'] = e.provider_id;
|
|
939
1037
|
return base;
|
|
940
1038
|
};
|
|
941
1039
|
const ordered: Record<string, unknown> = {};
|
|
942
1040
|
for (const k of key_order) {
|
|
943
1041
|
if (k === 'nodes') ordered[k] = g.nodes.map(node);
|
|
944
1042
|
else if (k === 'edges') ordered[k] = g.edges.map(edge);
|
|
945
|
-
else ordered[k] = g[k];
|
|
1043
|
+
else if (g[k] !== undefined) ordered[k] = g[k];
|
|
946
1044
|
}
|
|
947
1045
|
return JSON.stringify(ordered, null, 2) + '\n';
|
|
948
1046
|
}
|
|
@@ -1086,14 +1184,16 @@ export async function buildFromRepo(
|
|
|
1086
1184
|
continue;
|
|
1087
1185
|
}
|
|
1088
1186
|
}
|
|
1089
|
-
const
|
|
1187
|
+
const report: { passes?: PassReport } = {};
|
|
1188
|
+
const graph = buildGraph(files, extracts, readResolutionConfig(root), scip, docs, readProjectProbe(abs), report);
|
|
1189
|
+
for (const f of report.passes?.failures ?? []) process.stderr.write(`code-graph: pass ${f.pass} failed — ${f.reason}\n`);
|
|
1090
1190
|
const json = serializeGraph(graph);
|
|
1091
1191
|
if (cachePath) {
|
|
1092
1192
|
write_atomic(cachePath, json);
|
|
1093
1193
|
write_atomic(sidecarPath(cachePath), JSON.stringify(nextSidecar));
|
|
1094
1194
|
// Derived SQLite twin (ADR-129): best-effort accelerator beside the
|
|
1095
1195
|
// canonical JSON — never fails the build, no-op on old Node.
|
|
1096
|
-
emitSqliteTwin(graph,
|
|
1196
|
+
emitSqliteTwin(graph, cachePath);
|
|
1097
1197
|
}
|
|
1098
1198
|
return { graph, json, fileCount: files.length, reExtracted, reused };
|
|
1099
1199
|
}
|