semantic-js-mcp 0.10.1 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semantic-js-mcp",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Semantic understanding of JavaScript codebases for AI agents",
5
5
  "author": {
6
6
  "name": "Jonathan Muñoz Lucas"
package/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.10.3] - 2026-07-18
8
+
9
+ ### Changed
10
+
11
+ - Consolidate installation into `SETUP.md`, the shared setup procedure for people and coding agents. README now provides only a brief entry point. The procedure places guards, prerequisites, installation, and verification before troubleshooting and background; uses direct explanatory language; requires exact host and version checks; rejects partial npm extraction; and does not treat installation as permission to inspect source code.
12
+
13
+ ## [0.10.2] - 2026-07-17
14
+
15
+ ### Added
16
+
17
+ - Add `AGENT_SETUP.md` with safe installation, verification, update, rollback, and removal steps for Codex and standard stdio MCP hosts.
18
+ - Expand semantic-navigation guidance for values assembled from multiple sources so agents inspect existing merge rules, related helpers, consumers, and boundary cases before changing precedence or fallback behavior.
19
+
7
20
  ## [0.10.1] - 2026-07-17
8
21
 
9
22
  ### Added
package/README.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Semantic JS MCP
2
2
 
3
+ ## Setup
4
+
5
+ Before running an installation command, follow [SETUP.md](SETUP.md). It is the
6
+ single source of truth for setup by either a person or a coding agent, including
7
+ prerequisites, host selection, installation, verification, updates, rollback,
8
+ and removal.
9
+
10
+ Semantic JS MCP requires Node.js 22 or newer and `rg` (ripgrep). It can be
11
+ installed through the verified Codex plugin route or registered as a local
12
+ stdio server in another compatible MCP host. Package installation alone does not configure an MCP host.
13
+
14
+ After setup, see [Getting started](docs/getting-started.md) for practical
15
+ investigation prompts and compact evidence examples.
16
+
3
17
  **Your coding agent is only as good as its understanding of your codebase.**
4
18
 
5
19
  Coding agents can generate code quickly, but reliable engineering requires more than reading files and matching text. They need to understand which declaration a reference belongs to, how symbols cross workspace boundaries, whether the collected evidence is complete, and whether that evidence is still accurate.
@@ -16,45 +30,7 @@ For each file, it identifies the owning workspace and uses the corresponding Typ
16
30
 
17
31
  ## Project Status
18
32
 
19
- Version `0.10.1` is the current release. APIs and result contracts may evolve while the project remains on the `0.x` release line. See the [roadmap](ROADMAP.md) for areas under consideration.
20
-
21
- ## Installation
22
-
23
- ### Codex plugin
24
-
25
- Add the public marketplace, then install the plugin:
26
-
27
- ```bash
28
- codex plugin marketplace add elnonathan/semantic-js-mcp
29
- codex plugin add semantic-js-mcp@elnonathan
30
- ```
31
-
32
- Codex CLI 0.144.4 or newer is required for npm-backed marketplace installation. Start a new Codex session after installation. The plugin bundles both the MCP server configuration and the semantic-navigation skill.
33
-
34
- ### Updating the Codex plugin
35
-
36
- Codex installs from a local snapshot of each configured marketplace. Refresh that snapshot before reinstalling so `plugin add` resolves the version currently declared by the marketplace:
37
-
38
- ```bash
39
- codex plugin marketplace upgrade elnonathan
40
- codex plugin add semantic-js-mcp@elnonathan
41
- codex plugin list
42
- ```
43
-
44
- `plugin add` reinstalls the plugin; a separate removal is not required. Start a new Codex session after the command reports the expected version.
45
-
46
- ### MCP executable
47
-
48
- Install the package globally when using an MCP host that accepts a command-based stdio configuration:
49
-
50
- ```bash
51
- npm install --global semantic-js-mcp
52
- semantic-js-mcp doctor
53
- ```
54
-
55
- Configure the host to run `semantic-js-mcp serve`.
56
-
57
- For practical investigation prompts and compact evidence examples, see [Getting started](docs/getting-started.md).
33
+ Version `0.10.3` is the current release. APIs and result contracts may evolve while the project remains on the `0.x` release line. See the [roadmap](ROADMAP.md) for areas under consideration.
58
34
 
59
35
  ## Runtime
60
36
 
