@event4u/agent-config 17.2.1 → 17.3.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 +152 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +13 -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-delegate/{chunk-KR5ZIFPO.js → chunk-DHSSP7V3.js} +1 -1
- package/dist/cli-delegate/chunk-E7ACTJTJ.js +6792 -0
- package/dist/cli-delegate/{chunk-BZJ4Y3CI.js → chunk-MT2WOOCY.js} +1 -1
- package/dist/cli-delegate/{chunk-CMZEKSNN.js → chunk-ORJPBER2.js} +2 -2
- package/dist/cli-delegate/{chunk-VYZGJLCA.js → chunk-RKNT4LIK.js} +22 -1286
- package/dist/cli-delegate/cmd_conformance.js +20 -14
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_doctor_neighbours.js +2 -2
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_prune.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_validate.js +2 -2
- 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 +414 -0
- package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -0
- package/dist/hooks/dispatch.js +21 -11
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/egress_engine.chunk.cjs +1 -1
- package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
- 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 +5561 -2463
- package/dist/scripts/_lib/egress_rules.js +8 -2
- package/dist/scripts/condense.js +9 -9
- 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 +3 -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/gate-violation-baselines.json +3 -3
- package/src/config/gitignore-block.txt +6 -5
- package/src/config/hook-bundle-budget.json +16 -1
- package/src/config/pack-size-budget.json +12 -1
- package/src/config/packed-binary-manifest.json +9 -0
- package/src/scripts/_cli/cmd_settings_set.ts +3 -2
- package/src/scripts/_dispatch.bash +1 -1
- package/src/scripts/_lib/egress_rules.ts +8 -2
- package/src/scripts/_lib/listing_manifests.ts +85 -0
- package/src/scripts/_lib/neighbour_census.ts +1 -0
- package/src/scripts/_lib/release_settings_surface.ts +2 -6
- package/src/scripts/adoption_status.ts +231 -27
- package/src/scripts/build_mcp_registry_manifest.ts +23 -12
- package/src/scripts/check_dependency_floors.ts +7 -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 +80 -44
- package/src/scripts/code_graph/cli.ts +260 -16
- package/src/scripts/code_graph/conventions_default.ts +228 -0
- package/src/scripts/code_graph/extract.ts +5 -2
- package/src/scripts/code_graph/extract_more.ts +105 -1
- package/src/scripts/code_graph/loader.ts +10 -2
- package/src/scripts/code_graph/map.ts +174 -41
- package/src/scripts/code_graph/map_model.ts +53 -15
- package/src/scripts/code_graph/map_verify.ts +12 -0
- package/src/scripts/code_graph/map_viewer/viewer.css +6 -0
- package/src/scripts/code_graph/map_viewer/viewer.js +114 -13
- package/src/scripts/code_graph/passes.ts +173 -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 +290 -0
- package/src/scripts/code_graph/providers/django.ts +163 -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 +101 -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/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 +138 -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/refresh_cli.ts +81 -0
- package/src/scripts/code_graph/refresh_lock.ts +193 -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 +10 -4
- package/src/scripts/code_graph/types.ts +75 -41
- package/src/scripts/code_graph/validate.ts +6 -8
- package/src/scripts/condense.ts +9 -9
- package/src/scripts/generate_settings_reference.ts +2 -6
- package/src/scripts/hook_chunks.mjs +4 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +24 -7
- package/src/scripts/hooks/code_graph_refresh_hook.ts +160 -0
- package/src/scripts/hooks/lazy_concern.ts +4 -0
- package/src/scripts/install-hooks.sh +54 -33
- package/src/scripts/install.ts +2 -6
- 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 +1 -0
- package/src/scripts/mcp_server/graph_tools.ts +5 -1
- 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/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 +6 -5
- package/src/vendor/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/dist/cli-delegate/chunk-XIAPY44N.js +0 -4587
- package/dist/ui/assets/index-w70Ado09.js +0 -23
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-flight lock for `code-graph refresh --single-flight` and `map`.
|
|
3
|
+
*
|
|
4
|
+
* Every producer goes through {@link acquire}: the git-hook refresh block in
|
|
5
|
+
* `install-hooks.sh` and the `code-graph-refresh` session concern both run
|
|
6
|
+
* `refresh --single-flight`, so there is one locking protocol, not two.
|
|
7
|
+
* `mkdir` is the primitive because it is atomic on every POSIX filesystem.
|
|
8
|
+
*
|
|
9
|
+
* Ownership is recorded in a SIBLING file, never inside the directory, so the
|
|
10
|
+
* directory is empty and a single `rmdir`-style removal clears it.
|
|
11
|
+
*
|
|
12
|
+
* A lock is reclaimed only on positive evidence that its owner is gone (see
|
|
13
|
+
* {@link isStale}); an owner-less lock — a torn owner write — counts as stale
|
|
14
|
+
* only past {@link STALE_LOCK_MS}. A release removes the lock only while the
|
|
15
|
+
* owner record still carries the releaser's nonce, so a holder that was
|
|
16
|
+
* reclaimed cannot delete its successor's lock on the way out.
|
|
17
|
+
*/
|
|
18
|
+
import { randomBytes } from 'node:crypto';
|
|
19
|
+
import * as fs from 'node:fs';
|
|
20
|
+
import * as os from 'node:os';
|
|
21
|
+
import * as path from 'node:path';
|
|
22
|
+
|
|
23
|
+
export const LOCK_REL = path.join('agents', 'runtime', 'state', 'code-graph-refresh.lock');
|
|
24
|
+
/** Written by `code-graph auto-refresh on`; the session concern acts only while it exists. */
|
|
25
|
+
export const AUTO_REFRESH_REL = path.join('agents', 'runtime', 'state', 'code-graph-auto-refresh.json');
|
|
26
|
+
export const STALE_LOCK_MS = 5 * 60 * 1000;
|
|
27
|
+
/**
|
|
28
|
+
* How long an owner on ANOTHER host (a container sharing the checkout) is
|
|
29
|
+
* believed: this host cannot probe its PID, so age is the only signal. An
|
|
30
|
+
* availability compromise, not proof — it reads the owner's own wall clock, so
|
|
31
|
+
* clock skew between hosts shifts it. A same-host owner whose PID is alive is
|
|
32
|
+
* never reclaimed on age (ratification, 2026-10-24, two of two seats): a paused,
|
|
33
|
+
* debugged or overloaded refresh is still a refresh.
|
|
34
|
+
*/
|
|
35
|
+
export const MAX_LOCK_AGE_MS = 30 * 60 * 1000;
|
|
36
|
+
|
|
37
|
+
interface Owner {
|
|
38
|
+
pid: number;
|
|
39
|
+
started_at: number;
|
|
40
|
+
nonce: string;
|
|
41
|
+
/** Absent in records written before the host was stored; such an owner is judged by age alone. */
|
|
42
|
+
host?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ownerPath = (lock: string): string => `${lock}.owner.json`;
|
|
46
|
+
|
|
47
|
+
export interface RefreshRecord {
|
|
48
|
+
at: string;
|
|
49
|
+
/** `session-start` (the concern) or `refresh` (the CLI run it starts, or a manual one). */
|
|
50
|
+
by: 'session-start' | 'refresh';
|
|
51
|
+
outcome: string;
|
|
52
|
+
detail?: string | undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** One file per side, so the session concern writes without reading or merging anything. */
|
|
56
|
+
export const refreshRecordPath = (root: string, by: RefreshRecord['by']): string =>
|
|
57
|
+
path.join(root, path.dirname(LOCK_REL), `code-graph-refresh.${by}.json`);
|
|
58
|
+
|
|
59
|
+
/** The last outcome of one side, so a refresh that never happens is visible. Best-effort. */
|
|
60
|
+
export function recordRefresh(root: string, rec: Omit<RefreshRecord, 'at'>): void {
|
|
61
|
+
try {
|
|
62
|
+
fs.writeFileSync(refreshRecordPath(root, rec.by), JSON.stringify({ at: new Date().toISOString(), ...rec }));
|
|
63
|
+
} catch {
|
|
64
|
+
/* never fails a refresh */
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function readRefreshRecords(root: string): Partial<Record<RefreshRecord['by'], RefreshRecord>> {
|
|
69
|
+
const out: Partial<Record<RefreshRecord['by'], RefreshRecord>> = {};
|
|
70
|
+
for (const by of ['session-start', 'refresh'] as const) {
|
|
71
|
+
try {
|
|
72
|
+
out[by] = JSON.parse(fs.readFileSync(refreshRecordPath(root, by), 'utf-8')) as RefreshRecord;
|
|
73
|
+
} catch {
|
|
74
|
+
/* never recorded */
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function pidAlive(pid: number): boolean {
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, 0);
|
|
83
|
+
return true;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
// EPERM means the process exists but belongs to someone else.
|
|
86
|
+
return (e as NodeJS.ErrnoException).code === 'EPERM';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const errno = (e: unknown): string | undefined => (e as NodeJS.ErrnoException).code;
|
|
91
|
+
|
|
92
|
+
/** The owner record; `'none'` when there is no file or no valid record; `'unreadable'` on any other error. */
|
|
93
|
+
function readOwner(lock: string): Owner | 'none' | 'unreadable' {
|
|
94
|
+
let raw: string;
|
|
95
|
+
try {
|
|
96
|
+
raw = fs.readFileSync(ownerPath(lock), 'utf-8');
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return errno(e) === 'ENOENT' ? 'none' : 'unreadable';
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const o = JSON.parse(raw) as Partial<Owner>;
|
|
102
|
+
if (typeof o.pid === 'number' && typeof o.started_at === 'number' && typeof o.nonce === 'string') return o as Owner;
|
|
103
|
+
} catch {
|
|
104
|
+
/* a torn write; fall through to the age rule */
|
|
105
|
+
}
|
|
106
|
+
return 'none';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* True only on positive evidence: the owner's process is gone, the lock has
|
|
111
|
+
* vanished, or an owner-less lock is provably older than the threshold. A lock
|
|
112
|
+
* or owner record that cannot be inspected is treated as live.
|
|
113
|
+
*/
|
|
114
|
+
export function isStale(lock: string, now: number = Date.now()): boolean {
|
|
115
|
+
const owner = readOwner(lock);
|
|
116
|
+
if (owner === 'unreadable') return false;
|
|
117
|
+
if (owner !== 'none') {
|
|
118
|
+
// Only an owner that NAMES this host is judged by its PID. Another host's
|
|
119
|
+
// PID means nothing here, and a record without a host cannot be placed,
|
|
120
|
+
// so both are judged by age alone (see MAX_LOCK_AGE_MS).
|
|
121
|
+
if (owner.host !== os.hostname()) return now - owner.started_at > MAX_LOCK_AGE_MS;
|
|
122
|
+
return !pidAlive(owner.pid);
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
return now - fs.statSync(lock).mtimeMs > STALE_LOCK_MS;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return errno(e) === 'ENOENT';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function take(lock: string): () => void {
|
|
132
|
+
const nonce = randomBytes(16).toString('hex');
|
|
133
|
+
fs.writeFileSync(ownerPath(lock), JSON.stringify({ pid: process.pid, started_at: Date.now(), nonce, host: os.hostname() }));
|
|
134
|
+
return () => {
|
|
135
|
+
const owner = readOwner(lock);
|
|
136
|
+
if (typeof owner === 'string' || owner.nonce !== nonce) return;
|
|
137
|
+
fs.rmSync(ownerPath(lock), { force: true });
|
|
138
|
+
fs.rmSync(lock, { recursive: true, force: true });
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const reclaimMutexPath = (lock: string): string => `${lock}.reclaim`;
|
|
143
|
+
|
|
144
|
+
/** True when this process holds the lock (the CLI took it before calling in). */
|
|
145
|
+
export function heldByThisProcess(root: string): boolean {
|
|
146
|
+
const owner = readOwner(path.join(root, LOCK_REL));
|
|
147
|
+
return typeof owner !== 'string' && owner.pid === process.pid;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Take the lock, or return null when another refresh holds it.
|
|
152
|
+
*
|
|
153
|
+
* A stale lock is removed only while holding a second mkdir mutex, and only
|
|
154
|
+
* after staleness is checked AGAIN under it. A reclaimer that judged the lock
|
|
155
|
+
* stale and was then overtaken finds the successor's live lock on that second
|
|
156
|
+
* check and declines, so it can never delete a lock created after it looked.
|
|
157
|
+
* A reclaim mutex left by a crash inside that short section is never removed
|
|
158
|
+
* automatically: no check before a removal can prove which mutex instance it
|
|
159
|
+
* removes, and removing a successor's would let two reclaimers in (ratification,
|
|
160
|
+
* 2026-10-24). Stale locks then stay held — lost freshness, never lost exclusion
|
|
161
|
+
* — and `code-graph auto-refresh status` names the leftover for a human to remove.
|
|
162
|
+
* `afterStaleCheck` exists for the interleaving test only.
|
|
163
|
+
*/
|
|
164
|
+
export function acquire(root: string, afterStaleCheck?: () => void): (() => void) | null {
|
|
165
|
+
const lock = path.join(root, LOCK_REL);
|
|
166
|
+
fs.mkdirSync(path.dirname(lock), { recursive: true });
|
|
167
|
+
try {
|
|
168
|
+
fs.mkdirSync(lock);
|
|
169
|
+
return take(lock);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
if (errno(e) !== 'EEXIST' || !isStale(lock)) return null;
|
|
172
|
+
}
|
|
173
|
+
afterStaleCheck?.();
|
|
174
|
+
const mutex = reclaimMutexPath(lock);
|
|
175
|
+
try {
|
|
176
|
+
fs.mkdirSync(mutex);
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
if (!isStale(lock)) return null;
|
|
182
|
+
fs.rmSync(ownerPath(lock), { force: true });
|
|
183
|
+
fs.rmSync(lock, { recursive: true, force: true });
|
|
184
|
+
try {
|
|
185
|
+
fs.mkdirSync(lock);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return take(lock);
|
|
190
|
+
} finally {
|
|
191
|
+
fs.rmSync(mutex, { recursive: true, force: true });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -108,9 +108,10 @@ function isRecord(v: unknown): v is Record<string, unknown> {
|
|
|
108
108
|
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
/** Normalise a repo-relative path fragment: no leading `./`, no trailing
|
|
111
|
+
/** Normalise a repo-relative path fragment: no leading `./`, no trailing `/`, and `.` is the root (`''`). */
|
|
112
112
|
function normalizeFragment(p: string): string {
|
|
113
|
-
|
|
113
|
+
const s = p.replace(/^(\.\/)+/, '').replace(/\/+$/, '');
|
|
114
|
+
return s === '.' ? '' : s;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
/**
|
|
@@ -119,14 +120,19 @@ function normalizeFragment(p: string): string {
|
|
|
119
120
|
* `baseUrl` is resolved relative to the tsconfig's own directory, which for a
|
|
120
121
|
* root tsconfig is the repo root — so `baseUrl: "./src"` plus `"@shared/*":
|
|
121
122
|
* ["shared/*"]` yields the repo-relative prefix `src/shared/`.
|
|
123
|
+
*
|
|
124
|
+
* A `baseUrl` also resolves every other non-relative specifier against
|
|
125
|
+
* itself, which TypeScript does after the `paths` entries: `baseUrl: "."`
|
|
126
|
+
* makes `components/cart` the file `components/cart.tsx`. That fallback is the
|
|
127
|
+
* last rule, an empty wildcard prefix, so a `paths` entry always wins.
|
|
122
128
|
*/
|
|
123
129
|
export function aliasRulesFrom(tsconfig: unknown): AliasRule[] {
|
|
124
130
|
if (!isRecord(tsconfig)) return [];
|
|
125
131
|
const co = tsconfig['compilerOptions'];
|
|
126
132
|
if (!isRecord(co)) return [];
|
|
127
|
-
const paths = co['paths'];
|
|
128
|
-
|
|
129
|
-
const baseUrl =
|
|
133
|
+
const paths = isRecord(co['paths']) ? co['paths'] : {};
|
|
134
|
+
const hasBaseUrl = typeof co['baseUrl'] === 'string';
|
|
135
|
+
const baseUrl = hasBaseUrl ? normalizeFragment(co['baseUrl'] as string) : '';
|
|
130
136
|
const rules: AliasRule[] = [];
|
|
131
137
|
for (const [pattern, valueRaw] of Object.entries(paths)) {
|
|
132
138
|
if (!Array.isArray(valueRaw)) continue;
|
|
@@ -136,7 +142,8 @@ export function aliasRulesFrom(tsconfig: unknown): AliasRule[] {
|
|
|
136
142
|
for (const v of valueRaw) {
|
|
137
143
|
if (typeof v !== 'string') continue;
|
|
138
144
|
const tgt = wildcard ? (v.endsWith('*') ? v.slice(0, -1) : v) : v;
|
|
139
|
-
const
|
|
145
|
+
const frag = normalizeFragment(tgt);
|
|
146
|
+
const joined = baseUrl && frag ? `${baseUrl}/${frag}` : baseUrl || frag;
|
|
140
147
|
targets.push(joined.replace(/\/+$/, ''));
|
|
141
148
|
}
|
|
142
149
|
if (targets.length) rules.push({ prefix, wildcard, targets });
|
|
@@ -144,6 +151,7 @@ export function aliasRulesFrom(tsconfig: unknown): AliasRule[] {
|
|
|
144
151
|
// Longest prefix first: `@shared/deep/` must beat `@shared/` when both are
|
|
145
152
|
// declared, which is TypeScript's own rule and not a preference.
|
|
146
153
|
rules.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
154
|
+
if (hasBaseUrl) rules.push({ prefix: '', wildcard: true, targets: [baseUrl] });
|
|
147
155
|
return rules;
|
|
148
156
|
}
|
|
149
157
|
|
|
@@ -196,9 +204,13 @@ export function resolveAlias(spec: string, config: ResolutionConfig, fileIds: Re
|
|
|
196
204
|
for (const rule of config.aliases) {
|
|
197
205
|
if (rule.wildcard) {
|
|
198
206
|
if (!spec.startsWith(rule.prefix)) continue;
|
|
207
|
+
// The baseUrl fallback never claims a relative or absolute specifier.
|
|
208
|
+
if (rule.prefix === '' && /^[./]/.test(spec)) continue;
|
|
199
209
|
const rest = spec.slice(rule.prefix.length);
|
|
200
210
|
for (const t of rule.targets) {
|
|
201
|
-
const
|
|
211
|
+
const candidate = rest && t ? `${t}/${rest}` : rest || t;
|
|
212
|
+
if (!candidate) continue;
|
|
213
|
+
const hit = firstExisting(candidate, fileIds);
|
|
202
214
|
if (hit) return hit;
|
|
203
215
|
}
|
|
204
216
|
} else {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* still reads. A malformed buffer throws; the caller treats that as "no SCIP".
|
|
15
15
|
*/
|
|
16
16
|
import type { CodeEdge, CodeNode } from './types.js';
|
|
17
|
-
import {
|
|
17
|
+
import { isStatedResolution } from './types.js';
|
|
18
18
|
|
|
19
19
|
export interface ScipOccurrence {
|
|
20
20
|
/** 0-based start line. */
|
|
@@ -192,7 +192,7 @@ export function supersededByScip(scip: readonly CodeEdge[], nodes: readonly Code
|
|
|
192
192
|
named.set(e.source, set);
|
|
193
193
|
}
|
|
194
194
|
return (e: CodeEdge): boolean => {
|
|
195
|
-
if (
|
|
195
|
+
if (isStatedResolution(e.resolved_via)) return false;
|
|
196
196
|
const set = named.get(e.source);
|
|
197
197
|
if (!set) return false;
|
|
198
198
|
const target = label.get(e.target) ?? e.target.replace(/^symbol:/, '');
|
|
@@ -52,8 +52,11 @@ import type { CodeEdge, CodeGraph, CodeNode } from './types.js';
|
|
|
52
52
|
* back cleanly, answer `tests-for` with the empty set, and the caller could not
|
|
53
53
|
* tell "nothing tests this" from "this twin predates the relation". An index
|
|
54
54
|
* that answers a question it cannot answer is worse than one that refuses.
|
|
55
|
+
*
|
|
56
|
+
* 4 → 5 (`road-to-graph-providers-for-every-stack` 2.1): `edges` gained
|
|
57
|
+
* `provider_id`, the convention provider that drew a convention edge.
|
|
55
58
|
*/
|
|
56
|
-
export const GRAPH_STORE_VERSION =
|
|
59
|
+
export const GRAPH_STORE_VERSION = 5;
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
62
|
* Edge count at or above which the indexed read path replaces the blob path.
|
|
@@ -101,7 +104,7 @@ export function emitSqliteTwin(graph: CodeGraph, serialized: string, jsonPath: s
|
|
|
101
104
|
db.exec(
|
|
102
105
|
'CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);' +
|
|
103
106
|
'CREATE TABLE nodes (id TEXT PRIMARY KEY, label TEXT NOT NULL, kind TEXT, source_file TEXT, l0 INTEGER, l1 INTEGER, l2 INTEGER, l3 INTEGER);' +
|
|
104
|
-
'CREATE TABLE edges (source TEXT NOT NULL, target TEXT NOT NULL, relation TEXT NOT NULL, confidence TEXT NOT NULL, candidates TEXT, ambiguity_reason TEXT, resolved_via TEXT, provider TEXT);' +
|
|
107
|
+
'CREATE TABLE edges (source TEXT NOT NULL, target TEXT NOT NULL, relation TEXT NOT NULL, confidence TEXT NOT NULL, candidates TEXT, ambiguity_reason TEXT, resolved_via TEXT, provider TEXT, provider_id TEXT);' +
|
|
105
108
|
'CREATE INDEX edges_source ON edges(source);' +
|
|
106
109
|
'CREATE INDEX edges_target ON edges(target);' +
|
|
107
110
|
// Seed resolution and the changed-file seed set are the two
|
|
@@ -140,8 +143,8 @@ export function emitSqliteTwin(graph: CodeGraph, serialized: string, jsonPath: s
|
|
|
140
143
|
);
|
|
141
144
|
}
|
|
142
145
|
const insEdge = db.prepare(
|
|
143
|
-
'INSERT INTO edges (source, target, relation, confidence, candidates, ambiguity_reason, resolved_via, provider) ' +
|
|
144
|
-
'VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
|
146
|
+
'INSERT INTO edges (source, target, relation, confidence, candidates, ambiguity_reason, resolved_via, provider, provider_id) ' +
|
|
147
|
+
'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
145
148
|
);
|
|
146
149
|
for (const e of graph.edges) {
|
|
147
150
|
insEdge.run(
|
|
@@ -153,6 +156,7 @@ export function emitSqliteTwin(graph: CodeGraph, serialized: string, jsonPath: s
|
|
|
153
156
|
e.ambiguity_reason ?? null,
|
|
154
157
|
e.resolved_via,
|
|
155
158
|
e.provider,
|
|
159
|
+
e.provider_id ?? null,
|
|
156
160
|
);
|
|
157
161
|
}
|
|
158
162
|
db.exec('COMMIT');
|
|
@@ -271,6 +275,7 @@ interface EdgeRow {
|
|
|
271
275
|
ambiguity_reason: string | null;
|
|
272
276
|
resolved_via: string | null;
|
|
273
277
|
provider: string | null;
|
|
278
|
+
provider_id: string | null;
|
|
274
279
|
}
|
|
275
280
|
|
|
276
281
|
function toNode(r: NodeRow): CodeNode {
|
|
@@ -303,6 +308,7 @@ function toEdge(r: EdgeRow): CodeEdge {
|
|
|
303
308
|
if (r.ambiguity_reason === 'receiver-unknown' || r.ambiguity_reason === 'hierarchy-unresolved') {
|
|
304
309
|
e.ambiguity_reason = r.ambiguity_reason;
|
|
305
310
|
}
|
|
311
|
+
if (r.provider_id) e.provider_id = r.provider_id;
|
|
306
312
|
return e;
|
|
307
313
|
}
|
|
308
314
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/** Launch set — the stacks this suite's consumers actually run. */
|
|
12
|
-
export type Lang = 'php' | 'typescript' | 'javascript' | 'python' | 'go' | 'java';
|
|
12
|
+
export type Lang = 'php' | 'typescript' | 'javascript' | 'python' | 'go' | 'java' | 'csharp';
|
|
13
13
|
|
|
14
14
|
/** Same scale as `discovery_graph.ts::EdgeConfidence` (reused, not forked). */
|
|
15
15
|
export type EdgeConfidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
|
|
@@ -29,11 +29,12 @@ export type EdgeConfidence = 'EXTRACTED' | 'INFERRED' | 'AMBIGUOUS';
|
|
|
29
29
|
* grammar can see whether a file is a test.
|
|
30
30
|
*/
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
* (`
|
|
34
|
-
* renders, a
|
|
35
|
-
* file it reads, a migration to its table, a model to its table, a
|
|
36
|
-
* the model
|
|
32
|
+
* `routes-to` … `injects` are the convention links a registered provider reads
|
|
33
|
+
* by rule (`providers/`, ADR-298): a route to its handler, code to the template
|
|
34
|
+
* it renders, a template to the template it includes, code to the translation
|
|
35
|
+
* or config file it reads, a migration to its table, a model to its table, a
|
|
36
|
+
* model to the model a relation names, a class to the service a container
|
|
37
|
+
* injects into it. The vocabulary is fixed and names no framework.
|
|
37
38
|
*/
|
|
38
39
|
export type Relation =
|
|
39
40
|
| 'calls'
|
|
@@ -49,6 +50,7 @@ export type Relation =
|
|
|
49
50
|
| 'migrates'
|
|
50
51
|
| 'persists'
|
|
51
52
|
| 'relates'
|
|
53
|
+
| 'injects'
|
|
52
54
|
/** A Markdown document links to, or names, the file (`docs_layer.ts`). */
|
|
53
55
|
| 'documents';
|
|
54
56
|
|
|
@@ -86,12 +88,20 @@ export type ResolvedVia =
|
|
|
86
88
|
| 'path-alias'
|
|
87
89
|
| 'psr4'
|
|
88
90
|
| 'route-table'
|
|
89
|
-
/** A
|
|
91
|
+
/** A template directive, a render / translation / config call naming a file, or a Markdown link. */
|
|
90
92
|
| 'directive-scan'
|
|
91
|
-
/** A
|
|
93
|
+
/** A schema or migration call creating a table, or a model mapped to such a table. */
|
|
92
94
|
| 'schema-call'
|
|
93
|
-
/**
|
|
94
|
-
| '
|
|
95
|
+
/** An ORM relation declared inside a model, naming the related model. */
|
|
96
|
+
| 'orm-relation'
|
|
97
|
+
/** A dependency-injection declaration naming the injected class. */
|
|
98
|
+
| 'di-binding'
|
|
99
|
+
/** A string literal equal to exactly one file's path (the default pass). */
|
|
100
|
+
| 'literal-path'
|
|
101
|
+
/** A dotted name equal to exactly one template's path (the default pass). */
|
|
102
|
+
| 'literal-dotted'
|
|
103
|
+
/** A page module whose path is its route (the default pass). */
|
|
104
|
+
| 'fs-route'
|
|
95
105
|
/** Stated by a compiler-grade SCIP index the project ships (`scip_reader.ts`). */
|
|
96
106
|
| 'compiler-index'
|
|
97
107
|
| 'test-import'
|
|
@@ -131,33 +141,40 @@ export type EdgeProvider =
|
|
|
131
141
|
| 'scip';
|
|
132
142
|
|
|
133
143
|
/**
|
|
134
|
-
*
|
|
144
|
+
* Every mechanism, classified as a statement (the source names the target) or
|
|
145
|
+
* a guess. Trust is this ALLOWLIST (ADR-298): it replaced a denylist of guesses,
|
|
146
|
+
* under which a new mechanism was trusted merely because nobody had listed it —
|
|
147
|
+
* wrong once several providers emit edges. A string that is no member at all
|
|
148
|
+
* (an older or foreign graph) is a guess. The type below fails to compile when a
|
|
149
|
+
* `ResolvedVia` member is in neither list.
|
|
135
150
|
*
|
|
136
|
-
* `name-lookup` is the repo-wide same-name table (or a lookup that
|
|
137
|
-
* nothing); `dynamic` is a receiver the extractor cannot type
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* mechanism while its own evidence was a guess LAUNDERS the guess. A PHP
|
|
144
|
-
* `use Two\Mailer` with no `composer.json` binds by base name, so the `imports`
|
|
145
|
-
* edge is `name-lookup` and may point at `One\Mailer`; deriving a `tests` edge
|
|
146
|
-
* from it and tagging that `test-import` made a wrong target trustworthy to
|
|
147
|
-
* every consumer of the filter. One set, two readers, and the build pass can no
|
|
148
|
-
* longer disagree with the query tier about what a guess is.
|
|
149
|
-
*
|
|
150
|
-
* The accepted-edge filter is a DENYLIST, so a new value is accepted by
|
|
151
|
-
* default — which is exactly backwards for `foreign`. It must be listed here
|
|
152
|
-
* or a consumer's graph would silently answer the gate verbs as if this
|
|
153
|
-
* package had resolved its edges. (Kept out of the literal: esbuild carries a
|
|
154
|
-
* comment inside an expression into the hook bundle.)
|
|
151
|
+
* The guesses: `name-lookup` is the repo-wide same-name table (or a lookup that
|
|
152
|
+
* found nothing); `dynamic` is a receiver the extractor cannot type; `foreign`
|
|
153
|
+
* was read from another tool's graph and only mapped. The BUILD pass needs the
|
|
154
|
+
* split as much as the query tier: a derived edge stamped with a trustworthy
|
|
155
|
+
* mechanism while its own evidence was a guess LAUNDERS the guess — a PHP
|
|
156
|
+
* `use Two\Mailer` with no `composer.json` binds by base name, and a `tests`
|
|
157
|
+
* edge derived from it made a wrong target trustworthy to every consumer.
|
|
155
158
|
*/
|
|
156
|
-
|
|
159
|
+
// One space-separated string per list: this module ships in the hook bundle,
|
|
160
|
+
// where an array literal costs a line per entry. `Words` splits it in the type.
|
|
161
|
+
const STATED_WORDS =
|
|
162
|
+
'same-file import-specifier path-alias psr4 route-table directive-scan schema-call orm-relation di-binding compiler-index test-import literal-path literal-dotted fs-route' as const;
|
|
163
|
+
const GUESS_WORDS = 'name-lookup dynamic foreign' as const;
|
|
164
|
+
type Words<S extends string> = S extends `${infer H} ${infer T}` ? H | Words<T> : S;
|
|
165
|
+
/** Mechanisms in neither list; must be `never` (a test asserts it). */
|
|
166
|
+
export type UnclassifiedResolvedVia = Exclude<ResolvedVia, Words<typeof STATED_WORDS> | Words<typeof GUESS_WORDS>>;
|
|
167
|
+
/** Words that are no mechanism at all; must be `never` too. */
|
|
168
|
+
export type UnknownTrustWord = Exclude<Words<typeof STATED_WORDS> | Words<typeof GUESS_WORDS>, ResolvedVia>;
|
|
169
|
+
/** Fails to compile while either list is incomplete or carries a stray word. */
|
|
170
|
+
export type TrustListsComplete = [UnclassifiedResolvedVia | UnknownTrustWord] extends [never] ? true : never;
|
|
171
|
+
export const TRUST_LISTS_COMPLETE: TrustListsComplete = true;
|
|
172
|
+
export const STATED_RESOLVED_VIA: ReadonlySet<string> = new Set(STATED_WORDS.split(' '));
|
|
173
|
+
export const GUESS_RESOLVED_VIA: ReadonlySet<ResolvedVia> = new Set(GUESS_WORDS.split(' ') as ResolvedVia[]);
|
|
157
174
|
|
|
158
|
-
/** Does this edge's target rest on a stated fact
|
|
175
|
+
/** Does this edge's target rest on a stated fact? Anything not listed as stated is a guess. */
|
|
159
176
|
export function isStatedResolution(resolved_via: ResolvedVia): boolean {
|
|
160
|
-
return
|
|
177
|
+
return STATED_RESOLVED_VIA.has(resolved_via);
|
|
161
178
|
}
|
|
162
179
|
|
|
163
180
|
export interface CodeNode {
|
|
@@ -188,9 +205,9 @@ export interface CodeNode {
|
|
|
188
205
|
| 'type'
|
|
189
206
|
| 'enum'
|
|
190
207
|
| 'skipped'
|
|
191
|
-
/** A declared route, `<VERB> <path>` (
|
|
208
|
+
/** A declared route or dispatched entry, `<VERB> <path>` (a convention provider). */
|
|
192
209
|
| 'route'
|
|
193
|
-
/** A database table a migration creates (
|
|
210
|
+
/** A database table a migration or schema creates (a convention provider). */
|
|
194
211
|
| 'table';
|
|
195
212
|
source_file: string;
|
|
196
213
|
/** 1-based [startLine, startCol, endLine, endCol]; [] for synthetic nodes. */
|
|
@@ -240,6 +257,14 @@ export interface CodeEdge {
|
|
|
240
257
|
* target.
|
|
241
258
|
*/
|
|
242
259
|
ambiguity_reason?: 'receiver-unknown' | 'hierarchy-unresolved';
|
|
260
|
+
/** The convention provider that drew this edge (ADR-298); absent on every other edge. */
|
|
261
|
+
provider_id?: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** A convention provider active in this build, with the package roots it applied to. */
|
|
265
|
+
export interface ActiveProviderRecord {
|
|
266
|
+
id: string;
|
|
267
|
+
roots: string[];
|
|
243
268
|
}
|
|
244
269
|
|
|
245
270
|
export interface CodeGraph {
|
|
@@ -267,13 +292,15 @@ export interface CodeGraph {
|
|
|
267
292
|
suppressed_edge_counts: {
|
|
268
293
|
dynamic_no_candidate: number;
|
|
269
294
|
/**
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* method of the same name. Present only
|
|
273
|
-
*
|
|
295
|
+
* Base edges a convention provider proved false and removed, per
|
|
296
|
+
* provider id — e.g. a fluent route-builder call that name lookup had
|
|
297
|
+
* bound to an unrelated in-repo method of the same name. Present only
|
|
298
|
+
* when some count is non-zero.
|
|
274
299
|
*/
|
|
275
|
-
|
|
300
|
+
contested_by_provider?: Record<string, number>;
|
|
276
301
|
};
|
|
302
|
+
/** Convention providers active in this build; present only when one was. */
|
|
303
|
+
providers?: ActiveProviderRecord[];
|
|
277
304
|
nodes: CodeNode[];
|
|
278
305
|
edges: CodeEdge[];
|
|
279
306
|
}
|
|
@@ -307,8 +334,13 @@ export interface CodeGraph {
|
|
|
307
334
|
* Bumped 4 → 5 by `road-to-a-map-that-draws-itself` 1.3: same-file
|
|
308
335
|
* same-name declarations now carry `@2`, `@3` suffixes, so a cache written
|
|
309
336
|
* at v4 holds ids a v5 build would not produce.
|
|
337
|
+
*
|
|
338
|
+
* Bumped 5 → 6 by `road-to-graph-providers-for-every-stack` 2.1: framework
|
|
339
|
+
* names left the schema (`orm-relation`, `contested_by_provider`), provider
|
|
340
|
+
* edges carry `provider_id`, the graph records its active providers, and the
|
|
341
|
+
* vocabulary gained `injects` / `di-binding`.
|
|
310
342
|
*/
|
|
311
|
-
export const SCHEMA_VERSION =
|
|
343
|
+
export const SCHEMA_VERSION = 6;
|
|
312
344
|
|
|
313
345
|
/** Deterministic per-file byte cap — files above this become a SKIPPED node. */
|
|
314
346
|
export const MAX_FILE_BYTES = 1_000_000;
|
|
@@ -329,6 +361,7 @@ export const EXT_LANG: Readonly<Record<string, Lang>> = {
|
|
|
329
361
|
'.py': 'python',
|
|
330
362
|
'.go': 'go',
|
|
331
363
|
'.java': 'java',
|
|
364
|
+
'.cs': 'csharp',
|
|
332
365
|
'.vue': 'typescript',
|
|
333
366
|
};
|
|
334
367
|
|
|
@@ -340,6 +373,7 @@ export const GRAMMAR_WASM: Readonly<Record<Lang, string>> = {
|
|
|
340
373
|
python: 'tree-sitter-python.wasm',
|
|
341
374
|
go: 'tree-sitter-go.wasm',
|
|
342
375
|
java: 'tree-sitter-java.wasm',
|
|
376
|
+
csharp: 'tree-sitter-c_sharp.wasm',
|
|
343
377
|
};
|
|
344
378
|
|
|
345
379
|
/** The single ABI version the pinned grammar set is built against. */
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Schema gate — validate a graph.json (native cache or consumer-shipped)
|
|
3
3
|
* before any consumer trusts it. Pure, no IO.
|
|
4
4
|
*/
|
|
5
|
-
import type
|
|
5
|
+
import { type CodeGraph, GUESS_RESOLVED_VIA, STATED_RESOLVED_VIA } from './types.js';
|
|
6
6
|
|
|
7
7
|
/** One space-separated string per vocabulary: this module ships in the hook bundle, paid on every dispatch. */
|
|
8
8
|
const words = (s: string): ReadonlySet<string> => new Set(s.split(' '));
|
|
@@ -10,15 +10,11 @@ const words = (s: string): ReadonlySet<string> => new Set(s.split(' '));
|
|
|
10
10
|
/** Mirrors `Relation` in types.ts. `tests` is derived by the build pass (3.2),
|
|
11
11
|
* not extracted, and is validated on exactly the same terms as the rest. */
|
|
12
12
|
const RELATIONS = words(
|
|
13
|
-
'calls imports uses inherits member tests routes-to renders includes reads migrates persists relates documents',
|
|
13
|
+
'calls imports uses inherits member tests routes-to renders includes reads migrates persists relates injects documents',
|
|
14
14
|
);
|
|
15
15
|
const CONFIDENCES = new Set(['EXTRACTED', 'INFERRED', 'AMBIGUOUS']);
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
// `foreign`: adapted from a consumer's own graph file; see `foreign.ts`.
|
|
19
|
-
const RESOLVED_VIA = words(
|
|
20
|
-
'same-file import-specifier path-alias psr4 route-table directive-scan schema-call eloquent-relation compiler-index test-import name-lookup dynamic foreign',
|
|
21
|
-
);
|
|
16
|
+
/** Every `ResolvedVia` member: the trust lists in types.ts, so the two cannot drift. */
|
|
17
|
+
const RESOLVED_VIA: ReadonlySet<string> = new Set([...STATED_RESOLVED_VIA, ...GUESS_RESOLVED_VIA]);
|
|
22
18
|
const PROVIDERS = new Set(['native', 'foreign', 'scip']);
|
|
23
19
|
const KINDS = words('file class interface trait function method constant type enum skipped route table');
|
|
24
20
|
|
|
@@ -74,6 +70,8 @@ export function validateGraph(g: unknown): ValidateResult {
|
|
|
74
70
|
if (ee['provider'] === undefined) push(`edge[${i}].provider is required`);
|
|
75
71
|
else if (!PROVIDERS.has(ee['provider'] as string))
|
|
76
72
|
push(`edge[${i}].provider invalid: ${String(ee['provider'])}`);
|
|
73
|
+
if (ee['provider_id'] !== undefined && typeof ee['provider_id'] !== 'string')
|
|
74
|
+
push(`edge[${i}].provider_id must be a string`);
|
|
77
75
|
// a source must be a known node; targets may be unresolved `symbol:` refs
|
|
78
76
|
if (typeof ee['source'] === 'string' && !ids.has(ee['source'] as string))
|
|
79
77
|
push(`edge[${i}].source is not a known node: ${String(ee['source'])}`);
|
package/src/scripts/condense.ts
CHANGED
|
@@ -2150,19 +2150,19 @@ export const PLUGIN_POINTER_SLUG = 'install-agent-config';
|
|
|
2150
2150
|
export const PLUGIN_POINTER_SKILL_BODY = `---
|
|
2151
2151
|
name: install-agent-config
|
|
2152
2152
|
description: >-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
Use when the user asks to install, set up, or upgrade agent-config, or
|
|
2153
|
+
Governance rules, skills and commands for AI coding agents in Claude Code, Cursor, GitHub Copilot and Gemini CLI —
|
|
2154
|
+
install or upgrade the full event4u agent-config suite. No plugin that ships this pointer is the full suite (the Claude
|
|
2155
|
+
Code one adds hooks, the Copilot one a read-only MCP server); ALL content installs via \`npx -y @event4u/agent-config init\`.
|
|
2156
|
+
Use when the user asks to install, set up, or upgrade agent-config, wants coding-agent rules or skills, or
|
|
2157
2157
|
wonders why this plugin contains no other skills.
|
|
2158
2158
|
---
|
|
2159
2159
|
|
|
2160
2160
|
# Install agent-config
|
|
2161
2161
|
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2162
|
+
The plugin that delivered this skill is a **discovery entry**, not the suite. The Claude Code plugin adds hooks that
|
|
2163
|
+
run only alongside a global npm install of the package (never a project-local one) — on a plugin-only install no hook
|
|
2164
|
+
runs, so the \`fail_closed\` guards (\`block-no-verify\`, \`block-kernel-rule-writes\`, \`block-plumbing-writes\`) are
|
|
2165
|
+
**off**; the session start says so once. The Copilot plugin adds a read-only MCP server and no hooks.
|
|
2166
2166
|
|
|
2167
2167
|
## Install (canonical, one command)
|
|
2168
2168
|
|
|
@@ -2172,7 +2172,7 @@ npx -y @event4u/agent-config init
|
|
|
2172
2172
|
|
|
2173
2173
|
The installer writes the full content projection, registers the dispatcher
|
|
2174
2174
|
hooks as a managed block in \`~/.claude/settings.json\` (byte-identical to
|
|
2175
|
-
|
|
2175
|
+
the Claude Code plugin's hooks, so nothing double-fires), and keeps everything
|
|
2176
2176
|
upgradeable via \`agent-config upgrade\`.
|
|
2177
2177
|
|
|
2178
2178
|
## Already installed both?
|
|
@@ -33,7 +33,7 @@ import * as fs from 'node:fs';
|
|
|
33
33
|
import * as path from 'node:path';
|
|
34
34
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
35
35
|
|
|
36
|
-
import {
|
|
36
|
+
import { toJsonSchema7 } from '../shared/zodJsonSchema.js';
|
|
37
37
|
|
|
38
38
|
import { settingsSchema } from '../server/schemas/settings.js';
|
|
39
39
|
import {
|
|
@@ -97,11 +97,7 @@ export function renderProse(value: string | undefined): string {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
export function buildRows(contractMarkdown: string): ReferenceRow[] {
|
|
100
|
-
const jsonSchema =
|
|
101
|
-
name: 'AgentSettings',
|
|
102
|
-
$refStrategy: 'none',
|
|
103
|
-
target: 'jsonSchema7',
|
|
104
|
-
}) as Parameters<typeof flattenSurface>[0];
|
|
100
|
+
const jsonSchema = toJsonSchema7(settingsSchema, 'AgentSettings') as Parameters<typeof flattenSurface>[0];
|
|
105
101
|
// The version is a required parameter of the shared flattener and is
|
|
106
102
|
// discarded here on purpose — see the module header.
|
|
107
103
|
const surface = flattenSurface(jsonSchema, 'unversioned');
|
|
@@ -45,6 +45,10 @@ export const HOOK_CHUNKS = Object.freeze([
|
|
|
45
45
|
entry: 'src/scripts/hooks/instructions_observer_hook.ts',
|
|
46
46
|
outfile: 'dist/hooks/instructions_observer.chunk.cjs',
|
|
47
47
|
}),
|
|
48
|
+
Object.freeze({
|
|
49
|
+
entry: 'src/scripts/hooks/code_graph_refresh_hook.ts',
|
|
50
|
+
outfile: 'dist/hooks/code_graph_refresh.chunk.cjs',
|
|
51
|
+
}),
|
|
48
52
|
]);
|
|
49
53
|
|
|
50
54
|
/**
|