@event4u/agent-config 17.2.0 → 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 +174 -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,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The listing manifests outside the npm tarball that start the MCP server
|
|
3
|
+
* through `npx`: the GitHub Copilot discovery plugin and the Gemini CLI
|
|
4
|
+
* extension. Both carry the package version twice — in `version` and pinned
|
|
5
|
+
* inside the `npx` argument — and a crawler installs whatever the default
|
|
6
|
+
* branch says, so both copies follow `package.json` through the release.
|
|
7
|
+
*
|
|
8
|
+
* One module because three readers must agree on the same two facts: the
|
|
9
|
+
* release writes them (`release_env`), the release-PR content gate admits a
|
|
10
|
+
* change to them and nothing else (`check_release_manifest_content`), and the
|
|
11
|
+
* marketplace lint fails a tree where they drifted (`lint_marketplace`).
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
|
|
16
|
+
export const LISTING_MANIFESTS = ['.github/plugin/plugin.json', 'gemini-extension.json'] as const;
|
|
17
|
+
|
|
18
|
+
export const PACKAGE_NAME = '@event4u/agent-config';
|
|
19
|
+
|
|
20
|
+
/** An `npx` argument pinning this package to an exact release, prereleases included. */
|
|
21
|
+
export const PINNED_PACKAGE_ARG_RE = /^@event4u\/agent-config@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
|
22
|
+
|
|
23
|
+
export function pinnedPackageArg(version: string): string {
|
|
24
|
+
return `${PACKAGE_NAME}@${version}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Obj = Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
function _isObject(v: unknown): v is Obj {
|
|
30
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Every `[serverName, args]` pair under `mcpServers`, args left `unknown` for the caller to judge. */
|
|
34
|
+
export function mcpServerArgs(manifest: unknown): Array<[string, unknown]> {
|
|
35
|
+
if (!_isObject(manifest) || !_isObject(manifest['mcpServers'])) return [];
|
|
36
|
+
return Object.entries(manifest['mcpServers']).map(([name, server]) => [
|
|
37
|
+
name,
|
|
38
|
+
_isObject(server) ? server['args'] : undefined,
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Every way `manifest` disagrees with `version`: the `version` field, and each
|
|
44
|
+
* server whose `npx` arguments do not pin exactly this release. An unpinned
|
|
45
|
+
* `@event4u/agent-config` argument is a finding too — it resolves to whatever
|
|
46
|
+
* is latest on the day a user installs, which is the drift being prevented.
|
|
47
|
+
*/
|
|
48
|
+
export function listingVersionFindings(label: string, manifest: unknown, version: string): string[] {
|
|
49
|
+
const out: string[] = [];
|
|
50
|
+
const declared = _isObject(manifest) ? manifest['version'] : undefined;
|
|
51
|
+
if (declared !== version) {
|
|
52
|
+
out.push(`${label}: version \`${String(declared)}\` does not match package.json version \`${version}\``);
|
|
53
|
+
}
|
|
54
|
+
const servers = mcpServerArgs(manifest);
|
|
55
|
+
if (servers.length === 0) out.push(`${label}: declares no mcpServers entry`);
|
|
56
|
+
for (const [name, args] of servers) {
|
|
57
|
+
const own = Array.isArray(args)
|
|
58
|
+
? args.filter((a): a is string => typeof a === 'string' && a.startsWith(PACKAGE_NAME))
|
|
59
|
+
: [];
|
|
60
|
+
if (own.length !== 1 || own[0] !== pinnedPackageArg(version)) {
|
|
61
|
+
out.push(
|
|
62
|
+
`${label}: mcpServers.${name}.args must pin \`${pinnedPackageArg(version)}\` ` +
|
|
63
|
+
`(found ${own.length === 0 ? 'none' : own.map((a) => `\`${a}\``).join(', ')})`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Rewrite `version` and every pinned package argument; `jsonDump` is the release's own serializer. */
|
|
71
|
+
export function setListingManifestVersion(p: string, version: string, jsonDump: (v: unknown) => string): void {
|
|
72
|
+
const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as unknown;
|
|
73
|
+
if (_isObject(data) && 'version' in data) data['version'] = version;
|
|
74
|
+
for (const [, args] of mcpServerArgs(data)) {
|
|
75
|
+
if (!Array.isArray(args)) continue;
|
|
76
|
+
args.forEach((a, i) => {
|
|
77
|
+
if (typeof a === 'string' && PINNED_PACKAGE_ARG_RE.test(a)) args[i] = pinnedPackageArg(version);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
fs.writeFileSync(p, jsonDump(data) + '\n', 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function listingManifestPaths(root: string): string[] {
|
|
84
|
+
return LISTING_MANIFESTS.map((rel) => path.join(root, rel));
|
|
85
|
+
}
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
import * as fs from 'node:fs';
|
|
34
34
|
import * as path from 'node:path';
|
|
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 { computeSurfaceDelta, flattenSurface, type SettingsSurface, type SurfaceEntry } from '../../shared/settingsSurface.js';
|
|
@@ -50,11 +50,7 @@ import { RENAMED_KEYS, SETTINGS_REMOVAL_RULE } from './settings_renamed_keys.js'
|
|
|
50
50
|
* "keep a preview fast" rule was written about.
|
|
51
51
|
*/
|
|
52
52
|
function _jsonSchema(): Parameters<typeof flattenSurface>[0] {
|
|
53
|
-
return
|
|
54
|
-
name: 'AgentSettings',
|
|
55
|
-
$refStrategy: 'none',
|
|
56
|
-
target: 'jsonSchema7',
|
|
57
|
-
}) as Parameters<typeof flattenSurface>[0];
|
|
53
|
+
return toJsonSchema7(settingsSchema, 'AgentSettings') as Parameters<typeof flattenSurface>[0];
|
|
58
54
|
}
|
|
59
55
|
|
|
60
56
|
/** The one committed copy of the last release's surface. */
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* CLI:
|
|
24
24
|
*
|
|
25
|
-
* ./scripts-run src/scripts/adoption_status [--json] [--branch main] [--check-upstream]
|
|
25
|
+
* ./scripts-run src/scripts/adoption_status [--json] [--branch main] [--check-upstream] [--fail-on-action]
|
|
26
26
|
*
|
|
27
27
|
* `--check-upstream` reads each ledger row's upstream (`gh pr view` for a pull
|
|
28
28
|
* request, one `curl` for a listing page or registry query) and appends what it
|
|
@@ -30,10 +30,19 @@
|
|
|
30
30
|
* report exactly as before, and stderr names that mode plus every row whose
|
|
31
31
|
* `Last read` date is missing or older than the sheet's re-read threshold.
|
|
32
32
|
*
|
|
33
|
+
* With `--check-upstream`, a row whose sheet and upstream disagree in a way that
|
|
34
|
+
* needs a person also carries an action (`sheet-behind`, `needs-response`,
|
|
35
|
+
* `listing-lost`, `listing-stale`) next to its unchanged verdict, and the report
|
|
36
|
+
* names the next act. A submission is done when its entry is live, not when it
|
|
37
|
+
* was sent, so `--fail-on-action` turns any action into a non-zero exit for the
|
|
38
|
+
* daily watcher. Without `--check-upstream` there is nothing to judge and the
|
|
39
|
+
* flag is reported on stderr and ignored.
|
|
40
|
+
*
|
|
33
41
|
* Exit codes:
|
|
34
42
|
*
|
|
35
43
|
* 0 — printed successfully (status itself does not gate the exit).
|
|
36
44
|
* 1 — IO error reading the registry-submissions sheet.
|
|
45
|
+
* 2 — `--check-upstream --fail-on-action` and at least one row needs an action.
|
|
37
46
|
*/
|
|
38
47
|
|
|
39
48
|
import { spawnSync } from 'node:child_process';
|
|
@@ -243,25 +252,29 @@ interface Args {
|
|
|
243
252
|
json: boolean;
|
|
244
253
|
branch: string;
|
|
245
254
|
checkUpstream: boolean;
|
|
255
|
+
failOnAction: boolean;
|
|
246
256
|
}
|
|
247
257
|
|
|
248
258
|
export function parse_args(argv: string[]): Args {
|
|
249
259
|
let json = false;
|
|
250
260
|
let branch = 'main';
|
|
251
261
|
let checkUpstream = false;
|
|
262
|
+
let failOnAction = false;
|
|
252
263
|
for (let i = 0; i < argv.length; i++) {
|
|
253
264
|
const a = argv[i];
|
|
254
265
|
if (a === '--json') {
|
|
255
266
|
json = true;
|
|
256
267
|
} else if (a === '--check-upstream') {
|
|
257
268
|
checkUpstream = true;
|
|
269
|
+
} else if (a === '--fail-on-action') {
|
|
270
|
+
failOnAction = true;
|
|
258
271
|
} else if (a === '--branch') {
|
|
259
272
|
branch = argv[++i] ?? '';
|
|
260
273
|
} else if (a !== undefined && a.startsWith('--branch=')) {
|
|
261
274
|
branch = a.slice('--branch='.length);
|
|
262
275
|
}
|
|
263
276
|
}
|
|
264
|
-
return { json, branch, checkUpstream };
|
|
277
|
+
return { json, branch, checkUpstream, failOnAction };
|
|
265
278
|
}
|
|
266
279
|
|
|
267
280
|
export function main(argv: string[] = process.argv.slice(2)): number {
|
|
@@ -293,10 +306,21 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
293
306
|
for (const u of unread) {
|
|
294
307
|
process.stderr.write(` row ${u.id} ${u.registry}: ${u.label}\n`);
|
|
295
308
|
}
|
|
309
|
+
if (args.failOnAction) {
|
|
310
|
+
process.stderr.write(
|
|
311
|
+
'--fail-on-action ignored: actions are judged only with --check-upstream\n',
|
|
312
|
+
);
|
|
313
|
+
}
|
|
296
314
|
return 0;
|
|
297
315
|
}
|
|
298
316
|
|
|
299
|
-
const verdicts = judge_upstream(
|
|
317
|
+
const verdicts = judge_upstream(
|
|
318
|
+
rows,
|
|
319
|
+
live_upstream_reader,
|
|
320
|
+
today,
|
|
321
|
+
_releasedServer(),
|
|
322
|
+
parse_reread_threshold(registryText),
|
|
323
|
+
);
|
|
300
324
|
if (args.json) {
|
|
301
325
|
const base = JSON.parse(render_json(registryCounts, reports, ci, args.branch)) as Record<
|
|
302
326
|
string,
|
|
@@ -308,7 +332,32 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
308
332
|
process.stdout.write(render_text(registryCounts, reports, ci, args.branch) + '\n');
|
|
309
333
|
process.stdout.write(render_upstream_text(verdicts, unread) + '\n');
|
|
310
334
|
}
|
|
311
|
-
|
|
335
|
+
const code = upstream_exit_code(verdicts, args.failOnAction);
|
|
336
|
+
if (code !== 0) {
|
|
337
|
+
// On a runner the `::error::` prefix turns each line into an annotation
|
|
338
|
+
// naming the row, so the red job says what to do without opening the log.
|
|
339
|
+
const prefix = process.env.GITHUB_ACTIONS === 'true' ? '::error title=adoption_status::' : 'action: ';
|
|
340
|
+
for (const line of action_lines(verdicts)) {
|
|
341
|
+
process.stderr.write(prefix + line + '\n');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return code;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** `name` and `version` the registry should carry, from package.json. */
|
|
348
|
+
function _releasedServer(): ReleasedServer | undefined {
|
|
349
|
+
try {
|
|
350
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf-8')) as {
|
|
351
|
+
mcpName?: unknown;
|
|
352
|
+
version?: unknown;
|
|
353
|
+
};
|
|
354
|
+
if (typeof pkg.mcpName === 'string' && typeof pkg.version === 'string') {
|
|
355
|
+
return { name: pkg.mcpName, version: pkg.version };
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
// Without a readable package.json there is nothing to compare against.
|
|
359
|
+
}
|
|
360
|
+
return undefined;
|
|
312
361
|
}
|
|
313
362
|
|
|
314
363
|
// --- upstream reading --------------------------------------------------------
|
|
@@ -325,7 +374,15 @@ export interface LedgerRow {
|
|
|
325
374
|
}
|
|
326
375
|
|
|
327
376
|
export type UpstreamReading =
|
|
328
|
-
| {
|
|
377
|
+
| {
|
|
378
|
+
ok: true;
|
|
379
|
+
state: 'open' | 'closed' | 'merged' | 'listed' | 'not-listed';
|
|
380
|
+
at?: string;
|
|
381
|
+
/** `gh pr view` reviewDecision, e.g. `CHANGES_REQUESTED`; pull requests only. */
|
|
382
|
+
reviewDecision?: string;
|
|
383
|
+
/** Server name → the version the registry marks latest; registry queries only. */
|
|
384
|
+
latestVersions?: Record<string, string>;
|
|
385
|
+
}
|
|
329
386
|
| { ok: false; reason: string };
|
|
330
387
|
|
|
331
388
|
export type UpstreamReader = (row: LedgerRow) => UpstreamReading;
|
|
@@ -340,11 +397,25 @@ export type UpstreamVerdict =
|
|
|
340
397
|
| 'unreachable'
|
|
341
398
|
| 'no-upstream';
|
|
342
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Reported next to the verdict, never instead of it: the verdict says what the
|
|
402
|
+
* upstream reads, the action says the sheet and that reading disagree in a way a
|
|
403
|
+
* person has to resolve.
|
|
404
|
+
*/
|
|
405
|
+
export type ActionVerdict = 'sheet-behind' | 'needs-response' | 'listing-lost' | 'listing-stale' | 'read-overdue';
|
|
406
|
+
|
|
343
407
|
export interface RowVerdict {
|
|
344
408
|
id: string;
|
|
345
409
|
registry: string;
|
|
346
410
|
verdict: UpstreamVerdict;
|
|
347
411
|
detail: string;
|
|
412
|
+
action?: ActionVerdict;
|
|
413
|
+
next?: string;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export interface ReleasedServer {
|
|
417
|
+
name: string;
|
|
418
|
+
version: string;
|
|
348
419
|
}
|
|
349
420
|
|
|
350
421
|
export interface UnreadFinding {
|
|
@@ -451,6 +522,8 @@ export function judge_upstream(
|
|
|
451
522
|
rows: LedgerRow[],
|
|
452
523
|
reader: UpstreamReader,
|
|
453
524
|
today: string,
|
|
525
|
+
released?: ReleasedServer,
|
|
526
|
+
thresholdDays: number = DEFAULT_REREAD_THRESHOLD_DAYS,
|
|
454
527
|
): RowVerdict[] {
|
|
455
528
|
return rows.map((row): RowVerdict => {
|
|
456
529
|
const base = { id: row.id, registry: row.registry };
|
|
@@ -459,27 +532,116 @@ export function judge_upstream(
|
|
|
459
532
|
}
|
|
460
533
|
const r = reader(row);
|
|
461
534
|
if (!r.ok) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
return {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const age = row.date === '' ? null : _daysBetween(row.date, today);
|
|
474
|
-
if (row.status === 'submitted' && age !== null && age >= STALLED_AFTER_DAYS) {
|
|
475
|
-
return { ...base, verdict: 'stalled', detail: `open ${age} days, no answer` };
|
|
476
|
-
}
|
|
477
|
-
return { ...base, verdict: 'open', detail: age === null ? 'open' : `open ${age} days` };
|
|
535
|
+
const out = { ...base, verdict: 'unreachable' as const, detail: r.reason };
|
|
536
|
+
// One outage never reds the watcher; a listing nobody has been able to read
|
|
537
|
+
// for longer than the sheet's own threshold does, or it could vanish unseen.
|
|
538
|
+
const age = row.lastRead === '' ? null : _daysBetween(row.lastRead, today);
|
|
539
|
+
const live = row.status === 'accepted' || row.status === 'submitted';
|
|
540
|
+
if (live && (age === null || age > thresholdDays)) {
|
|
541
|
+
return {
|
|
542
|
+
...out,
|
|
543
|
+
action: 'read-overdue',
|
|
544
|
+
next: `read the upstream another way (a headless browser) and set Last read; unread ${age === null ? 'ever' : `for ${age} days`}`,
|
|
545
|
+
};
|
|
478
546
|
}
|
|
547
|
+
return out;
|
|
479
548
|
}
|
|
549
|
+
const out = { ...base, ..._verdictOf(row, r, today) };
|
|
550
|
+
const action = _actionFor(row, r, released);
|
|
551
|
+
// Spread only when present: the JSON writer prints an undefined value as `null`.
|
|
552
|
+
return action === null ? out : { ...out, ...action };
|
|
480
553
|
});
|
|
481
554
|
}
|
|
482
555
|
|
|
556
|
+
function _verdictOf(
|
|
557
|
+
row: LedgerRow,
|
|
558
|
+
r: Extract<UpstreamReading, { ok: true }>,
|
|
559
|
+
today: string,
|
|
560
|
+
): { verdict: UpstreamVerdict; detail: string } {
|
|
561
|
+
switch (r.state) {
|
|
562
|
+
case 'closed':
|
|
563
|
+
return { verdict: 'answered-closed', detail: `closed ${r.at ?? 'date unknown'}` };
|
|
564
|
+
case 'merged':
|
|
565
|
+
return { verdict: 'answered-merged', detail: `merged ${r.at ?? 'date unknown'}` };
|
|
566
|
+
case 'listed':
|
|
567
|
+
case 'not-listed':
|
|
568
|
+
return { verdict: r.state, detail: row.ref };
|
|
569
|
+
case 'open': {
|
|
570
|
+
const age = row.date === '' ? null : _daysBetween(row.date, today);
|
|
571
|
+
if (row.status === 'submitted' && age !== null && age >= STALLED_AFTER_DAYS) {
|
|
572
|
+
return { verdict: 'stalled', detail: `open ${age} days, no answer` };
|
|
573
|
+
}
|
|
574
|
+
return { verdict: 'open', detail: age === null ? 'open' : `open ${age} days` };
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* The cases where the sheet and the live upstream disagree in a way that needs a
|
|
581
|
+
* person. `rejected` and `stalled` rows are left alone: the sheet already says
|
|
582
|
+
* the attempt ended, and an unreadable upstream is never an action, so a network
|
|
583
|
+
* outage cannot red the watcher.
|
|
584
|
+
*/
|
|
585
|
+
function _actionFor(
|
|
586
|
+
row: LedgerRow,
|
|
587
|
+
r: Extract<UpstreamReading, { ok: true }>,
|
|
588
|
+
released: ReleasedServer | undefined,
|
|
589
|
+
): { action: ActionVerdict; next: string } | null {
|
|
590
|
+
const awaiting = row.status === 'submitted' || row.status === 'pending';
|
|
591
|
+
if (awaiting && (r.state === 'closed' || r.state === 'merged')) {
|
|
592
|
+
return {
|
|
593
|
+
action: 'sheet-behind',
|
|
594
|
+
next: `update the ledger row: the upstream ${r.state} ${r.at ?? 'on an unknown date'} while the sheet says ${row.status}`,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
// Row 4's path after its first CI publish: the entry went live, the sheet still waits.
|
|
598
|
+
if (awaiting && r.state === 'listed') {
|
|
599
|
+
return {
|
|
600
|
+
action: 'sheet-behind',
|
|
601
|
+
next: `update the ledger row: the listing reads listed while the sheet says ${row.status}`,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
if (r.state === 'open' && r.reviewDecision === 'CHANGES_REQUESTED') {
|
|
605
|
+
return { action: 'needs-response', next: `answer the review on ${row.ref}` };
|
|
606
|
+
}
|
|
607
|
+
if (row.status === 'accepted' && r.state === 'not-listed') {
|
|
608
|
+
return { action: 'listing-lost', next: `re-check or re-submit the listing at ${row.ref}` };
|
|
609
|
+
}
|
|
610
|
+
if (
|
|
611
|
+
row.status === 'accepted' &&
|
|
612
|
+
row.kind === 'registry-query' &&
|
|
613
|
+
r.state === 'listed' &&
|
|
614
|
+
released !== undefined &&
|
|
615
|
+
r.latestVersions !== undefined
|
|
616
|
+
) {
|
|
617
|
+
const live = r.latestVersions[released.name];
|
|
618
|
+
if (live === undefined) {
|
|
619
|
+
return {
|
|
620
|
+
action: 'listing-lost',
|
|
621
|
+
next: `re-check or re-submit the listing: the registry answer carries no ${released.name}`,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (live !== released.version) {
|
|
625
|
+
return {
|
|
626
|
+
action: 'listing-stale',
|
|
627
|
+
next: `publish ${released.version} to the registry: it carries ${live} for ${released.name}`,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** The rows `--fail-on-action` reds on, one line each: row, action, next act. */
|
|
635
|
+
export function action_lines(verdicts: RowVerdict[]): string[] {
|
|
636
|
+
return verdicts
|
|
637
|
+
.filter((v) => v.action !== undefined)
|
|
638
|
+
.map((v) => `row ${v.id} ${v.action} (${v.registry}) — ${v.next ?? ''}`);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export function upstream_exit_code(verdicts: RowVerdict[], failOnAction: boolean): number {
|
|
642
|
+
return failOnAction && verdicts.some((v) => v.action !== undefined) ? 2 : 0;
|
|
643
|
+
}
|
|
644
|
+
|
|
483
645
|
export function upstream_mode_note(checked: boolean): string {
|
|
484
646
|
return checked
|
|
485
647
|
? 'upstream: read live (--check-upstream)'
|
|
@@ -508,6 +670,14 @@ export function render_upstream_text(verdicts: RowVerdict[], unread: UnreadFindi
|
|
|
508
670
|
for (const u of unread) {
|
|
509
671
|
lines.push(` row ${u.id.padEnd(3)} ${u.label} ${u.registry}`);
|
|
510
672
|
}
|
|
673
|
+
const actions = action_lines(verdicts);
|
|
674
|
+
if (actions.length > 0) {
|
|
675
|
+
lines.push('');
|
|
676
|
+
lines.push('Actions needed (a submission is done only once it is live):');
|
|
677
|
+
for (const a of actions) {
|
|
678
|
+
lines.push(` ${a}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
511
681
|
return lines.join('\n');
|
|
512
682
|
}
|
|
513
683
|
|
|
@@ -521,14 +691,14 @@ function _readPullRequest(ref: string): UpstreamReading {
|
|
|
521
691
|
}
|
|
522
692
|
const proc = spawnSync(
|
|
523
693
|
'gh',
|
|
524
|
-
['pr', 'view', m[2] as string, '--repo', m[1] as string, '--json', 'state,closedAt,mergedAt'],
|
|
694
|
+
['pr', 'view', m[2] as string, '--repo', m[1] as string, '--json', 'state,closedAt,mergedAt,reviewDecision'],
|
|
525
695
|
{ encoding: 'utf8', timeout: 20_000 },
|
|
526
696
|
);
|
|
527
697
|
if (proc.status !== 0) {
|
|
528
698
|
const snip = sanitize_text(_pyStrip(proc.stderr ?? '')).slice(0, 80);
|
|
529
699
|
return { ok: false, reason: `gh exit=${proc.status}: ${snip}` };
|
|
530
700
|
}
|
|
531
|
-
let d: { state?: string; closedAt?: string; mergedAt?: string };
|
|
701
|
+
let d: { state?: string; closedAt?: string; mergedAt?: string; reviewDecision?: string };
|
|
532
702
|
try {
|
|
533
703
|
d = JSON.parse(proc.stdout) as typeof d;
|
|
534
704
|
} catch {
|
|
@@ -536,7 +706,10 @@ function _readPullRequest(ref: string): UpstreamReading {
|
|
|
536
706
|
}
|
|
537
707
|
if (d.state === 'MERGED') return { ok: true, state: 'merged', at: sanitize_text(d.mergedAt ?? '').slice(0, 10) };
|
|
538
708
|
if (d.state === 'CLOSED') return { ok: true, state: 'closed', at: sanitize_text(d.closedAt ?? '').slice(0, 10) };
|
|
539
|
-
if (d.state === 'OPEN')
|
|
709
|
+
if (d.state === 'OPEN') {
|
|
710
|
+
const review = sanitize_text(d.reviewDecision ?? '').slice(0, 40);
|
|
711
|
+
return review === '' ? { ok: true, state: 'open' } : { ok: true, state: 'open', reviewDecision: review };
|
|
712
|
+
}
|
|
540
713
|
return { ok: false, reason: `unknown pull-request state ${sanitize_text(String(d.state)).slice(0, 40)}` };
|
|
541
714
|
}
|
|
542
715
|
|
|
@@ -574,9 +747,21 @@ export function read_url(
|
|
|
574
747
|
if (code === '404') return { ok: true, state: 'not-listed' };
|
|
575
748
|
return { ok: false, reason: `HTTP ${code} from ${ref}` };
|
|
576
749
|
}
|
|
577
|
-
|
|
750
|
+
return read_registry_answer(out.slice(0, cut), ref);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const _REGISTRY_META_KEY = 'io.modelcontextprotocol.registry/official';
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Shape read live from `GET /v0/servers?search=` on 2026-10-24:
|
|
757
|
+
* `{servers: [{server: {name, version}, _meta: {"<key>": {isLatest}}}], metadata: {count}}`.
|
|
758
|
+
* A search returns every published version, so only the one flagged `isLatest`
|
|
759
|
+
* names what a client installs.
|
|
760
|
+
*/
|
|
761
|
+
export function read_registry_answer(text: string, ref: string): UpstreamReading {
|
|
762
|
+
let body: { metadata?: { count?: unknown }; servers?: unknown };
|
|
578
763
|
try {
|
|
579
|
-
body = JSON.parse(
|
|
764
|
+
body = JSON.parse(text) as typeof body;
|
|
580
765
|
} catch {
|
|
581
766
|
return { ok: false, reason: `registry answer from ${ref} not parseable as JSON` };
|
|
582
767
|
}
|
|
@@ -584,7 +769,26 @@ export function read_url(
|
|
|
584
769
|
if (typeof n !== 'number') {
|
|
585
770
|
return { ok: false, reason: 'registry answer has no metadata.count' };
|
|
586
771
|
}
|
|
587
|
-
|
|
772
|
+
if (n === 0) {
|
|
773
|
+
return { ok: true, state: 'not-listed' };
|
|
774
|
+
}
|
|
775
|
+
const latestVersions: Record<string, string> = {};
|
|
776
|
+
let flagged = false;
|
|
777
|
+
for (const entry of Array.isArray(body.servers) ? body.servers : []) {
|
|
778
|
+
const e = entry as {
|
|
779
|
+
server?: { name?: unknown; version?: unknown };
|
|
780
|
+
_meta?: Record<string, { isLatest?: unknown } | undefined>;
|
|
781
|
+
};
|
|
782
|
+
const name = e.server?.name;
|
|
783
|
+
const version = e.server?.version;
|
|
784
|
+
if (typeof name !== 'string' || typeof version !== 'string') continue;
|
|
785
|
+
if (e._meta?.[_REGISTRY_META_KEY]?.isLatest !== true) continue;
|
|
786
|
+
flagged = true;
|
|
787
|
+
latestVersions[sanitize_text(name).slice(0, 200)] = sanitize_text(version).slice(0, 40);
|
|
788
|
+
}
|
|
789
|
+
// No `isLatest` flag anywhere means the answer is not the shape read above;
|
|
790
|
+
// comparing against an empty map would report a live listing as lost.
|
|
791
|
+
return flagged ? { ok: true, state: 'listed', latestVersions } : { ok: true, state: 'listed' };
|
|
588
792
|
}
|
|
589
793
|
|
|
590
794
|
/** Reads the real upstream: `gh` for a pull request, `curl` otherwise. */
|
|
@@ -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],
|
|
@@ -75,6 +75,13 @@ export const SECURITY_FLOOR_EXCEPTIONS: Readonly<Record<string, { required: stri
|
|
|
75
75
|
'high-severity route-guard bypass via path traversal (CVSS 7.5) affects <=10.1.1; ' +
|
|
76
76
|
'10.1.2 is the fix. Verified with npm audit on a clean 10.1.0 install, 2026-07-31.',
|
|
77
77
|
},
|
|
78
|
+
zod: {
|
|
79
|
+
required: '^4.6.6',
|
|
80
|
+
reason:
|
|
81
|
+
'high-severity memory exhaustion while validating oversized constrained collections ' +
|
|
82
|
+
'(GHSA-mrhx-c7xc-m3w5) affects <4.6.6; 4.6.6 is the fix. Verified with npm audit on ' +
|
|
83
|
+
'clean 4.6.5 (1 high) and 4.6.6 (0) installs, 2026-10-24.',
|
|
84
|
+
},
|
|
78
85
|
};
|
|
79
86
|
|
|
80
87
|
/** 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',
|