@@ -62,7 +38,7 @@ For practical investigation prompts and compact evidence examples, see [Getting
62
38
  - Node.js 24 LTS is recommended for new installations. See the [Node.js release schedule](https://nodejs.org/en/about/previous-releases).
63
39
  - `rg` (ripgrep) must be available on `PATH` for repository-wide discovery.
64
40
  - The nearest workspace TypeScript SDK is used when available; the bundled TypeScript SDK is the fallback.
65
- - TypeScript, `typescript-language-server`, and the Vue language server are pinned dependencies. Published packages bundle every production dependency so Codex installations do not require a separate dependency installation step. Source checkouts use `npm ci`; startup verifies provider entry points before accepting MCP requests.
41
+ - TypeScript, `typescript-language-server`, and the Vue language server are pinned dependencies. Published packages bundle every production dependency so Codex installations do not require a separate dependency installation step. Startup verifies provider entry points before accepting MCP requests.
66
42
  - Source discovery and language selection include `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, `.cjs`, and `.vue` files.
67
43
  - Run `npm run check:runtime` to report every required component and its resolved file path.
68
44
 
@@ -70,10 +46,10 @@ The package exposes a `semantic-js-mcp` executable. `semantic-js-mcp serve` star
70
46
 
71
47
  ## Development Setup
72
48
 
73
- From a source checkout:
49
+ Complete the [source-checkout setup](SETUP.md#source-checkout), then run the
50
+ checks applicable to the change:
74
51
 
75
52
  ```bash
76
- npm ci
77
53
  npm run check
78
54
  npm run check:runtime
79
55
  npm run doctor
@@ -85,34 +61,6 @@ npm run smoke:lifecycle
85
61
 
86
62
  Run `npm run benchmark` after changes to scanning, references, caching, lifecycle, or memory. Provider disposal changes can also be characterized with `npm run benchmark:lifecycle-memory`; its output declares the garbage-collection and platform measurement method. Normal analysis is local and read-only; it does not require network access after dependencies are installed.
87
63
 
88
- For an MCP host that accepts a direct stdio configuration, point it at the checked-out server using an absolute path:
89
-
90
- ```json
91
- {
92
- "mcpServers": {
93
- "semantic-js-mcp": {
94
- "command": "node",
95
- "args": ["/absolute/path/to/semantic-js-mcp/server.mjs"]
96
- }
97
- }
98
- }
99
- ```
100
-
101
- The bundled [`.mcp.json`](.mcp.json) provides the relative-path configuration used when the repository is installed as a Codex plugin.
102
-
103
- When the package executable is available on the host's `PATH`, the equivalent stdio configuration is:
104
-
105
- ```json
106
- {
107
- "mcpServers": {
108
- "semantic-js-mcp": {
109
- "command": "semantic-js-mcp",
110
- "args": ["serve"]
111
- }
112
- }
113
- }
114
- ```
115
-
116
64
  ## Tool Graph
117
65
 
118
66
  Tools are ordered from smaller responses to deeper evidence:
package/SETUP.md ADDED
@@ -0,0 +1,413 @@
1
+ # Setup
2
+
3
+ This guide provides one setup procedure for both people and coding agents. For
4
+ a new installation, complete Guards, Prerequisites, Choose A Version, one
5
+ installation route, and Verify The Installation in that order.
6
+
7
+ For an update, rollback, removal, or source checkout, go directly to the named
8
+ section and follow its references to the shared verification steps.
9
+
10
+ ## Guards
11
+
12
+ Setup changes software or MCP configuration on the user's machine. Before
13
+ making a change, confirm all of the following:
14
+
15
+ 1. The user explicitly requested the setup operation.
16
+ 2. Choose exactly one installation route. Use the Codex route for Codex and the
17
+ generic stdio route for another compatible MCP host.
18
+ 3. Keep the package out of the current project's dependencies.
19
+ Do not run `npm install semantic-js-mcp` without `--global`.
20
+ 4. Inspect existing MCP configuration before editing it, and preserve every
21
+ unrelated server and setting.
22
+ 5. Obtain any approval required for network access, global installation,
23
+ configuration changes, cleanup, or restart.
24
+ 6. Respect the security policy and configuration schema of the operating
25
+ system, organization, repository, and MCP host.
26
+
27
+ If a guard cannot be satisfied, stop and report `blocked` with the specific
28
+ reason. Do not continue from a failed command, a partial installation, or an
29
+ unknown configuration format.
30
+
31
+ Setup does not authorize source-code analysis. No source-code call is required
32
+ to verify installation unless the user separately requests a functional test.
33
+
34
+ ## Prerequisites
35
+
36
+ Run:
37
+
38
+ ```bash
39
+ node --version
40
+ rg --version
41
+ ```
42
+
43
+ Both commands must succeed. Semantic JS MCP requires Node.js 22 or newer and
44
+ `rg` (ripgrep) on `PATH`.
45
+
46
+ If either prerequisite is missing, report `blocked`. Installing, replacing, or
47
+ downgrading Node.js or ripgrep is a separate system change and requires the
48
+ user's approval.
49
+
50
+ ## Choose A Version
51
+
52
+ When the supplied npm URL ends in `/v/X.Y.Z`, use that exact numeric version.
53
+ For example, `/v/0.10.2` requests version `0.10.2`.
54
+
55
+ When no version is supplied for an installation or update, run:
56
+
57
+ ```bash
58
+ npm view semantic-js-mcp version
59
+ ```
60
+
61
+ Record the returned numeric version. Commands in this guide use `X.Y.Z` as a
62
+ placeholder; replace it before execution. A command containing the literal
63
+ `X.Y.Z` is not ready to run.
64
+
65
+ Do not silently substitute `latest`, an installed version, or a newer version
66
+ for the version the user requested.
67
+
68
+ ## Install With Codex
69
+
70
+ Use this route when Codex is the MCP host. The plugin supplies both the MCP
71
+ server configuration and the semantic-navigation skill, so a separate global
72
+ npm installation is unnecessary.
73
+
74
+ ### 1. Inspect Codex State
75
+
76
+ Run:
77
+
78
+ ```bash
79
+ codex --version
80
+ codex plugin marketplace list
81
+ codex plugin list
82
+ ```
83
+
84
+ Codex CLI must be version 0.144.4 or newer. The two list commands establish
85
+ whether the marketplace or plugin already exists. There is no need to locate
86
+ or edit a Codex configuration file.
87
+
88
+ If the Codex version is too old, report `blocked`.
89
+
90
+ ### 2. Add Or Refresh The Marketplace
91
+
92
+ If marketplace `elnonathan` is absent, run:
93
+
94
+ ```bash
95
+ codex plugin marketplace add elnonathan/semantic-js-mcp
96
+ ```
97
+
98
+ If marketplace `elnonathan` is already present, refresh it instead:
99
+
100
+ ```bash
101
+ codex plugin marketplace upgrade elnonathan
102
+ ```
103
+
104
+ ### 3. Confirm The Offered Version
105
+
106
+ Run:
107
+
108
+ ```bash
109
+ codex plugin list
110
+ ```
111
+
112
+ Find `semantic-js-mcp@elnonathan` and confirm that its offered version matches
113
+ the version recorded in Choose A Version. If it differs, report `blocked`
114
+ instead of installing a different release.
115
+
116
+ ### 4. Install The Plugin
117
+
118
+ Run:
119
+
120
+ ```bash
121
+ codex plugin add semantic-js-mcp@elnonathan
122
+ codex plugin list
123
+ ```
124
+
125
+ The final list must show `semantic-js-mcp@elnonathan` as `installed, enabled`
126
+ at the requested version.
127
+
128
+ Do not also run `npm install` or `semantic-js-mcp doctor` for this route. The
129
+ plugin owns the package and server configuration.
130
+
131
+ ### 5. Restart Codex
132
+
133
+ Start a new Codex session so it can load the plugin. A coding agent cannot
134
+ restart its own active session; in that case, report `pending-restart` and ask
135
+ the user to start the new session.
136
+
137
+ After restart, continue with Verify The Installation.
138
+
139
+ ## Install With Another MCP Host
140
+
141
+ Use this route only when the application is not Codex and its official
142
+ documentation confirms support for local MCP servers over `stdio`.
143
+
144
+ ### 1. Confirm The Host Configuration
145
+
146
+ Before installing the package:
147
+
148
+ 1. identify the MCP host by name;
149
+ 2. find its documented MCP configuration location and schema;
150
+ 3. inspect the existing configuration;
151
+ 4. record any existing Semantic JS MCP entry; and
152
+ 5. preserve every unrelated entry.
153
+
154
+ If the host, stdio support, configuration location, or schema cannot be
155
+ confirmed, report `blocked`. Configuration formats are host-specific, so do
156
+ not copy a filename, JSON shape, URL, or port from another application.
157
+
158
+ ### 2. Install The Exact Global Version
159
+
160
+ Replace `X.Y.Z` with the recorded version, then run:
161
+
162
+ ```bash
163
+ npm install --global semantic-js-mcp@X.Y.Z
164
+ ```
165
+
166
+ Read the complete npm output. A zero exit code is not sufficient when npm also
167
+ reports a permission or extraction problem.
168
+ Treat `EPERM`, `EACCES`, `TAR_ENTRY_ERROR`, or a missing-file error as a partial installation.
169
+
170
+ Do not edit the host configuration after a partial installation. Remove the
171
+ partial global package only with the user's approval, then report `blocked`
172
+ with the npm error.
173
+
174
+ ### 3. Check The Executable
175
+
176
+ Run:
177
+
178
+ ```bash
179
+ semantic-js-mcp --version
180
+ semantic-js-mcp doctor
181
+ ```
182
+
183
+ The version must exactly match the requested version. Interpret the structured
184
+ doctor status as follows:
185
+
186
+ - `pass`: continue;
187
+ - `untrusted`: continue, but preserve the reported uncertainty;
188
+ - `blocked` or `fail`: do not configure the host until the reported problem is
189
+ corrected with the user's approval.
190
+
191
+ ### 4. Register The Server
192
+
193
+ Using the host's documented schema, add one server with these values:
194
+
195
+ - server name: `semanticjsmcp`
196
+ - transport: `stdio`
197
+ - executable or command: `semantic-js-mcp`
198
+ - arguments: one argument, `serve`
199
+ - enabled: `true`, only when the host schema defines an enabled field
200
+
201
+ Save the smallest possible configuration change and preserve every unrelated
202
+ entry. Do not add a URL, port, shell command, credentials, or background
203
+ service.
204
+
205
+ Do not start `semantic-js-mcp serve` manually. The MCP host must start the
206
+ process and communicate with it through standard input and standard output.
207
+
208
+ ### 5. Restart The Host
209
+
210
+ Restart or reload the host exactly as its documentation requires, then continue
211
+ with Verify The Installation.
212
+
213
+ ## Verify The Installation
214
+
215
+ Verification occurs after the required restart or reload.
216
+
217
+ ### Codex
218
+
219
+ 1. Run `codex plugin list`.
220
+ 2. Confirm that `semantic-js-mcp@elnonathan` is `installed, enabled` at the
221
+ requested version.
222
+ 3. Open the MCP tool list in the new Codex session.
223
+
224
+ ### Another MCP Host
225
+
226
+ Run:
227
+
228
+ ```bash
229
+ semantic-js-mcp --version
230
+ semantic-js-mcp doctor
231
+ ```
232
+
233
+ The version must match. Doctor must return `pass` or `untrusted`; preserve any
234
+ uncertainty reported by `untrusted`.
235
+
236
+ Confirm that the host still contains the `semanticjsmcp` entry, then open the
237
+ host's MCP tool list.
238
+
239
+ ### Expected Result
240
+
241
+ The host must expose tools whose base names begin with `lsp_`. Some hosts add
242
+ the server name as a prefix.
243
+
244
+ Use one of these outcomes:
245
+
246
+ - `success`: the requested version is registered and `lsp_*` tools are
247
+ available;
248
+ - `pending-restart`: installation and configuration succeeded, but a new host
249
+ session is still required to check tool discovery;
250
+ - `blocked`: a prerequisite, installation, configuration, doctor, version, or
251
+ tool-discovery check failed.
252
+
253
+ Package installation or configuration text by itself is not `success`. Missing
254
+ tools are a reason to inspect registration and restart state, not to start the
255
+ server manually.
256
+
257
+ An installation-only request ends after tool discovery. Do not call an `lsp_*`
258
+ tool against source code unless the user separately requests a functional test
259
+ or explicitly approves the target file.
260
+
261
+ Report the outcome, detected host, requested and installed versions, changed
262
+ Semantic JS MCP entry, doctor status for a generic installation, tool-discovery
263
+ result, and any remaining blocker or uncertainty. Do not expose credentials or
264
+ unrelated configuration values.
265
+
266
+ ## Troubleshooting
267
+
268
+ | Symptom | Action |
269
+ | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
270
+ | Node.js is older than 22 or `node` is missing | Report `blocked`. Change Node.js only with separate user approval. |
271
+ | `rg` is missing | Report `blocked`. Install ripgrep only with separate user approval. |
272
+ | npm reports `EPERM`, `EACCES`, `TAR_ENTRY_ERROR`, or a missing file | Treat the installation as partial. Do not configure the host. Remove only the partial global package when authorized. |
273
+ | `semantic-js-mcp --version` differs from the requested version | Report `blocked`; do not configure the different version. |
274
+ | Doctor returns `blocked` or `fail` | Correct only the reported prerequisite and only with user approval. |
275
+ | Codex offers the wrong version | Run `codex plugin marketplace upgrade elnonathan`, check again, and report `blocked` if it still differs. |
276
+ | The host does not show `lsp_*` tools | Confirm the saved entry, command, arguments, enabled state, and required restart. Do not invent a URL or port. |
277
+ | The host configuration location or schema is unknown | Use installed help or official host documentation. Report `blocked` rather than guessing. |
278
+
279
+ Troubleshooting never requires disabling a security control, bypassing a
280
+ sandbox, executing a remote shell pipeline, or replacing an entire
281
+ configuration file.
282
+
283
+ ## Update
284
+
285
+ Record the installed version and existing Semantic JS MCP entry first. Resolve
286
+ the exact target version with Choose A Version.
287
+
288
+ For Codex, run:
289
+
290
+ ```bash
291
+ codex plugin marketplace upgrade elnonathan
292
+ codex plugin list
293
+ codex plugin add semantic-js-mcp@elnonathan
294
+ codex plugin list
295
+ ```
296
+
297
+ Confirm the offered version before `plugin add`. Start a new Codex session,
298
+ then complete Verify The Installation.
299
+
300
+ For another host, replace `X.Y.Z` and run:
301
+
302
+ ```bash
303
+ npm install --global semantic-js-mcp@X.Y.Z
304
+ ```
305
+
306
+ Repeat the executable check, doctor, required restart, and Verify The
307
+ Installation. Keep an existing host entry when it is already correct.
308
+
309
+ ## Rollback
310
+
311
+ Record the previously known working version and Semantic JS MCP entry. Do not
312
+ replace that version with the current registry version.
313
+
314
+ For another host, replace `X.Y.Z` with the previous numeric version:
315
+
316
+ ```bash
317
+ npm install --global semantic-js-mcp@X.Y.Z
318
+ ```
319
+
320
+ Restore only the recorded Semantic JS MCP entry. Restart the host and complete
321
+ Verify The Installation.
322
+
323
+ A Codex marketplace can install only the version it currently offers. If it
324
+ does not offer the requested rollback version, report `blocked`; do not
325
+ substitute another version or edit the marketplace cache.
326
+
327
+ ## Removal
328
+
329
+ Record the installed version and current Semantic JS MCP entry before making a
330
+ change.
331
+
332
+ For Codex, run:
333
+
334
+ ```bash
335
+ codex plugin remove semantic-js-mcp@elnonathan
336
+ ```
337
+
338
+ Remove marketplace `elnonathan` only when no other installed plugin uses it
339
+ and the user approves:
340
+
341
+ ```bash
342
+ codex plugin marketplace remove elnonathan
343
+ ```
344
+
345
+ For another host:
346
+
347
+ 1. Remove only the `semanticjsmcp` entry from the documented host
348
+ configuration.
349
+ 2. Preserve every unrelated server and setting.
350
+ 3. Run:
351
+
352
+ ```bash
353
+ npm uninstall --global semantic-js-mcp
354
+ ```
355
+
356
+ Restart or reload the host. Confirm that Semantic JS MCP tools are no longer
357
+ registered.
358
+
359
+ ## Source Checkout
360
+
361
+ Use this section for repository development or when the user explicitly asks
362
+ to run a source checkout. Do not combine it with a registry or Codex plugin
363
+ installation.
364
+
365
+ From the repository root, run:
366
+
367
+ ```bash
368
+ npm ci
369
+ npm run check:runtime
370
+ npm run doctor
371
+ ```
372
+
373
+ For a non-Codex host that supports direct stdio commands, use its documented
374
+ schema and these values:
375
+
376
+ - server name: `semanticjsmcp`
377
+ - transport: `stdio`
378
+ - executable or command: `node`
379
+ - arguments: the absolute path to the checkout's `server.mjs`
380
+
381
+ Preserve every unrelated host entry. Restart or reload the host and complete
382
+ the source-checkout verification below. Do not run the global executable checks
383
+ for this route.
384
+
385
+ After restart, confirm that the host still contains the `semanticjsmcp` entry
386
+ with the recorded `node` command and absolute `server.mjs` argument. Open the
387
+ host's MCP tool list and apply the outcomes under
388
+ [Expected Result](#expected-result). Do not call an `lsp_*` tool unless the
389
+ user separately requests a functional test or explicitly approves the target
390
+ file.
391
+
392
+ The repository's `.mcp.json` is the relative-path configuration bundled by the
393
+ Codex plugin. It is not a universal configuration file for other MCP hosts.
394
+
395
+ ## Background
396
+
397
+ An MCP host is the application that starts the server and exposes its tools.
398
+ Installing an npm package places files and an executable on the machine, but it
399
+ does not register that executable with an MCP host.
400
+
401
+ A generic stdio host starts `semantic-js-mcp serve` as a child process and
402
+ communicates through standard input and standard output. The host owns that
403
+ process lifecycle, which is why manually starting the command does not make
404
+ tools appear in the host.
405
+
406
+ The Codex plugin route supplies both MCP configuration and the
407
+ semantic-navigation skill. Codex plugin state and tool discovery verify that
408
+ route, so it does not need a separate global npm installation or doctor run.
409
+
410
+ This procedure does not override system, developer, organization, repository,
411
+ or host security policy. Its authority is limited to the requested Semantic JS
412
+ MCP operation and never includes unrelated configuration, credentials, or
413
+ source-code analysis.
@@ -4,22 +4,15 @@ The package manifest defines an explicit public file allowlist and exposes the `
4
4
 
5
5
  ## Codex
6
6
 
7
- ```bash
8
- codex plugin marketplace add elnonathan/semantic-js-mcp
9
- codex plugin add semantic-js-mcp@elnonathan
10
- ```
11
-
12
- The marketplace entry pins a concrete npm package version. Codex CLI 0.144.4 is the minimum verified client for npm-backed marketplace installation. A release updates the package version, plugin manifest, and marketplace entry together.
13
-
14
- For an existing marketplace installation, refresh its local snapshot before reinstalling the plugin:
15
-
16
- ```bash
17
- codex plugin marketplace upgrade elnonathan
18
- codex plugin add semantic-js-mcp@elnonathan
19
- codex plugin list
20
- ```
7
+ The marketplace entry pins a concrete npm package version. Codex CLI 0.144.4
8
+ is the minimum verified client for npm-backed marketplace installation. A
9
+ release updates the package version, plugin manifest, and marketplace entry
10
+ together.
21
11
 
22
- `plugin add` reinstalls the plugin selected by the refreshed marketplace. Start a new Codex session after the listed version matches the release being installed.
12
+ Follow [Install With Codex](../SETUP.md#install-with-codex) for installation or
13
+ update commands, version checks, restart handling, and tool verification. This
14
+ document describes the distribution contract and does not define a second
15
+ setup procedure.
23
16
 
24
17
  ## Executable
25
18
 
@@ -85,7 +78,7 @@ The `publish.yml` workflow publishes tags matching `v*` from a GitHub-hosted Nod
85
78
 
86
79
  Configure the npm trusted publisher for GitHub user `elnonathan`, repository `semantic-js-mcp`, workflow filename `publish.yml`, and environment `npm-publish`. Allow `npm publish` only. In package publishing access, require two-factor authentication and disallow tokens.
87
80
 
88
- Create and push the matching `v<version>` tag only after the release commit is on `main` and its CI matrix passes. Approve the protected environment deployment, verify the published package, and then create the matching GitHub release.
81
+ Create and push the matching `v<version>` tag only after the release commit is on `main` and its CI matrix passes. If the protected environment requires review, approve the waiting deployment. Verify the published package before creating the matching GitHub release.
89
82
 
90
83
  ## Continuous Integration
91
84
 
@@ -10,6 +10,10 @@ Semantic JS MCP adds static semantic evidence to an agent's existing code invest
10
10
 
11
11
  > Review the authentication changes in this diff. Use semantic evidence to verify every material symbol and cross-workspace reference, corroborate reachability with text search and direct source inspection, and distinguish introduced defects from pre-existing behavior.
12
12
 
13
+ ## Review Combination Logic
14
+
15
+ > Review this precedence, fallback, normalization, or merge change. Identify every producer of the combined domain value, recover the invariant encoded by existing helpers and direct consumers, verify the discovered symbols and references with semantic evidence, and test a boundary case that could disprove the proposed rule.
16
+
13
17
  ## Check Current Diagnostics
14
18
 
15
19
  > Check current diagnostics for `src/handler.ts`. Treat the file as clean only if the language server confirms the current document snapshot; otherwise report the result as untrusted and explain the required follow-up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semantic-js-mcp",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Semantic understanding of JavaScript codebases for AI agents",
5
5
  "keywords": [
6
6
  "mcp",
@@ -28,6 +28,7 @@
28
28
  ".codex-plugin/",
29
29
  ".mcp.json",
30
30
  ".prettierrc.json",
31
+ "SETUP.md",
31
32
  "CHANGELOG.md",
32
33
  "CONTRIBUTING.md",
33
34
  "ROADMAP.md",
@@ -66,8 +67,8 @@
66
67
  "verify:published": "node scripts/postpublication-smoke.mjs",
67
68
  "benchmark": "node scripts/benchmark.mjs",
68
69
  "benchmark:lifecycle-memory": "node scripts/lifecycle-memory-benchmark.mjs",
69
- "format": "prettier --write \".codex-plugin/**/*.json\" \".agents/plugins/marketplace.json\" \".github/**/*.yml\" \".mcp.json\" \".prettierrc.json\" \"package.json\" \"package-lock.json\" \"CHANGELOG.md\" \"CONTRIBUTING.md\" \"README.md\" \"ROADMAP.md\" \"SECURITY.md\" \"*.mjs\" \"docs/**/*.md\" \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"skills/**/SKILL.md\" \"skills/**/agents/*.yaml\"",
70
- "format:check": "prettier --check \".codex-plugin/**/*.json\" \".agents/plugins/marketplace.json\" \".github/**/*.yml\" \".mcp.json\" \".prettierrc.json\" \"package.json\" \"package-lock.json\" \"CHANGELOG.md\" \"CONTRIBUTING.md\" \"README.md\" \"ROADMAP.md\" \"SECURITY.md\" \"*.mjs\" \"docs/**/*.md\" \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"skills/**/SKILL.md\" \"skills/**/agents/*.yaml\""
70
+ "format": "prettier --write \".codex-plugin/**/*.json\" \".agents/plugins/marketplace.json\" \".github/**/*.yml\" \".mcp.json\" \".prettierrc.json\" \"package.json\" \"package-lock.json\" \"CHANGELOG.md\" \"CONTRIBUTING.md\" \"README.md\" \"ROADMAP.md\" \"SECURITY.md\" \"SETUP.md\" \"*.mjs\" \"docs/**/*.md\" \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"skills/**/SKILL.md\" \"skills/**/agents/*.yaml\"",
71
+ "format:check": "prettier --check \".codex-plugin/**/*.json\" \".agents/plugins/marketplace.json\" \".github/**/*.yml\" \".mcp.json\" \".prettierrc.json\" \"package.json\" \"package-lock.json\" \"CHANGELOG.md\" \"CONTRIBUTING.md\" \"README.md\" \"ROADMAP.md\" \"SECURITY.md\" \"SETUP.md\" \"*.mjs\" \"docs/**/*.md\" \"lib/**/*.mjs\" \"scripts/**/*.mjs\" \"skills/**/SKILL.md\" \"skills/**/agents/*.yaml\""
71
72
  },
72
73
  "dependencies": {
73
74
  "@modelcontextprotocol/sdk": "1.27.1",
package/protocol.mjs CHANGED
@@ -408,7 +408,7 @@ export const RESULT_SCHEMA = Object.freeze({
408
408
  VERSION: 7,
409
409
  });
410
410
 
411
- export const SERVER_VERSION = "0.10.1";
411
+ export const SERVER_VERSION = "0.10.3";
412
412
 
413
413
  export const REQUIRED_RUNTIME_COMPONENT = Object.freeze({
414
414
  TYPESCRIPT_LANGUAGE_SERVER: "typescript-language-server/lib/cli.mjs",
@@ -2,6 +2,7 @@ export const REQUIRED_PACKAGE_FILE = Object.freeze({
2
2
  PACKAGE_MANIFEST: "package.json",
3
3
  PRETTIER_CONFIGURATION: ".prettierrc.json",
4
4
  README: "README.md",
5
+ SETUP: "SETUP.md",
5
6
  LICENSE: "LICENSE",
6
7
  CLI: "cli.mjs",
7
8
  SERVER: "server.mjs",
@@ -2,36 +2,103 @@ 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
+ SETUP: "SETUP.md",
6
+ DISTRIBUTION: "docs/distribution.md",
5
7
  GETTING_STARTED: "docs/getting-started.md",
8
+ SEMANTIC_NAVIGATION_SKILL: "skills/semantic-navigation/SKILL.md",
6
9
  CONTRIBUTING: "CONTRIBUTING.md",
7
10
  SECURITY: "SECURITY.md",
8
11
  });
9
12
 
10
- export const PUBLIC_ROOT_DOCUMENT = Object.freeze(["CHANGELOG.md", "CONTRIBUTING.md", "README.md", "ROADMAP.md", "SECURITY.md"]);
13
+ export const PUBLIC_ROOT_DOCUMENT = Object.freeze([
14
+ "CHANGELOG.md",
15
+ "CONTRIBUTING.md",
16
+ "README.md",
17
+ "ROADMAP.md",
18
+ "SECURITY.md",
19
+ "SETUP.md",
20
+ ]);
11
21
 
12
22
  export const PUBLIC_DOCUMENT_DIRECTORY = Object.freeze(["docs", "skills"]);
13
23
 
14
24
  export const DOCUMENTATION_REQUIREMENT = Object.freeze({
15
- README_HEADINGS: Object.freeze([
16
- "Installation",
17
- "Updating the Codex plugin",
18
- "Runtime",
19
- "Verification",
20
- "Current Limitations",
21
- "Reporting Problems",
22
- ]),
23
- README_LITERALS: Object.freeze(["codex plugin marketplace upgrade elnonathan", "codex plugin add semantic-js-mcp@elnonathan"]),
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",
43
+ ]),
24
44
  GETTING_STARTED_HEADINGS: Object.freeze([
25
45
  "Trace A Symbol",
26
46
  "Review A Security-Sensitive Change",
47
+ "Review Combination Logic",
27
48
  "Check Current Diagnostics",
28
49
  "Complete Evidence",
29
50
  "Partial Evidence",
30
51
  "Untrusted Diagnostics",
31
52
  "Startup Failure",
32
53
  ]),
33
- README_LINKS: Object.freeze(["docs/getting-started.md", "CONTRIBUTING.md", "SECURITY.md"]),
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",
64
+ "Removal",
65
+ "Source Checkout",
66
+ "Background",
67
+ ]),
68
+ SETUP_FIRST_SECTION: "Guards",
69
+ SETUP_LAST_SECTION: "Background",
70
+ DISTRIBUTION_HEADINGS: Object.freeze(["Release Verification", "npm Trusted Publishing"]),
71
+ SEMANTIC_NAVIGATION_SKILL_HEADINGS: Object.freeze(["Preserve Combination Invariants"]),
72
+ README_LINKS: Object.freeze(["SETUP.md", "docs/getting-started.md", "CONTRIBUTING.md", "SECURITY.md"]),
34
73
  GETTING_STARTED_LITERALS: Object.freeze([SEMANTIC_EVIDENCE_FOLLOW_UP_REASON.COLLECTION_PARTIAL, DOCTOR_REASON.RUNTIME_COMPONENT_MISSING]),
74
+ SETUP_LITERALS: Object.freeze([
75
+ "semantic-js-mcp doctor",
76
+ "semantic-js-mcp serve",
77
+ "does not override system, developer, organization, repository",
78
+ "npm uninstall --global semantic-js-mcp",
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.",
96
+ ]),
97
+ SEMANTIC_NAVIGATION_SKILL_LITERALS: Object.freeze([
98
+ "Identify every producer of the value",
99
+ "boundary case that could disprove",
100
+ "conceptual relationships that an LSP cannot establish",
101
+ ]),
35
102
  });
36
103
 
37
104
  export const DOCUMENTATION_REASON = Object.freeze({
@@ -39,6 +106,12 @@ export const DOCUMENTATION_REASON = Object.freeze({
39
106
  HEADING_MISSING: "required-heading-missing",
40
107
  LINK_MISSING: "required-link-missing",
41
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",
42
115
  PRIVATE_COORDINATION: "private-coordination-language-found",
43
116
  LOCAL_PATH: "local-absolute-path-found",
44
117
  });
@@ -6,14 +6,116 @@ 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")}`,
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")}`,
10
12
  [DOCUMENTATION_FILE.GETTING_STARTED]: `${headingText(DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS.join("\n")}`,
13
+ [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: `${headingText(DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS.join("\n")}`,
11
14
  [DOCUMENTATION_FILE.CONTRIBUTING]: "# Contributing",
12
15
  [DOCUMENTATION_FILE.SECURITY]: "# Security",
13
16
  };
14
17
 
15
18
  strictEqual(evaluateDocumentation(valid).length, 0, "Valid public documentation failed the gate");
16
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
+
17
119
  const missingHeading = {...valid, [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace("## Verification", "")};
18
120
  strictEqual(
19
121
  evaluateDocumentation(missingHeading).some((finding) => finding.reason === DOCUMENTATION_REASON.HEADING_MISSING),
@@ -46,26 +148,137 @@ strictEqual(
46
148
  "Missing canonical example literal was accepted",
47
149
  );
48
150
 
49
- const missingMarketplaceUpgrade = {
151
+ const missingSetupLink = {
152
+ ...valid,
153
+ [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replaceAll(
154
+ `](${DOCUMENTATION_REQUIREMENT.README_LINKS[0]})`,
155
+ "](missing-setup.md)",
156
+ ),
157
+ };
158
+ strictEqual(
159
+ evaluateDocumentation(missingSetupLink).some(
160
+ (finding) => finding.reason === DOCUMENTATION_REASON.LINK_MISSING && finding.link === DOCUMENTATION_REQUIREMENT.README_LINKS[0],
161
+ ),
162
+ true,
163
+ "Missing setup README link was accepted",
164
+ );
165
+
166
+ const missingSetupEntryInstruction = {
167
+ ...valid,
168
+ [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0], ""),
169
+ };
170
+ strictEqual(
171
+ evaluateDocumentation(missingSetupEntryInstruction).some(
172
+ (finding) =>
173
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
174
+ finding.literal === DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0],
175
+ ),
176
+ true,
177
+ "Missing README setup instruction was accepted",
178
+ );
179
+
180
+ const missingSetup = {...valid};
181
+ delete missingSetup[DOCUMENTATION_FILE.SETUP];
182
+ strictEqual(
183
+ evaluateDocumentation(missingSetup).some(
184
+ (finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.SETUP,
185
+ ),
186
+ true,
187
+ "Missing setup guide was accepted",
188
+ );
189
+
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 = {
217
+ ...valid,
218
+ [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: valid[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL].replace(
219
+ DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
220
+ "",
221
+ ),
222
+ };
223
+ strictEqual(
224
+ evaluateDocumentation(missingCombinationInvariant).some(
225
+ (finding) =>
226
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
227
+ finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
228
+ ),
229
+ true,
230
+ "Missing combination-invariant guidance was accepted",
231
+ );
232
+
233
+ const missingTrustedPublishingPolicy = {
234
+ ...valid,
235
+ [DOCUMENTATION_FILE.DISTRIBUTION]: valid[DOCUMENTATION_FILE.DISTRIBUTION].replace(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[2], ""),
236
+ };
237
+ strictEqual(
238
+ evaluateDocumentation(missingTrustedPublishingPolicy).some(
239
+ (finding) =>
240
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[2],
241
+ ),
242
+ true,
243
+ "Missing trusted-publishing policy was accepted",
244
+ );
245
+
246
+ const missingPostpublicationOrder = {
50
247
  ...valid,
51
- [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_LITERALS[0], ""),
248
+ [DOCUMENTATION_FILE.DISTRIBUTION]: valid[DOCUMENTATION_FILE.DISTRIBUTION].replace(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[5], ""),
52
249
  };
53
250
  strictEqual(
54
- evaluateDocumentation(missingMarketplaceUpgrade).some(
251
+ evaluateDocumentation(missingPostpublicationOrder).some(
55
252
  (finding) =>
56
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.README_LITERALS[0],
253
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[5],
57
254
  ),
58
255
  true,
59
- "Missing marketplace upgrade guidance was accepted",
256
+ "Missing postpublication order was accepted",
60
257
  );
61
258
 
62
259
  process.stdout.write(
63
260
  `${JSON.stringify(
64
261
  {
65
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",
66
271
  missingHeading: "rejected",
67
272
  missingLiteral: "rejected",
68
- missingMarketplaceUpgrade: "rejected",
273
+ missingSetupLink: "rejected",
274
+ missingSetupEntryInstruction: "rejected",
275
+ missingSetup: "rejected",
276
+ missingSetupSafetyBoundary: "rejected",
277
+ missingGenericServerName: "rejected",
278
+ missingCodexMarketplaceRefresh: "rejected",
279
+ missingCombinationInvariant: "rejected",
280
+ missingTrustedPublishingPolicy: "rejected",
281
+ missingPostpublicationOrder: "rejected",
69
282
  privateCoordination: "rejected",
70
283
  },
71
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,10 @@ export function evaluateDocumentation(documents) {
35
69
 
36
70
  const headingRequirements = [
37
71
  [DOCUMENTATION_FILE.README, DOCUMENTATION_REQUIREMENT.README_HEADINGS],
72
+ [DOCUMENTATION_FILE.SETUP, DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS],
73
+ [DOCUMENTATION_FILE.DISTRIBUTION, DOCUMENTATION_REQUIREMENT.DISTRIBUTION_HEADINGS],
38
74
  [DOCUMENTATION_FILE.GETTING_STARTED, DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS],
75
+ [DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL, DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS],
39
76
  ];
40
77
  for (const [file, requiredHeadings] of headingRequirements) {
41
78
  if (documents[file] === undefined) continue;
@@ -48,14 +85,30 @@ export function evaluateDocumentation(documents) {
48
85
 
49
86
  const readme = documents[DOCUMENTATION_FILE.README];
50
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
+ }
51
95
  for (const link of DOCUMENTATION_REQUIREMENT.README_LINKS) {
52
96
  if (readme.includes(`](${link})`)) continue;
53
97
  findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LINK_MISSING, link});
54
98
  }
55
- for (const literal of DOCUMENTATION_REQUIREMENT.README_LITERALS) {
99
+ for (const literal of DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS) {
56
100
  if (readme.includes(literal)) continue;
57
101
  findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
58
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
+ }
59
112
  }
60
113
 
61
114
  const gettingStarted = documents[DOCUMENTATION_FILE.GETTING_STARTED];
@@ -66,6 +119,59 @@ export function evaluateDocumentation(documents) {
66
119
  }
67
120
  }
68
121
 
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});
148
+ }
149
+ }
150
+
151
+ const semanticNavigationSkill = documents[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL];
152
+ if (semanticNavigationSkill !== undefined) {
153
+ for (const literal of DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS) {
154
+ if (semanticNavigationSkill.includes(literal)) continue;
155
+ findings.push({
156
+ file: DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL,
157
+ reason: DOCUMENTATION_REASON.LITERAL_MISSING,
158
+ literal,
159
+ });
160
+ }
161
+ }
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
+
69
175
  for (const [file, source] of Object.entries(documents)) {
70
176
  if (PRIVATE_COORDINATION_PATTERN.test(source)) {
71
177
  findings.push({file, reason: DOCUMENTATION_REASON.PRIVATE_COORDINATION});
@@ -131,6 +131,24 @@ Omitted collection limits still mean unlimited. The server never inserts a hidde
131
131
  4. Inspect focused tests for asserted behavior; run them when permitted.
132
132
  5. Re-read cited locations before presenting a finding.
133
133
 
134
+ ### Preserve Combination Invariants
135
+
136
+ When a change combines multiple sources into one domain value or introduces
137
+ precedence, fallback, normalization, ordering, or merge logic:
138
+
139
+ 1. Identify every producer of the value, not only the source involved in the
140
+ reported example.
141
+ 2. Find existing comparison, normalization, fallback, and combination helpers
142
+ and state the invariant they encode.
143
+ 3. Inspect direct consumers and focused tests to confirm how the combined value
144
+ is treated as a contract.
145
+ 4. Exercise a boundary case that could disprove the proposed precedence or
146
+ merge rule.
147
+
148
+ Semantic tools verify the identities and references discovered during this
149
+ work. Use text search, direct source inspection, tests, and domain reasoning for
150
+ conceptual relationships that an LSP cannot establish.
151
+
134
152
  An actionable finding requires a reachable path, a violated contract, and concrete impact. For commit or pull-request reviews, compare with the exact parent and classify material conclusions as `introduced`, `modified`, or `pre-existing`.
135
153
 
136
154
  Empty symbol, reference, or diagnostic results are evidence to investigate. Use `collection`, `textSearch`, `rg`, and direct inspection to establish the supported conclusion.
@@ -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.1`
5
+ Producer: `semantic-js-mcp` version `0.10.3`
6
6
  Display name: `Semantic JS MCP`
7
7
  Result schema version: `7`
8
8