Repository navigation
Stage update 2026-10-15: The Forge plugin and orchestrator are in alpha. On 2026-10-15 (UTC) a separate NXTG.AI team (product-oracle) ran the pre-registered core-job test on a clean install of the plugin (v3.14.0) and the orchestrator (forge 1.7.0). The test passed; each repeated check passed 3 of 3 (run 7). The evidence is attached to the forge-plugin v3.14.0 release. No SLA; interfaces may change without notice.
Superseded 2026-10-15: Stage: Public preview. Forge is public: anyone may install and use it. Its maturity is below alpha (pre-alpha): no SLA, and interfaces may change without notice.
[3.14.0] — 2026-10-14
MINOR. The orchestrator moves to an optional companion plugin, nxtg-forge-orchestrator, so nxtg-forge alone registers no orchestrator-mcp (C1 as registered). /nxtg-forge:feature creates its task through MCP with forge_create_task (C7 as registered). Semgrep MCP no longer shows as failed when the published package crashes. Plus the hardening from the codex review of v3.13.1 and the re-landed status order (#82). Breaking: the orchestrator tools' full names change, and forge_open_dashboard honours BROWSER again (3.13.1 had dropped it).
Changed (breaking for anyone who calls orchestrator tools by full name)
orchestrator-mcpmoves to a companion plugin,nxtg-forge-orchestrator, in the same marketplace (DIRECTIVE-NXTG-20261014-12, EMMA-SOUL design C).nxtg-forgeno longer registers it, so a user without the Forge orchestrator sees noorchestrator-mcpentry at all, rather than a connected stand-in. The companion carries today's launcher unchanged:forge mcpwhen theforgebinary is found, the stand-in that names the installer when it is not (never a failed server), andFORGE_PROJECT_ROOT=${CLAUDE_PROJECT_DIR}. Itsfind-forge.shand stand-in are byte copies ofnxtg-forge's, and a test keeps them equal.install.sh(forge-orchestrator) installs the companion when it finds theclaudeCLI.- The orchestrator now binds the Forge project when a session starts in a subdirectory. Claude Code passes the directory the session started in, and
forge mcpdoes not walk up, so a session started in<project>/srcgot "Forge is not initialized" (true of every release so far). The companion's launcher binds the nearest ancestor holding.forge/state.json. It never goes above$HOME, so forge-ui's global~/.forgenever counts; both paths are resolved first, so a relative value, a trailing slash or a symlinked$HOMEcannot make the walk loop or pass$HOME. With no Forge project above, the session directory is used as before. As a result, the companion'sstart.shis no longer a byte copy ofnxtg-forge's;find-forge.shand the stand-in still are. - The companion warns when the installed
nxtg-forgeis a release that still registersorchestrator-mcp(3.13.1 and earlier, kept untilclaude plugin update). Measured: with both installed, the session lists two connectedorchestrator-mcpservers, so every orchestrator tool appears twice. Once per session, the companion's hook namesclaude plugin update nxtg-forge@nxtg-forge. - Only NXTG forge is launched as the orchestrator.
find-forge.sh(both plugins, and so the session hint andfeature-task.sh) now accepts aforgeonly if its--versionreadsforge X.Y.Z, as every NXTG forge release does. The probe is bounded to 2 s (background and poll, so it also works with macOS's bash 3.2 and notimeout), and a candidate that is slower counts as not NXTG forge. Other programs namedforge, such as Atlassian's and Laravel's CLIs, used to be run asforge mcp, fail, and show as a failed server cached for 15 minutes. Now the search continues past them, and if no NXTG forge is found the stand-in names the other program and the fix, includingFORGE_BIN. - The orchestrator tools' full names change from
mcp__plugin_nxtg-forge_orchestrator-mcp__*tomcp__plugin_nxtg-forge-orchestrator_orchestrator-mcp__*. Nothing in the plugin names them in full: the commands use the short names, and the/nxtg-forge:featureStop gate matches the suffix. - The session hook still tells Claude, once per session, that the orchestrator is optional and the one command that adds it. With
forgeabsent:curl -fsSL https://forge.nxtg.ai/install.sh | sh. Withforgeinstalled but the companion not:claude plugin install nxtg-forge-orchestrator@nxtg-forge. With both: nothing. The hook reads Claude Code'sinstalled_plugins.json. It is printed even withFORGE_QUIET_HOOKS=1: it is one line of context Claude needs in every session, not progress output.
Fixed
- semgrep-mcp no longer shows as a failed server when uv is installed (DIRECTIVE-NXTG-20261014-11 part 4).
- The published
semgrep-mcp(0.9.0) exits 1 at import (ImportError: cannot import name 'eval_type_backport'from pydantic) before it answers, soexec uvx semgrep-mcpleft Claude Code with "MCP server failed", cached for 15 minutes. servers/semgrep-mcp/start.shnow runs the upstream behind a pure-bash guard. It buffers what the client sends and forwards it. If the upstream exits before its first stdout line, the buffer is replayed to the stand-in, which answers with no tools and says how to see the error (uvx --refresh semgrep-mcp). Once the upstream has answered, both directions are relayed unchanged. The same guard covers asemgrep-mcpon the PATH.- The guard is written for bash 3.2, the stock macOS
/bin/bashthat.mcp.json's plainbashruns: nocoproc, no{fd}allocation, no fractionalread -t. Its parts signal each other, and the session runs as its own process group. Checked under the officialbash:3.2image and bash 5.1. - SIGTERM to the launcher ends it and every process under it, both while relaying a running upstream and while the stand-in serves (PR #92 review M-1).
- No version pin ships:
--with 'pydantic<2.12'gets past the import, but the server then exits in its startup unless asemgrepCLI is on the PATH.
- The published
Hardening from the codex review of v3.13.1 (eb5499d), DIRECTIVE-NXTG-20261014-09
Fixed
tools/callvalidates its params before any tool runs (M1). v3.13.0 got this from the MCP SDK; v3.13.1's own loop checked only the tool name, so"arguments": "invalid"ran the project's tests andforge_open_dashboardwrote a dashboard file.paramsmust be an object,namea string, andargumentsan object or absent; anything else (a string, an array,null, a number) gets JSON-RPC error -32602 and no tool runs.- A frame that is not JSON-RPC 2.0 is rejected (L2). A message without
"jsonrpc": "2.0"(missing, or"1.0") gets -32600 Invalid Request and is never dispatched; v3.13.1 ran it. forge_open_dashboardhonoursBROWSERagain (M2). v3.13.1 replaced theopenpackage, whose bundledxdg-openreadBROWSER, with the system opener, so a Linux machine withBROWSERset and noxdg-openstopped opening the dashboard. The order is now:BROWSER(entries separated by:,%sreplaced by the file path or the path appended, run without a shell), then the system opener (xdg-open,open,start; skipped on WSL), then on WSL PowerShell andwslview, then the path hint.
Changed
/nxtg-forge:featurecreates its orchestrator task through MCP (DIRECTIVE-NXTG-20261014-11 part 3, C7 as registered). Step 4.2 callsforge_create_task(forge 1.7.0 and later) with the feature title and description, then claims the returnedtask_id. Thefeature-task.shhelper (forge plan --generate --spec) is used only when that tool is not available. If the tool returns an error or notask_id, Step 4.2 falls back to the helper. The feature Stop gate takes the task id from theforge_create_taskresult, counts an errored or id-less create as no task, and its "no task" message names the MCP tool first.start.shstarts Node once (L1). It no longer runs a separatenode -p process.versions.nodebefore the server;index.mjschecks the version itself and, on a Node older than 20, hands stdin/stdout to the same no-tools stand-in, so the handshake still completes and nothing is cached as a failed start. Measured on this host, freshbash start.shto the initialize reply, n=15 each: unloaded p50 41.9 → 27.0 ms; withtaskset -c 0,1and 8 busy processes on those cores, p50 235.8 → 120.8 ms. Real Node 18.20.8 gets the stand-in (exit 0, 0 tools); Node 20.10.0 gets all 8 tools.
Tests
- New: malformed
arguments(string, array,null, number) are rejected with no trace offorge_run_testsorforge_open_dashboardhaving run, with a valid call as the positive control; non-2.0 frames, in process and throughstart.sh;BROWSERparsing, order and fall-through, plus a realBROWSERprogram receiving the path; one Node start per launch on Node 20+ and the old-Node stand-in decided inside that one process. - vitest 326 → 344.