-
Notifications
You must be signed in to change notification settings - Fork 15
ci: flag a renamed tool as withdrawn in the sync #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
twk3
merged 3 commits into
main
from
miguel/eng-1540-automate-review-merge-and-npm-release-of-currentsmcp
Sep 28, 2026
+160
−30
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| /** | ||
| * The tool registrations in `server.ts`, read from its source rather than by | ||
| * importing it, so nothing the file carries is executed. | ||
| * | ||
| * Shared by `sync-readme-tools.mjs`, which builds the README table from them, | ||
| * and `withdrawn-tools.mjs`, which compares two copies of `server.ts` to find a | ||
| * tool a sync removes or renames. One pattern for both, so the check for a | ||
| * withdrawn tool cannot quietly find fewer tools than the README lists. | ||
| */ | ||
|
|
||
| // Quote-agnostic: the source is prettier-formatted with singleQuote, but the | ||
| // monorepo copy this file's subject is synced from has been both. | ||
| // Anchored on the tool name and its description, not on whatever function | ||
| // declares them. That call has been renamed three times — `server.registerTool`, | ||
| // then a local `registerTool` wrapper applying scope filtering, then | ||
| // `catalogTool` building a TOOL_CATALOG the server loops over — and each rename | ||
| // silently found zero tools until someone noticed. | ||
| // | ||
| // `currents-` is the stable part: `server.test.ts` asserts every registered name | ||
| // matches it, so a tool that stopped being found here would have to stop being a | ||
| // tool. The loop that registers them passes variables, so it cannot match and | ||
| // nothing is counted twice. | ||
| // | ||
| // The character class is the one `server.test.ts` allows, dots and slashes | ||
| // included, rather than the narrower `\w`. A name outside it would be skipped | ||
| // rather than reported: this only refuses to run when it finds *no* tools, so a | ||
| // partial match writes a README missing a tool and says nothing. That would be | ||
| // caught next by `host/readme.test.ts`, which fails when a registered tool is | ||
| // absent from the table — one step later, and for a reason that does not name | ||
| // the cause. | ||
| const TOOL_PATTERN = | ||
| /(['"])(currents-[A-Za-z0-9_./-]+)\1\s*,\s*\{\s*description:\s*(['"])((?:\\.|(?!\3)[^\\])*)\3/g; | ||
|
miguelangaranocurrents marked this conversation as resolved.
|
||
|
|
||
| const LINE_END = /[\n\r\u2028\u2029]/g; | ||
|
|
||
| /** | ||
| * `src` with every comment blanked to spaces, line breaks kept. | ||
| * | ||
| * A registration left behind in a comment is not a tool, and counting it as | ||
| * one would hide exactly the withdrawal `withdrawn-tools.mjs` is looking for: | ||
| * the name disappears from the catalog but still matches here. | ||
| * | ||
| * Strings and template literals are stepped over, so a `//` inside one (a URL | ||
| * in a description) does not start a comment. Two things are not handled, | ||
| * because `server.ts` has neither: regex literals, and a backtick nested inside | ||
| * a template's `${}`. | ||
| * | ||
| * @param {string} src | ||
| * @returns {string} | ||
| */ | ||
| export function withoutComments(src) { | ||
| let out = ""; | ||
| let i = 0; | ||
| while (i < src.length) { | ||
| const char = src[i]; | ||
| const next = src[i + 1]; | ||
| if (char === "/" && next === "/") { | ||
| // Every line terminator JavaScript recognises, not only LF: a comment | ||
| // running on past a CR would blank the registrations after it. | ||
| LINE_END.lastIndex = i; | ||
| const stop = LINE_END.exec(src)?.index ?? src.length; | ||
| out += " ".repeat(stop - i); | ||
| i = stop; | ||
| } else if (char === "/" && next === "*") { | ||
| const end = src.indexOf("*/", i + 2); | ||
| const stop = end === -1 ? src.length : end + 2; | ||
| out += src.slice(i, stop).replace(/[^\n\r\u2028\u2029]/g, " "); | ||
| i = stop; | ||
| } else if (char === "'" || char === '"' || char === "`") { | ||
| let j = i + 1; | ||
| while (j < src.length && src[j] !== char) { | ||
| j += src[j] === "\\" ? 2 : 1; | ||
| } | ||
| out += src.slice(i, j + 1); | ||
| i = j + 1; | ||
| } else { | ||
| out += char; | ||
| i += 1; | ||
| } | ||
| } | ||
| return out; | ||
| } | ||
|
|
||
| /** | ||
| * Every tool `serverSrc` registers, in source order, with its description as | ||
| * written between the quotes (escapes still in place). Registrations inside | ||
| * comments are not counted. | ||
| * | ||
| * @param {string} serverSrc | ||
| * @returns {{ name: string, rawDescription: string }[]} | ||
| */ | ||
| export function registeredTools(serverSrc) { | ||
| const code = withoutComments(serverSrc); | ||
| return [...code.matchAll(TOOL_PATTERN)].map((match) => ({ | ||
| name: match[2], | ||
| rawDescription: match[4], | ||
| })); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /** | ||
| * Prints the tools registered in one `server.ts` and absent from another, one | ||
| * per line, so a sync can tell whether it withdraws a tool. | ||
| * | ||
| * Deleted files are not enough to tell. The tools live in files under | ||
| * `src/tools`, but the name a client calls is the string `server.ts` | ||
| * registers, and a rename changes that string while leaving every file in | ||
| * place. `currents-get-affected-executions` became | ||
| * `currents-get-action-executions` that way, and the sync reported nothing | ||
| * withdrawn. | ||
| * | ||
| * Usage: node scripts/withdrawn-tools.mjs <before/server.ts> <after/server.ts> | ||
| * | ||
| * Exits 1 when either file registers no tools: a pattern that stopped | ||
| * matching would otherwise report every tool withdrawn, or none. | ||
| */ | ||
|
|
||
| import { readFileSync } from "node:fs"; | ||
| import { registeredTools } from "./tool-names.mjs"; | ||
|
|
||
| const [beforePath, afterPath] = process.argv.slice(2); | ||
| if (!beforePath || !afterPath) { | ||
| console.error("usage: withdrawn-tools.mjs <before/server.ts> <after/server.ts>"); | ||
| process.exit(2); | ||
| } | ||
|
|
||
| /** @param {string} path */ | ||
| function namesIn(path) { | ||
| const names = new Set( | ||
| registeredTools(readFileSync(path, "utf-8")).map((tool) => tool.name), | ||
| ); | ||
| if (names.size === 0) { | ||
| console.error(`ERROR: no tools found in ${path}`); | ||
| process.exit(1); | ||
| } | ||
| return names; | ||
| } | ||
|
|
||
| const before = namesIn(beforePath); | ||
| const after = namesIn(afterPath); | ||
| for (const name of before) { | ||
| if (!after.has(name)) console.log(name); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.