semantic-js-mcp 0.10.2 → 0.10.3

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.
@@ -2,7 +2,8 @@ import {DOCTOR_REASON, SEMANTIC_EVIDENCE_FOLLOW_UP_REASON} from "../protocol.mjs
2
2
 
3
3
  export const DOCUMENTATION_FILE = Object.freeze({
4
4
  README: "README.md",
5
- AGENT_SETUP: "AGENT_SETUP.md",
5
+ SETUP: "SETUP.md",
6
+ DISTRIBUTION: "docs/distribution.md",
6
7
  GETTING_STARTED: "docs/getting-started.md",
7
8
  SEMANTIC_NAVIGATION_SKILL: "skills/semantic-navigation/SKILL.md",
8
9
  CONTRIBUTING: "CONTRIBUTING.md",
@@ -10,26 +11,36 @@ export const DOCUMENTATION_FILE = Object.freeze({
10
11
  });
11
12
 
12
13
  export const PUBLIC_ROOT_DOCUMENT = Object.freeze([
13
- "AGENT_SETUP.md",
14
14
  "CHANGELOG.md",
15
15
  "CONTRIBUTING.md",
16
16
  "README.md",
17
17
  "ROADMAP.md",
18
18
  "SECURITY.md",
19
+ "SETUP.md",
19
20
  ]);
20
21
 
21
22
  export const PUBLIC_DOCUMENT_DIRECTORY = Object.freeze(["docs", "skills"]);
22
23
 
23
24
  export const DOCUMENTATION_REQUIREMENT = Object.freeze({
24
- README_HEADINGS: Object.freeze([
25
- "Installation",
26
- "Updating the Codex plugin",
27
- "Runtime",
28
- "Verification",
29
- "Current Limitations",
30
- "Reporting Problems",
25
+ README_HEADINGS: Object.freeze(["Setup", "Runtime", "Development Setup", "Verification", "Current Limitations", "Reporting Problems"]),
26
+ README_FIRST_SECTION: "Setup",
27
+ README_SETUP_ENTRY_LITERALS: Object.freeze([
28
+ "Before running an installation command, follow [SETUP.md](SETUP.md).",
29
+ "single source of truth",
30
+ "Package installation alone does not configure an MCP host.",
31
+ ]),
32
+ README_FORBIDDEN_SETUP_HEADINGS: Object.freeze(["Agent Installation", "Installation", "Updating the Codex plugin"]),
33
+ README_FORBIDDEN_SETUP_LITERALS: Object.freeze([
34
+ "codex plugin marketplace add elnonathan/semantic-js-mcp",
35
+ "codex plugin add semantic-js-mcp@elnonathan",
36
+ "npm install --global semantic-js-mcp",
37
+ "npm ci",
38
+ ]),
39
+ DISTRIBUTION_FORBIDDEN_SETUP_LITERALS: Object.freeze([
40
+ "codex plugin marketplace add elnonathan/semantic-js-mcp",
41
+ "codex plugin marketplace upgrade elnonathan",
42
+ "codex plugin add semantic-js-mcp@elnonathan",
31
43
  ]),
32
- README_LITERALS: Object.freeze(["codex plugin marketplace upgrade elnonathan", "codex plugin add semantic-js-mcp@elnonathan"]),
33
44
  GETTING_STARTED_HEADINGS: Object.freeze([
34
45
  "Trace A Symbol",
35
46
  "Review A Security-Sensitive Change",
@@ -40,24 +51,48 @@ export const DOCUMENTATION_REQUIREMENT = Object.freeze({
40
51
  "Untrusted Diagnostics",
41
52
  "Startup Failure",
42
53
  ]),
43
- AGENT_SETUP_HEADINGS: Object.freeze([
44
- "Scope",
45
- "Safety Boundaries",
46
- "Installation Procedure",
47
- "Verification",
48
- "Update And Rollback",
54
+ SETUP_HEADINGS: Object.freeze([
55
+ "Guards",
56
+ "Prerequisites",
57
+ "Choose A Version",
58
+ "Install With Codex",
59
+ "Install With Another MCP Host",
60
+ "Verify The Installation",
61
+ "Troubleshooting",
62
+ "Update",
63
+ "Rollback",
49
64
  "Removal",
50
- "Completion Report",
65
+ "Source Checkout",
66
+ "Background",
51
67
  ]),
68
+ SETUP_FIRST_SECTION: "Guards",
69
+ SETUP_LAST_SECTION: "Background",
70
+ DISTRIBUTION_HEADINGS: Object.freeze(["Release Verification", "npm Trusted Publishing"]),
52
71
  SEMANTIC_NAVIGATION_SKILL_HEADINGS: Object.freeze(["Preserve Combination Invariants"]),
53
- README_LINKS: Object.freeze(["AGENT_SETUP.md", "docs/getting-started.md", "CONTRIBUTING.md", "SECURITY.md"]),
72
+ README_LINKS: Object.freeze(["SETUP.md", "docs/getting-started.md", "CONTRIBUTING.md", "SECURITY.md"]),
54
73
  GETTING_STARTED_LITERALS: Object.freeze([SEMANTIC_EVIDENCE_FOLLOW_UP_REASON.COLLECTION_PARTIAL, DOCTOR_REASON.RUNTIME_COMPONENT_MISSING]),
55
- AGENT_SETUP_LITERALS: Object.freeze([
74
+ SETUP_LITERALS: Object.freeze([
56
75
  "semantic-js-mcp doctor",
57
76
  "semantic-js-mcp serve",
58
77
  "does not override system, developer, organization, repository",
59
78
  "npm uninstall --global semantic-js-mcp",
60
79
  "server name: `semanticjsmcp`",
80
+ "codex plugin marketplace upgrade elnonathan",
81
+ "Do not run `npm install semantic-js-mcp` without `--global`.",
82
+ "Treat `EPERM`, `EACCES`, `TAR_ENTRY_ERROR`, or a missing-file error as a partial installation.",
83
+ "Do not start `semantic-js-mcp serve` manually.",
84
+ "Choose exactly one installation route.",
85
+ "pending-restart",
86
+ "No source-code call is required",
87
+ "Do not run the global executable checks",
88
+ ]),
89
+ DISTRIBUTION_LITERALS: Object.freeze([
90
+ "npm run release:verify",
91
+ "npm run verify:published -- <version>",
92
+ "The `publish.yml` workflow publishes tags matching `v*`",
93
+ "protected `npm-publish` environment",
94
+ "No long-lived npm token is used.",
95
+ "Verify the published package before creating the matching GitHub release.",
61
96
  ]),
62
97
  SEMANTIC_NAVIGATION_SKILL_LITERALS: Object.freeze([
63
98
  "Identify every producer of the value",
@@ -71,6 +106,12 @@ export const DOCUMENTATION_REASON = Object.freeze({
71
106
  HEADING_MISSING: "required-heading-missing",
72
107
  LINK_MISSING: "required-link-missing",
73
108
  LITERAL_MISSING: "required-literal-missing",
109
+ SETUP_ENTRY_POINT_NOT_FIRST: "setup-entry-point-not-first",
110
+ DUPLICATE_SETUP_SECTION: "duplicate-setup-section",
111
+ DUPLICATE_SETUP_COMMAND: "duplicate-setup-command",
112
+ SETUP_GUARDS_NOT_FIRST: "setup-guards-not-first",
113
+ SETUP_SECTION_ORDER_INVALID: "setup-section-order-invalid",
114
+ SETUP_BACKGROUND_NOT_LAST: "setup-background-not-last",
74
115
  PRIVATE_COORDINATION: "private-coordination-language-found",
75
116
  LOCAL_PATH: "local-absolute-path-found",
76
117
  });
@@ -6,8 +6,9 @@ import {evaluateDocumentation} from "./documentation-gate.mjs";
6
6
 
7
7
  const headingText = (headings) => headings.map((heading) => `## ${heading}`).join("\n");
8
8
  const valid = {
9
- [DOCUMENTATION_FILE.README]: `${headingText(DOCUMENTATION_REQUIREMENT.README_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.README_LINKS.map((link) => `[doc](${link})`).join("\n")}\n${DOCUMENTATION_REQUIREMENT.README_LITERALS.join("\n")}`,
10
- [DOCUMENTATION_FILE.AGENT_SETUP]: `${headingText(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS.join("\n")}`,
9
+ [DOCUMENTATION_FILE.README]: `${headingText(DOCUMENTATION_REQUIREMENT.README_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.README_LINKS.map((link) => `[doc](${link})`).join("\n")}\n${DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS.join("\n")}`,
10
+ [DOCUMENTATION_FILE.SETUP]: `${headingText(DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.SETUP_LITERALS.join("\n")}`,
11
+ [DOCUMENTATION_FILE.DISTRIBUTION]: `${headingText(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS.join("\n")}`,
11
12
  [DOCUMENTATION_FILE.GETTING_STARTED]: `${headingText(DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS.join("\n")}`,
12
13
  [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: `${headingText(DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS.join("\n")}`,
13
14
  [DOCUMENTATION_FILE.CONTRIBUTING]: "# Contributing",
@@ -16,6 +17,105 @@ const valid = {
16
17
 
17
18
  strictEqual(evaluateDocumentation(valid).length, 0, "Valid public documentation failed the gate");
18
19
 
20
+ const lateSetupGuards = {
21
+ ...valid,
22
+ [DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP].replace("## Guards\n", "").concat("\n## Guards\n"),
23
+ };
24
+ strictEqual(
25
+ evaluateDocumentation(lateSetupGuards).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_GUARDS_NOT_FIRST),
26
+ true,
27
+ "Late setup guards were accepted",
28
+ );
29
+
30
+ const fencedSetupGuards = {
31
+ ...valid,
32
+ [DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP].replace("## Guards\n", "```md\n## Guards\n```\n"),
33
+ };
34
+ strictEqual(
35
+ evaluateDocumentation(fencedSetupGuards).some(
36
+ (finding) =>
37
+ finding.reason === DOCUMENTATION_REASON.HEADING_MISSING &&
38
+ finding.file === DOCUMENTATION_FILE.SETUP &&
39
+ finding.heading === DOCUMENTATION_REQUIREMENT.SETUP_FIRST_SECTION,
40
+ ),
41
+ true,
42
+ "Setup heading inside a fenced block was accepted",
43
+ );
44
+
45
+ const invalidSetupOrder = {
46
+ ...valid,
47
+ [DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP]
48
+ .replace("## Prerequisites\n", "## Setup Order Placeholder\n")
49
+ .replace("## Choose A Version\n", "## Prerequisites\n")
50
+ .replace("## Setup Order Placeholder\n", "## Choose A Version\n"),
51
+ };
52
+ strictEqual(
53
+ evaluateDocumentation(invalidSetupOrder).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_SECTION_ORDER_INVALID),
54
+ true,
55
+ "Invalid setup section order was accepted",
56
+ );
57
+
58
+ const earlySetupBackground = {
59
+ ...valid,
60
+ [DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP]
61
+ .replace("## Background\n", "")
62
+ .replace("## Troubleshooting\n", "## Background\n## Troubleshooting\n"),
63
+ };
64
+ strictEqual(
65
+ evaluateDocumentation(earlySetupBackground).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_BACKGROUND_NOT_LAST),
66
+ true,
67
+ "Setup background outside the final section was accepted",
68
+ );
69
+
70
+ const lateSetupEntryPoint = {
71
+ ...valid,
72
+ [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace("## Setup\n", "").concat("\n## Setup\n"),
73
+ };
74
+ strictEqual(
75
+ evaluateDocumentation(lateSetupEntryPoint).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_ENTRY_POINT_NOT_FIRST),
76
+ true,
77
+ "Late setup entry point was accepted",
78
+ );
79
+
80
+ const duplicateSetupSection = {
81
+ ...valid,
82
+ [DOCUMENTATION_FILE.README]: `${valid[DOCUMENTATION_FILE.README]}\n## ${DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_HEADINGS[1]}`,
83
+ };
84
+ strictEqual(
85
+ evaluateDocumentation(duplicateSetupSection).some((finding) => finding.reason === DOCUMENTATION_REASON.DUPLICATE_SETUP_SECTION),
86
+ true,
87
+ "Duplicate README setup section was accepted",
88
+ );
89
+
90
+ for (const literal of DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_LITERALS) {
91
+ const duplicateSetupCommand = {
92
+ ...valid,
93
+ [DOCUMENTATION_FILE.README]: `${valid[DOCUMENTATION_FILE.README]}\n${literal}`,
94
+ };
95
+ strictEqual(
96
+ evaluateDocumentation(duplicateSetupCommand).some(
97
+ (finding) => finding.reason === DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND && finding.literal === literal,
98
+ ),
99
+ true,
100
+ `Duplicate README setup command was accepted: ${literal}`,
101
+ );
102
+ }
103
+
104
+ const duplicateDistributionSetupCommand = {
105
+ ...valid,
106
+ [DOCUMENTATION_FILE.DISTRIBUTION]: `${valid[DOCUMENTATION_FILE.DISTRIBUTION]}\n${DOCUMENTATION_REQUIREMENT.DISTRIBUTION_FORBIDDEN_SETUP_LITERALS[0]}`,
107
+ };
108
+ strictEqual(
109
+ evaluateDocumentation(duplicateDistributionSetupCommand).some(
110
+ (finding) =>
111
+ finding.file === DOCUMENTATION_FILE.DISTRIBUTION &&
112
+ finding.reason === DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND &&
113
+ finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_FORBIDDEN_SETUP_LITERALS[0],
114
+ ),
115
+ true,
116
+ "Duplicate distribution setup command was accepted",
117
+ );
118
+
19
119
  const missingHeading = {...valid, [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace("## Verification", "")};
20
120
  strictEqual(
21
121
  evaluateDocumentation(missingHeading).some((finding) => finding.reason === DOCUMENTATION_REASON.HEADING_MISSING),
@@ -48,96 +148,137 @@ strictEqual(
48
148
  "Missing canonical example literal was accepted",
49
149
  );
50
150
 
51
- const missingMarketplaceUpgrade = {
151
+ const missingSetupLink = {
52
152
  ...valid,
53
- [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_LITERALS[0], ""),
153
+ [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replaceAll(
154
+ `](${DOCUMENTATION_REQUIREMENT.README_LINKS[0]})`,
155
+ "](missing-setup.md)",
156
+ ),
54
157
  };
55
158
  strictEqual(
56
- evaluateDocumentation(missingMarketplaceUpgrade).some(
57
- (finding) =>
58
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.README_LITERALS[0],
159
+ evaluateDocumentation(missingSetupLink).some(
160
+ (finding) => finding.reason === DOCUMENTATION_REASON.LINK_MISSING && finding.link === DOCUMENTATION_REQUIREMENT.README_LINKS[0],
59
161
  ),
60
162
  true,
61
- "Missing marketplace upgrade guidance was accepted",
163
+ "Missing setup README link was accepted",
62
164
  );
63
165
 
64
- const missingAgentSetupLink = {
166
+ const missingSetupEntryInstruction = {
65
167
  ...valid,
66
- [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(`[doc](${DOCUMENTATION_REQUIREMENT.README_LINKS[0]})`, ""),
168
+ [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0], ""),
67
169
  };
68
170
  strictEqual(
69
- evaluateDocumentation(missingAgentSetupLink).some(
70
- (finding) => finding.reason === DOCUMENTATION_REASON.LINK_MISSING && finding.link === DOCUMENTATION_REQUIREMENT.README_LINKS[0],
171
+ evaluateDocumentation(missingSetupEntryInstruction).some(
172
+ (finding) =>
173
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
174
+ finding.literal === DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0],
71
175
  ),
72
176
  true,
73
- "Missing agent setup README link was accepted",
177
+ "Missing README setup instruction was accepted",
74
178
  );
75
179
 
76
- const missingAgentSetup = {...valid};
77
- delete missingAgentSetup[DOCUMENTATION_FILE.AGENT_SETUP];
180
+ const missingSetup = {...valid};
181
+ delete missingSetup[DOCUMENTATION_FILE.SETUP];
78
182
  strictEqual(
79
- evaluateDocumentation(missingAgentSetup).some(
80
- (finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.AGENT_SETUP,
183
+ evaluateDocumentation(missingSetup).some(
184
+ (finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.SETUP,
81
185
  ),
82
186
  true,
83
- "Missing agent setup guide was accepted",
187
+ "Missing setup guide was accepted",
84
188
  );
85
189
 
86
- const missingAgentSafetyBoundary = {
190
+ for (const [literal, message] of [
191
+ ["does not override system, developer, organization, repository", "Missing setup safety boundary was accepted"],
192
+ ["server name: `semanticjsmcp`", "Missing generic MCP server name was accepted"],
193
+ ["codex plugin marketplace upgrade elnonathan", "Missing Codex marketplace refresh was accepted"],
194
+ ["Do not run `npm install semantic-js-mcp` without `--global`.", "Missing local npm installation prohibition was accepted"],
195
+ [
196
+ "Treat `EPERM`, `EACCES`, `TAR_ENTRY_ERROR`, or a missing-file error as a partial installation.",
197
+ "Missing partial-installation warning was accepted",
198
+ ],
199
+ ["Do not start `semantic-js-mcp serve` manually.", "Missing manual stdio startup prohibition was accepted"],
200
+ ["Choose exactly one installation route.", "Missing single-route boundary was accepted"],
201
+ ["No source-code call is required", "Missing installation authority boundary was accepted"],
202
+ ]) {
203
+ const missingInstruction = {
204
+ ...valid,
205
+ [DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP].replace(literal, ""),
206
+ };
207
+ strictEqual(
208
+ evaluateDocumentation(missingInstruction).some(
209
+ (finding) => finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === literal,
210
+ ),
211
+ true,
212
+ message,
213
+ );
214
+ }
215
+
216
+ const missingCombinationInvariant = {
87
217
  ...valid,
88
- [DOCUMENTATION_FILE.AGENT_SETUP]: valid[DOCUMENTATION_FILE.AGENT_SETUP].replace(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[2], ""),
218
+ [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: valid[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL].replace(
219
+ DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
220
+ "",
221
+ ),
89
222
  };
90
223
  strictEqual(
91
- evaluateDocumentation(missingAgentSafetyBoundary).some(
224
+ evaluateDocumentation(missingCombinationInvariant).some(
92
225
  (finding) =>
93
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[2],
226
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
227
+ finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
94
228
  ),
95
229
  true,
96
- "Missing agent setup safety boundary was accepted",
230
+ "Missing combination-invariant guidance was accepted",
97
231
  );
98
232
 
99
- const missingGenericServerName = {
233
+ const missingTrustedPublishingPolicy = {
100
234
  ...valid,
101
- [DOCUMENTATION_FILE.AGENT_SETUP]: valid[DOCUMENTATION_FILE.AGENT_SETUP].replace(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[4], ""),
235
+ [DOCUMENTATION_FILE.DISTRIBUTION]: valid[DOCUMENTATION_FILE.DISTRIBUTION].replace(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[2], ""),
102
236
  };
103
237
  strictEqual(
104
- evaluateDocumentation(missingGenericServerName).some(
238
+ evaluateDocumentation(missingTrustedPublishingPolicy).some(
105
239
  (finding) =>
106
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[4],
240
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[2],
107
241
  ),
108
242
  true,
109
- "Missing generic MCP server name was accepted",
243
+ "Missing trusted-publishing policy was accepted",
110
244
  );
111
245
 
112
- const missingCombinationInvariant = {
246
+ const missingPostpublicationOrder = {
113
247
  ...valid,
114
- [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: valid[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL].replace(
115
- DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
116
- "",
117
- ),
248
+ [DOCUMENTATION_FILE.DISTRIBUTION]: valid[DOCUMENTATION_FILE.DISTRIBUTION].replace(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[5], ""),
118
249
  };
119
250
  strictEqual(
120
- evaluateDocumentation(missingCombinationInvariant).some(
251
+ evaluateDocumentation(missingPostpublicationOrder).some(
121
252
  (finding) =>
122
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
123
- finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
253
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[5],
124
254
  ),
125
255
  true,
126
- "Missing combination-invariant guidance was accepted",
256
+ "Missing postpublication order was accepted",
127
257
  );
128
258
 
129
259
  process.stdout.write(
130
260
  `${JSON.stringify(
131
261
  {
132
262
  validDocumentation: "pass",
263
+ lateSetupGuards: "rejected",
264
+ fencedSetupGuards: "rejected",
265
+ invalidSetupOrder: "rejected",
266
+ earlySetupBackground: "rejected",
267
+ lateSetupEntryPoint: "rejected",
268
+ duplicateSetupSection: "rejected",
269
+ duplicateSetupCommand: "rejected",
270
+ duplicateDistributionSetupCommand: "rejected",
133
271
  missingHeading: "rejected",
134
272
  missingLiteral: "rejected",
135
- missingMarketplaceUpgrade: "rejected",
136
- missingAgentSetupLink: "rejected",
137
- missingAgentSetup: "rejected",
138
- missingAgentSafetyBoundary: "rejected",
273
+ missingSetupLink: "rejected",
274
+ missingSetupEntryInstruction: "rejected",
275
+ missingSetup: "rejected",
276
+ missingSetupSafetyBoundary: "rejected",
139
277
  missingGenericServerName: "rejected",
278
+ missingCodexMarketplaceRefresh: "rejected",
140
279
  missingCombinationInvariant: "rejected",
280
+ missingTrustedPublishingPolicy: "rejected",
281
+ missingPostpublicationOrder: "rejected",
141
282
  privateCoordination: "rejected",
142
283
  },
143
284
  null,
@@ -13,15 +13,49 @@ import {
13
13
  PUBLIC_ROOT_DOCUMENT,
14
14
  } from "./documentation-contract.mjs";
15
15
 
16
+ function markdownStructuralLines(source) {
17
+ const lines = [];
18
+ let fence;
19
+ for (const line of source.split("\n")) {
20
+ const match = /^\s{0,3}(`{3,}|~{3,})(.*)$/.exec(line);
21
+ if (match && fence === undefined) {
22
+ fence = {marker: match[1][0], length: match[1].length};
23
+ continue;
24
+ }
25
+ if (match && match[1][0] === fence?.marker && match[1].length >= fence.length && match[2].trim() === "") {
26
+ fence = undefined;
27
+ continue;
28
+ }
29
+ if (fence === undefined) lines.push(line);
30
+ }
31
+ return lines;
32
+ }
33
+
16
34
  function markdownHeadings(source) {
17
35
  return new Set(
18
- source
19
- .split("\n")
36
+ markdownStructuralLines(source)
20
37
  .map((line) => /^#{1,6}\s+(.+?)\s*$/.exec(line)?.[1])
21
38
  .filter(Boolean),
22
39
  );
23
40
  }
24
41
 
42
+ function levelTwoHeadings(source) {
43
+ return markdownStructuralLines(source)
44
+ .map((line) => /^##\s+(.+?)\s*$/.exec(line)?.[1])
45
+ .filter(Boolean);
46
+ }
47
+
48
+ function requiredHeadingsAreOrdered(source, requiredHeadings) {
49
+ const headings = levelTwoHeadings(source);
50
+ let nextIndex = 0;
51
+ for (const requiredHeading of requiredHeadings) {
52
+ const index = headings.indexOf(requiredHeading, nextIndex);
53
+ if (index === -1) return false;
54
+ nextIndex = index + 1;
55
+ }
56
+ return true;
57
+ }
58
+
25
59
  function repositoryPath(root, absolute) {
26
60
  return path.relative(root, absolute).split(path.sep).join("/");
27
61
  }
@@ -35,7 +69,8 @@ export function evaluateDocumentation(documents) {
35
69
 
36
70
  const headingRequirements = [
37
71
  [DOCUMENTATION_FILE.README, DOCUMENTATION_REQUIREMENT.README_HEADINGS],
38
- [DOCUMENTATION_FILE.AGENT_SETUP, DOCUMENTATION_REQUIREMENT.AGENT_SETUP_HEADINGS],
72
+ [DOCUMENTATION_FILE.SETUP, DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS],
73
+ [DOCUMENTATION_FILE.DISTRIBUTION, DOCUMENTATION_REQUIREMENT.DISTRIBUTION_HEADINGS],
39
74
  [DOCUMENTATION_FILE.GETTING_STARTED, DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS],
40
75
  [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL, DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS],
41
76
  ];
@@ -50,14 +85,30 @@ export function evaluateDocumentation(documents) {
50
85
 
51
86
  const readme = documents[DOCUMENTATION_FILE.README];
52
87
  if (readme !== undefined) {
88
+ if (levelTwoHeadings(readme)[0] !== DOCUMENTATION_REQUIREMENT.README_FIRST_SECTION) {
89
+ findings.push({
90
+ file: DOCUMENTATION_FILE.README,
91
+ reason: DOCUMENTATION_REASON.SETUP_ENTRY_POINT_NOT_FIRST,
92
+ expectedHeading: DOCUMENTATION_REQUIREMENT.README_FIRST_SECTION,
93
+ });
94
+ }
53
95
  for (const link of DOCUMENTATION_REQUIREMENT.README_LINKS) {
54
96
  if (readme.includes(`](${link})`)) continue;
55
97
  findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LINK_MISSING, link});
56
98
  }
57
- for (const literal of DOCUMENTATION_REQUIREMENT.README_LITERALS) {
99
+ for (const literal of DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS) {
58
100
  if (readme.includes(literal)) continue;
59
101
  findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
60
102
  }
103
+ const readmeHeadings = markdownHeadings(readme);
104
+ for (const heading of DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_HEADINGS) {
105
+ if (!readmeHeadings.has(heading)) continue;
106
+ findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.DUPLICATE_SETUP_SECTION, heading});
107
+ }
108
+ for (const literal of DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_LITERALS) {
109
+ if (!readme.includes(literal)) continue;
110
+ findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND, literal});
111
+ }
61
112
  }
62
113
 
63
114
  const gettingStarted = documents[DOCUMENTATION_FILE.GETTING_STARTED];
@@ -68,11 +119,32 @@ export function evaluateDocumentation(documents) {
68
119
  }
69
120
  }
70
121
 
71
- const agentSetup = documents[DOCUMENTATION_FILE.AGENT_SETUP];
72
- if (agentSetup !== undefined) {
73
- for (const literal of DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS) {
74
- if (agentSetup.includes(literal)) continue;
75
- findings.push({file: DOCUMENTATION_FILE.AGENT_SETUP, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
122
+ const setup = documents[DOCUMENTATION_FILE.SETUP];
123
+ if (setup !== undefined) {
124
+ const setupHeadings = levelTwoHeadings(setup);
125
+ if (setupHeadings[0] !== DOCUMENTATION_REQUIREMENT.SETUP_FIRST_SECTION) {
126
+ findings.push({
127
+ file: DOCUMENTATION_FILE.SETUP,
128
+ reason: DOCUMENTATION_REASON.SETUP_GUARDS_NOT_FIRST,
129
+ expectedHeading: DOCUMENTATION_REQUIREMENT.SETUP_FIRST_SECTION,
130
+ });
131
+ }
132
+ if (!requiredHeadingsAreOrdered(setup, DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS)) {
133
+ findings.push({
134
+ file: DOCUMENTATION_FILE.SETUP,
135
+ reason: DOCUMENTATION_REASON.SETUP_SECTION_ORDER_INVALID,
136
+ });
137
+ }
138
+ if (setupHeadings.at(-1) !== DOCUMENTATION_REQUIREMENT.SETUP_LAST_SECTION) {
139
+ findings.push({
140
+ file: DOCUMENTATION_FILE.SETUP,
141
+ reason: DOCUMENTATION_REASON.SETUP_BACKGROUND_NOT_LAST,
142
+ expectedHeading: DOCUMENTATION_REQUIREMENT.SETUP_LAST_SECTION,
143
+ });
144
+ }
145
+ for (const literal of DOCUMENTATION_REQUIREMENT.SETUP_LITERALS) {
146
+ if (setup.includes(literal)) continue;
147
+ findings.push({file: DOCUMENTATION_FILE.SETUP, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
76
148
  }
77
149
  }
78
150
 
@@ -88,6 +160,18 @@ export function evaluateDocumentation(documents) {
88
160
  }
89
161
  }
90
162
 
163
+ const distribution = documents[DOCUMENTATION_FILE.DISTRIBUTION];
164
+ if (distribution !== undefined) {
165
+ for (const literal of DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS) {
166
+ if (distribution.includes(literal)) continue;
167
+ findings.push({file: DOCUMENTATION_FILE.DISTRIBUTION, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
168
+ }
169
+ for (const literal of DOCUMENTATION_REQUIREMENT.DISTRIBUTION_FORBIDDEN_SETUP_LITERALS) {
170
+ if (!distribution.includes(literal)) continue;
171
+ findings.push({file: DOCUMENTATION_FILE.DISTRIBUTION, reason: DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND, literal});
172
+ }
173
+ }
174
+
91
175
  for (const [file, source] of Object.entries(documents)) {
92
176
  if (PRIVATE_COORDINATION_PATTERN.test(source)) {
93
177
  findings.push({file, reason: DOCUMENTATION_REASON.PRIVATE_COORDINATION});
@@ -2,7 +2,7 @@
2
2
 
3
3
  Generated from `protocol.mjs`. Do not edit this file directly.
4
4
 
5
- Producer: `semantic-js-mcp` version `0.10.2`
5
+ Producer: `semantic-js-mcp` version `0.10.3`
6
6
  Display name: `Semantic JS MCP`
7
7
  Result schema version: `7`
8
8