@nifrajs/cli 3.5.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/dist/app-files.d.ts +11 -0
- package/dist/app-files.d.ts.map +1 -0
- package/dist/app-files.js +34 -0
- package/dist/app-files.js.map +1 -0
- package/dist/assure-hydration.d.ts +7 -1
- package/dist/assure-hydration.d.ts.map +1 -1
- package/dist/assure-hydration.js +128 -43
- package/dist/assure-hydration.js.map +1 -1
- package/dist/assure.d.ts.map +1 -1
- package/dist/assure.js +16 -2
- package/dist/assure.js.map +1 -1
- package/dist/capabilities-tool.d.ts +5 -1
- package/dist/capabilities-tool.d.ts.map +1 -1
- package/dist/capabilities-tool.js +32 -10
- package/dist/capabilities-tool.js.map +1 -1
- package/dist/cdn-check.d.ts +41 -0
- package/dist/cdn-check.d.ts.map +1 -0
- package/dist/cdn-check.js +207 -0
- package/dist/cdn-check.js.map +1 -0
- package/dist/check-diagnostics.d.ts +4 -0
- package/dist/check-diagnostics.d.ts.map +1 -1
- package/dist/check-diagnostics.js +13 -0
- package/dist/check-diagnostics.js.map +1 -1
- package/dist/check-scan.d.ts +49 -7
- package/dist/check-scan.d.ts.map +1 -1
- package/dist/check-scan.js +291 -62
- package/dist/check-scan.js.map +1 -1
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +99 -63
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts +42 -16
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +262 -82
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.d.ts +57 -5
- package/dist/command-catalog.d.ts.map +1 -1
- package/dist/command-catalog.js +270 -26
- package/dist/command-catalog.js.map +1 -1
- package/dist/config-target.d.ts +8 -0
- package/dist/config-target.d.ts.map +1 -0
- package/dist/config-target.js +45 -0
- package/dist/config-target.js.map +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +52 -13
- package/dist/contracts.js.map +1 -1
- package/dist/db-audit.d.ts +37 -0
- package/dist/db-audit.d.ts.map +1 -0
- package/dist/db-audit.js +99 -0
- package/dist/db-audit.js.map +1 -0
- package/dist/db-child.d.ts +64 -0
- package/dist/db-child.d.ts.map +1 -0
- package/dist/db-child.js +213 -0
- package/dist/db-child.js.map +1 -0
- package/dist/db-config.d.ts +60 -0
- package/dist/db-config.d.ts.map +1 -0
- package/dist/db-config.js +176 -0
- package/dist/db-config.js.map +1 -0
- package/dist/db-tool.d.ts +84 -0
- package/dist/db-tool.d.ts.map +1 -0
- package/dist/db-tool.js +537 -0
- package/dist/db-tool.js.map +1 -0
- package/dist/dev-bun-config.d.ts +20 -13
- package/dist/dev-bun-config.d.ts.map +1 -1
- package/dist/dev-bun-config.js +98 -41
- package/dist/dev-bun-config.js.map +1 -1
- package/dist/dev-feed-tool.d.ts +106 -0
- package/dist/dev-feed-tool.d.ts.map +1 -0
- package/dist/dev-feed-tool.js +713 -0
- package/dist/dev-feed-tool.js.map +1 -0
- package/dist/dev-server-client.d.ts +67 -0
- package/dist/dev-server-client.d.ts.map +1 -0
- package/dist/dev-server-client.js +195 -0
- package/dist/dev-server-client.js.map +1 -0
- package/dist/diff-tool.d.ts +3 -3
- package/dist/diff-tool.d.ts.map +1 -1
- package/dist/diff-tool.js +6 -5
- package/dist/diff-tool.js.map +1 -1
- package/dist/docs-search.d.ts.map +1 -1
- package/dist/docs-search.js +12 -4
- package/dist/docs-search.js.map +1 -1
- package/dist/doctor.d.ts +17 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +76 -15
- package/dist/doctor.js.map +1 -1
- package/dist/env-file.d.ts +15 -0
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +73 -1
- package/dist/env-file.js.map +1 -1
- package/dist/fix-recipes.d.ts.map +1 -1
- package/dist/fix-recipes.js +41 -21
- package/dist/fix-recipes.js.map +1 -1
- package/dist/frontend-guidance.js +5 -5
- package/dist/frontend-guidance.js.map +1 -1
- package/dist/i18n-check.d.ts +16 -0
- package/dist/i18n-check.d.ts.map +1 -0
- package/dist/i18n-check.js +154 -0
- package/dist/i18n-check.js.map +1 -0
- package/dist/init-agents.d.ts +49 -13
- package/dist/init-agents.d.ts.map +1 -1
- package/dist/init-agents.js +239 -34
- package/dist/init-agents.js.map +1 -1
- package/dist/internal/code-unit-order.d.ts +4 -0
- package/dist/internal/code-unit-order.d.ts.map +1 -0
- package/dist/internal/code-unit-order.js +6 -0
- package/dist/internal/code-unit-order.js.map +1 -0
- package/dist/internal/script-kind.d.ts +7 -0
- package/dist/internal/script-kind.d.ts.map +1 -0
- package/dist/internal/script-kind.js +12 -0
- package/dist/internal/script-kind.js.map +1 -0
- package/dist/internal/source-facts.d.ts +5 -0
- package/dist/internal/source-facts.d.ts.map +1 -1
- package/dist/internal/source-facts.js +61 -5
- package/dist/internal/source-facts.js.map +1 -1
- package/dist/internal/typescript-import.d.ts +49 -24
- package/dist/internal/typescript-import.d.ts.map +1 -1
- package/dist/internal/typescript-import.js +507 -67
- package/dist/internal/typescript-import.js.map +1 -1
- package/dist/introspect.d.ts +5 -1
- package/dist/introspect.d.ts.map +1 -1
- package/dist/introspect.js +92 -27
- package/dist/introspect.js.map +1 -1
- package/dist/learn.js +3 -3
- package/dist/learn.js.map +1 -1
- package/dist/levels-tool.d.ts +10 -0
- package/dist/levels-tool.d.ts.map +1 -1
- package/dist/levels-tool.js +33 -3
- package/dist/levels-tool.js.map +1 -1
- package/dist/load.d.ts +67 -17
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +78 -18
- package/dist/load.js.map +1 -1
- package/dist/manifest-tool.d.ts.map +1 -1
- package/dist/manifest-tool.js +3 -2
- package/dist/manifest-tool.js.map +1 -1
- package/dist/mcp-app-tool.d.ts.map +1 -1
- package/dist/mcp-app-tool.js +2 -0
- package/dist/mcp-app-tool.js.map +1 -1
- package/dist/mcp-context.js +1 -1
- package/dist/mcp-context.js.map +1 -1
- package/dist/mcp-delegate.d.ts +57 -0
- package/dist/mcp-delegate.d.ts.map +1 -0
- package/dist/mcp-delegate.js +130 -0
- package/dist/mcp-delegate.js.map +1 -0
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +23 -5
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-exec.d.ts +2 -0
- package/dist/mcp-exec.d.ts.map +1 -1
- package/dist/mcp-exec.js +132 -108
- package/dist/mcp-exec.js.map +1 -1
- package/dist/mcp-http.d.ts.map +1 -1
- package/dist/mcp-http.js +17 -5
- package/dist/mcp-http.js.map +1 -1
- package/dist/mcp-io.d.ts +14 -0
- package/dist/mcp-io.d.ts.map +1 -1
- package/dist/mcp-io.js +45 -0
- package/dist/mcp-io.js.map +1 -1
- package/dist/mcp-isolate.d.ts +48 -0
- package/dist/mcp-isolate.d.ts.map +1 -0
- package/dist/mcp-isolate.js +222 -0
- package/dist/mcp-isolate.js.map +1 -0
- package/dist/mcp-project-child.d.ts +74 -0
- package/dist/mcp-project-child.d.ts.map +1 -0
- package/dist/mcp-project-child.js +130 -0
- package/dist/mcp-project-child.js.map +1 -0
- package/dist/mcp-render.js +2 -2
- package/dist/mcp-render.js.map +1 -1
- package/dist/mcp-root.d.ts +37 -9
- package/dist/mcp-root.d.ts.map +1 -1
- package/dist/mcp-root.js +110 -23
- package/dist/mcp-root.js.map +1 -1
- package/dist/mcp-run.d.ts +18 -1
- package/dist/mcp-run.d.ts.map +1 -1
- package/dist/mcp-run.js +82 -9
- package/dist/mcp-run.js.map +1 -1
- package/dist/mcp-ws.js +4 -1
- package/dist/mcp-ws.js.map +1 -1
- package/dist/mcp.d.ts +16 -3
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +81 -53
- package/dist/mcp.js.map +1 -1
- package/dist/migrate-layout.d.ts +44 -0
- package/dist/migrate-layout.d.ts.map +1 -0
- package/dist/migrate-layout.js +982 -0
- package/dist/migrate-layout.js.map +1 -0
- package/dist/openapi-tool.d.ts +2 -2
- package/dist/openapi-tool.d.ts.map +1 -1
- package/dist/openapi-tool.js.map +1 -1
- package/dist/openapi-types.d.ts +2 -1
- package/dist/openapi-types.d.ts.map +1 -1
- package/dist/openapi-types.js +240 -12
- package/dist/openapi-types.js.map +1 -1
- package/dist/pipeline-report.d.ts +3 -2
- package/dist/pipeline-report.d.ts.map +1 -1
- package/dist/pipeline-report.js +19 -16
- package/dist/pipeline-report.js.map +1 -1
- package/dist/port.d.ts +5 -4
- package/dist/port.d.ts.map +1 -1
- package/dist/port.js +46 -29
- package/dist/port.js.map +1 -1
- package/dist/project-facts.d.ts +5 -0
- package/dist/project-facts.d.ts.map +1 -1
- package/dist/project-facts.js.map +1 -1
- package/dist/recipes/4.0.0.d.ts +9 -0
- package/dist/recipes/4.0.0.d.ts.map +1 -0
- package/dist/recipes/4.0.0.js +34 -0
- package/dist/recipes/4.0.0.js.map +1 -0
- package/dist/recipes/index.d.ts.map +1 -1
- package/dist/recipes/index.js +2 -0
- package/dist/recipes/index.js.map +1 -1
- package/dist/release-verification.d.ts +11 -1
- package/dist/release-verification.d.ts.map +1 -1
- package/dist/release-verification.js +47 -4
- package/dist/release-verification.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +12 -8
- package/dist/review.js.map +1 -1
- package/dist/route-types.d.ts +28 -0
- package/dist/route-types.d.ts.map +1 -0
- package/dist/route-types.js +106 -0
- package/dist/route-types.js.map +1 -0
- package/dist/rules/codes.d.ts +9 -0
- package/dist/rules/codes.d.ts.map +1 -1
- package/dist/rules/codes.js +9 -0
- package/dist/rules/codes.js.map +1 -1
- package/dist/rules/data-guard.d.ts +12 -0
- package/dist/rules/data-guard.d.ts.map +1 -0
- package/dist/rules/data-guard.js +213 -0
- package/dist/rules/data-guard.js.map +1 -0
- package/dist/rules/index.d.ts +3 -0
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +17 -5
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/islands.d.ts.map +1 -1
- package/dist/rules/islands.js +7 -4
- package/dist/rules/islands.js.map +1 -1
- package/dist/rules/legacy.d.ts.map +1 -1
- package/dist/rules/legacy.js +71 -38
- package/dist/rules/legacy.js.map +1 -1
- package/dist/rules/nano.d.ts.map +1 -1
- package/dist/rules/nano.js +15 -6
- package/dist/rules/nano.js.map +1 -1
- package/dist/rules/pages.d.ts +15 -0
- package/dist/rules/pages.d.ts.map +1 -0
- package/dist/rules/pages.js +88 -0
- package/dist/rules/pages.js.map +1 -0
- package/dist/rules/routes.d.ts +11 -0
- package/dist/rules/routes.d.ts.map +1 -1
- package/dist/rules/routes.js +180 -2
- package/dist/rules/routes.js.map +1 -1
- package/dist/rules/secrets.d.ts +10 -0
- package/dist/rules/secrets.d.ts.map +1 -0
- package/dist/rules/secrets.js +169 -0
- package/dist/rules/secrets.js.map +1 -0
- package/dist/rules/security.d.ts +7 -0
- package/dist/rules/security.d.ts.map +1 -1
- package/dist/rules/security.js +171 -65
- package/dist/rules/security.js.map +1 -1
- package/dist/rules/typescript.d.ts +16 -0
- package/dist/rules/typescript.d.ts.map +1 -0
- package/dist/rules/typescript.js +59 -0
- package/dist/rules/typescript.js.map +1 -0
- package/dist/rules/zones.d.ts +17 -0
- package/dist/rules/zones.d.ts.map +1 -0
- package/dist/rules/zones.js +174 -0
- package/dist/rules/zones.js.map +1 -0
- package/dist/scaffold.d.ts +25 -12
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +132 -87
- package/dist/scaffold.js.map +1 -1
- package/dist/sdk.d.ts +17 -3
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +941 -153
- package/dist/sdk.js.map +1 -1
- package/dist/search-terms.d.ts +2 -0
- package/dist/search-terms.d.ts.map +1 -1
- package/dist/search-terms.js +23 -8
- package/dist/search-terms.js.map +1 -1
- package/dist/smoke.d.ts +79 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +494 -0
- package/dist/smoke.js.map +1 -0
- package/dist/sync-manifest.d.ts.map +1 -1
- package/dist/sync-manifest.js +2 -1
- package/dist/sync-manifest.js.map +1 -1
- package/dist/sync-routes.d.ts +4 -3
- package/dist/sync-routes.d.ts.map +1 -1
- package/dist/sync-routes.js +4 -3
- package/dist/sync-routes.js.map +1 -1
- package/dist/test-tool.d.ts.map +1 -1
- package/dist/test-tool.js +30 -11
- package/dist/test-tool.js.map +1 -1
- package/dist/types-search.d.ts.map +1 -1
- package/dist/types-search.js +11 -5
- package/dist/types-search.js.map +1 -1
- package/dist/upgrade.d.ts +36 -6
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +167 -29
- package/dist/upgrade.js.map +1 -1
- package/dist/verification-plan.d.ts.map +1 -1
- package/dist/verification-plan.js +4 -0
- package/dist/verification-plan.js.map +1 -1
- package/dist/verification.d.ts.map +1 -1
- package/dist/verification.js +45 -12
- package/dist/verification.js.map +1 -1
- package/dist/web-app-options.d.ts +19 -0
- package/dist/web-app-options.d.ts.map +1 -0
- package/dist/web-app-options.js +49 -0
- package/dist/web-app-options.js.map +1 -0
- package/dist/work-graph.d.ts.map +1 -1
- package/dist/work-graph.js +29 -13
- package/dist/work-graph.js.map +1 -1
- package/docs/examples.json +185 -29
- package/docs/llms-full.txt +3807 -580
- package/docs/types.json +14229 -10939
- package/package.json +19 -12
- package/src/app-files.ts +40 -0
- package/src/assure-hydration.ts +139 -46
- package/src/assure.ts +17 -2
- package/src/capabilities-tool.ts +35 -9
- package/src/cdn-check.ts +268 -0
- package/src/check-diagnostics.ts +17 -0
- package/src/check-scan.ts +358 -63
- package/src/check.ts +97 -62
- package/src/cli.ts +296 -87
- package/src/command-catalog.ts +374 -38
- package/src/config-target.ts +47 -0
- package/src/contracts.ts +54 -12
- package/src/db-audit.ts +128 -0
- package/src/db-child.ts +285 -0
- package/src/db-config.ts +224 -0
- package/src/db-tool.ts +690 -0
- package/src/dev-bun-config.ts +116 -40
- package/src/dev-feed-tool.ts +912 -0
- package/src/dev-server-client.ts +267 -0
- package/src/diff-tool.ts +6 -5
- package/src/docs-search.ts +10 -3
- package/src/doctor.ts +98 -15
- package/src/env-file.ts +78 -1
- package/src/fix-recipes.ts +44 -31
- package/src/frontend-guidance.ts +5 -5
- package/src/i18n-check.ts +179 -0
- package/src/init-agents.ts +301 -38
- package/src/internal/code-unit-order.ts +5 -0
- package/src/internal/script-kind.ts +11 -0
- package/src/internal/source-facts.ts +96 -22
- package/src/internal/typescript-import.ts +663 -68
- package/src/introspect.ts +90 -27
- package/src/learn.ts +3 -3
- package/src/levels-tool.ts +47 -2
- package/src/load.ts +140 -33
- package/src/manifest-tool.ts +3 -2
- package/src/mcp-app-tool.ts +2 -0
- package/src/mcp-context.ts +1 -1
- package/src/mcp-delegate.ts +162 -0
- package/src/mcp-docs-tools.ts +23 -5
- package/src/mcp-exec.ts +160 -132
- package/src/mcp-http.ts +19 -5
- package/src/mcp-io.ts +57 -0
- package/src/mcp-isolate.ts +287 -0
- package/src/mcp-project-child.ts +182 -0
- package/src/mcp-render.ts +2 -2
- package/src/mcp-root.ts +117 -22
- package/src/mcp-run.ts +104 -10
- package/src/mcp-ws.ts +4 -1
- package/src/mcp.ts +106 -64
- package/src/migrate-layout.ts +1121 -0
- package/src/openapi-tool.ts +7 -3
- package/src/openapi-types.ts +315 -13
- package/src/pipeline-report.ts +22 -18
- package/src/port.ts +53 -30
- package/src/project-facts.ts +5 -0
- package/src/recipes/4.0.0.ts +35 -0
- package/src/recipes/index.ts +2 -0
- package/src/release-verification.ts +74 -5
- package/src/review.ts +13 -8
- package/src/route-types.ts +133 -0
- package/src/rules/codes.ts +9 -0
- package/src/rules/data-guard.ts +230 -0
- package/src/rules/index.ts +18 -5
- package/src/rules/islands.ts +12 -4
- package/src/rules/legacy.ts +71 -39
- package/src/rules/nano.ts +18 -6
- package/src/rules/pages.ts +99 -0
- package/src/rules/routes.ts +181 -2
- package/src/rules/secrets.ts +174 -0
- package/src/rules/security.ts +198 -70
- package/src/rules/typescript.ts +77 -0
- package/src/rules/zones.ts +195 -0
- package/src/scaffold.ts +147 -95
- package/src/sdk.ts +1215 -158
- package/src/search-terms.ts +24 -7
- package/src/smoke.ts +607 -0
- package/src/sync-manifest.ts +2 -1
- package/src/sync-routes.ts +4 -3
- package/src/test-tool.ts +32 -11
- package/src/types-search.ts +11 -4
- package/src/upgrade.ts +205 -25
- package/src/verification-plan.ts +13 -0
- package/src/verification.ts +50 -12
- package/src/web-app-options.ts +57 -0
- package/src/work-graph.ts +27 -13
package/src/manifest-tool.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
serializeNifraManifestSignature,
|
|
13
13
|
signNifraManifest,
|
|
14
14
|
} from "@nifrajs/core/manifest"
|
|
15
|
+
import { codeUnitOrder } from "./internal/code-unit-order.ts"
|
|
15
16
|
import { collectProjectVerification } from "./verification.ts"
|
|
16
17
|
|
|
17
18
|
export const DEFAULT_MANIFEST_FILE = "nifra.manifest.json"
|
|
@@ -23,8 +24,8 @@ export function formatManifestDiff(diff: NifraManifestDiff): string {
|
|
|
23
24
|
.sort(
|
|
24
25
|
(a, b) =>
|
|
25
26
|
weight[a.severity] - weight[b.severity] ||
|
|
26
|
-
a.path
|
|
27
|
-
a.method
|
|
27
|
+
codeUnitOrder(a.path, b.path) ||
|
|
28
|
+
codeUnitOrder(a.method, b.method),
|
|
28
29
|
)
|
|
29
30
|
.map(
|
|
30
31
|
(change) =>
|
package/src/mcp-app-tool.ts
CHANGED
|
@@ -54,6 +54,8 @@ export function examplesAppTool(loadExamples: () => Promise<Example[] | undefine
|
|
|
54
54
|
annotations: {
|
|
55
55
|
title: "Browse the nifra example gallery",
|
|
56
56
|
readOnlyHint: true,
|
|
57
|
+
destructiveHint: false,
|
|
58
|
+
idempotentHint: true,
|
|
57
59
|
openWorldHint: false,
|
|
58
60
|
},
|
|
59
61
|
description:
|
package/src/mcp-context.ts
CHANGED
|
@@ -74,7 +74,7 @@ export function projectResources(
|
|
|
74
74
|
{
|
|
75
75
|
uri: "nifra://openapi",
|
|
76
76
|
name: "OpenAPI 3.1",
|
|
77
|
-
description: "OpenAPI document generated from backend.ts using @nifrajs/schema.",
|
|
77
|
+
description: "OpenAPI document generated from backend/app.ts using @nifrajs/schema.",
|
|
78
78
|
mimeType: "application/json",
|
|
79
79
|
read: async () => ({ text: await openApiHandler({ format: "json" }, loadAppCached) }),
|
|
80
80
|
},
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One nifra per MCP session: the version the project installs.
|
|
3
|
+
*
|
|
4
|
+
* A client that spawns a globally installed `nifra mcp` against a project pinned to a different
|
|
5
|
+
* release gets types, checks, docs and contracts for a surface the project does not have - and every
|
|
6
|
+
* answer still reads as authoritative. So the server settles the version before it serves:
|
|
7
|
+
*
|
|
8
|
+
* 1. The project installs its own `@nifrajs/cli` at a different version: hand the whole session to
|
|
9
|
+
* that CLI (`node_modules/.bin/nifra mcp`, stdio inherited) and step aside.
|
|
10
|
+
* 2. Handing off is impossible (no project CLI, a missing bin, or this process IS the hand-off and
|
|
11
|
+
* the versions still disagree): serve, but the version-sensitive tools refuse with the fix
|
|
12
|
+
* instead of answering for the wrong release.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { existsSync } from "node:fs"
|
|
16
|
+
import { readFile } from "node:fs/promises"
|
|
17
|
+
import { dirname, join } from "node:path"
|
|
18
|
+
import type { McpTool, McpToolResult } from "./mcp-protocol.ts"
|
|
19
|
+
import type { ToolingDrift } from "./mcp-root.ts"
|
|
20
|
+
|
|
21
|
+
/** Set on the hand-off child so it never hands off again, whatever it finds. */
|
|
22
|
+
export const MCP_DELEGATED_ENV = "NIFRA_MCP_DELEGATED"
|
|
23
|
+
|
|
24
|
+
/** The tools whose answer is a property of the nifra release: they refuse under a version split. */
|
|
25
|
+
export const VERSION_SENSITIVE_TOOLS: ReadonlySet<string> = new Set([
|
|
26
|
+
"nifra_check",
|
|
27
|
+
"nifra_types",
|
|
28
|
+
"nifra_docs",
|
|
29
|
+
"nifra_example",
|
|
30
|
+
"nifra_assure",
|
|
31
|
+
"nifra_contracts",
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
/** The project's own CLI: its version and, when the install linked one, its bin. */
|
|
35
|
+
export interface ProjectCli {
|
|
36
|
+
readonly version: string
|
|
37
|
+
readonly bin?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const BIN_NAME = process.platform === "win32" ? "nifra.exe" : "nifra"
|
|
41
|
+
const MAX_ANCESTOR_DIRS = 8
|
|
42
|
+
|
|
43
|
+
/** The nearest `@nifrajs/cli` install at or above `root` (a monorepo app hoists to the repo root). */
|
|
44
|
+
export async function findProjectCli(root: string): Promise<ProjectCli | undefined> {
|
|
45
|
+
let dir = root
|
|
46
|
+
for (let depth = 0; depth < MAX_ANCESTOR_DIRS; depth++) {
|
|
47
|
+
const modules = join(dir, "node_modules")
|
|
48
|
+
try {
|
|
49
|
+
const meta = JSON.parse(
|
|
50
|
+
await readFile(join(modules, "@nifrajs", "cli", "package.json"), "utf8"),
|
|
51
|
+
) as { version?: unknown }
|
|
52
|
+
if (typeof meta.version === "string" && meta.version.length > 0) {
|
|
53
|
+
const bin = join(modules, ".bin", BIN_NAME)
|
|
54
|
+
return existsSync(bin) ? { version: meta.version, bin } : { version: meta.version }
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// Not installed at this level - keep walking up.
|
|
58
|
+
}
|
|
59
|
+
const parent = dirname(dir)
|
|
60
|
+
if (parent === dir) return undefined
|
|
61
|
+
dir = parent
|
|
62
|
+
}
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Hand this stdio session to the project's own CLI when it installs a different version. Resolves to
|
|
68
|
+
* the child's exit code once the session ends, or `undefined` when this process should serve itself
|
|
69
|
+
* (versions agree, no project CLI or bin, already a hand-off, or the spawn failed). Must run before
|
|
70
|
+
* anything reads stdin: the child inherits the descriptor and owns the whole conversation.
|
|
71
|
+
*/
|
|
72
|
+
export async function delegateToProjectCli(options: {
|
|
73
|
+
readonly cwd: string
|
|
74
|
+
readonly root: string
|
|
75
|
+
readonly version: string
|
|
76
|
+
readonly args: readonly string[]
|
|
77
|
+
}): Promise<number | undefined> {
|
|
78
|
+
if (process.env[MCP_DELEGATED_ENV] === "1") return undefined
|
|
79
|
+
const cli = await findProjectCli(options.root)
|
|
80
|
+
if (cli === undefined || cli.version === options.version || cli.bin === undefined)
|
|
81
|
+
return undefined
|
|
82
|
+
return handOffSession([cli.bin, "mcp", ...options.args], {
|
|
83
|
+
cwd: options.cwd,
|
|
84
|
+
env: { ...process.env, [MCP_DELEGATED_ENV]: "1" },
|
|
85
|
+
announce: `[nifra] mcp: this CLI is ${options.version}; the project installs @nifrajs/cli ${cli.version} - serving this session from ${cli.bin}`,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Hand this stdio session to `command`: the child inherits stdin, stdout and stderr and owns the
|
|
91
|
+
* whole conversation, and this process forwards signals and waits. Resolves to the child's exit code,
|
|
92
|
+
* or `undefined` when it could not start. Must run before anything reads stdin.
|
|
93
|
+
*/
|
|
94
|
+
export async function handOffSession(
|
|
95
|
+
command: readonly string[],
|
|
96
|
+
options: {
|
|
97
|
+
readonly cwd: string
|
|
98
|
+
readonly env: Record<string, string | undefined>
|
|
99
|
+
/** Written to stderr once the child runs. */
|
|
100
|
+
readonly announce?: string
|
|
101
|
+
},
|
|
102
|
+
): Promise<number | undefined> {
|
|
103
|
+
let child: ReturnType<typeof Bun.spawn>
|
|
104
|
+
try {
|
|
105
|
+
child = Bun.spawn([...command], {
|
|
106
|
+
cwd: options.cwd,
|
|
107
|
+
env: options.env,
|
|
108
|
+
stdin: "inherit",
|
|
109
|
+
stdout: "inherit",
|
|
110
|
+
stderr: "inherit",
|
|
111
|
+
})
|
|
112
|
+
} catch {
|
|
113
|
+
return undefined
|
|
114
|
+
}
|
|
115
|
+
// stderr only: stdout is the JSON-RPC channel, and it now belongs to the child.
|
|
116
|
+
if (options.announce !== undefined) process.stderr.write(`${options.announce}\n`)
|
|
117
|
+
const forward = (signal: NodeJS.Signals) => () => child.kill(signal)
|
|
118
|
+
const onTerm = forward("SIGTERM")
|
|
119
|
+
const onInt = forward("SIGINT")
|
|
120
|
+
process.on("SIGTERM", onTerm)
|
|
121
|
+
process.on("SIGINT", onInt)
|
|
122
|
+
try {
|
|
123
|
+
return await child.exited
|
|
124
|
+
} finally {
|
|
125
|
+
process.off("SIGTERM", onTerm)
|
|
126
|
+
process.off("SIGINT", onInt)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const refusalFix = (drift: ToolingDrift): string =>
|
|
131
|
+
drift.package === "@nifrajs/cli"
|
|
132
|
+
? "Fix: point the MCP client at the project's own CLI (`./node_modules/.bin/nifra mcp`, reinstalling if that file is missing), or restart this server from the project directory so it hands the session over."
|
|
133
|
+
: `Fix: add the matching CLI to the project (\`bun add -d @nifrajs/cli@${drift.project}\`) and restart the MCP server - it then hands the session to the project's CLI - or point the client at a nifra ${drift.project} install.`
|
|
134
|
+
|
|
135
|
+
/** The in-band refusal a version-sensitive tool returns under a version split. */
|
|
136
|
+
export const versionRefusal = (tool: string, drift: ToolingDrift): McpToolResult => ({
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: "text",
|
|
140
|
+
text:
|
|
141
|
+
`${tool} refused: this MCP server runs nifra CLI ${drift.cli}, but the project installs ${drift.package} ${drift.project}, ` +
|
|
142
|
+
`so its answer would describe a different nifra than your code builds with. ${refusalFix(drift)}`,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
isError: true,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Swap each version-sensitive tool's handler for the refusal while `drift` holds. Apply it before
|
|
150
|
+
* namespacing, so a monorepo's `nifra_<app>_check` refuses too. No drift: the tools pass through.
|
|
151
|
+
*/
|
|
152
|
+
export function refuseVersionSensitive(
|
|
153
|
+
tools: McpTool[],
|
|
154
|
+
drift: ToolingDrift | undefined,
|
|
155
|
+
): McpTool[] {
|
|
156
|
+
if (drift === undefined) return tools
|
|
157
|
+
return tools.map((tool) =>
|
|
158
|
+
VERSION_SENSITIVE_TOOLS.has(tool.name)
|
|
159
|
+
? { ...tool, handler: async () => versionRefusal(tool.name, drift) }
|
|
160
|
+
: tool,
|
|
161
|
+
)
|
|
162
|
+
}
|
package/src/mcp-docs-tools.ts
CHANGED
|
@@ -28,7 +28,13 @@ export function docsTools(
|
|
|
28
28
|
// The annotations state that for MCP hosts (and directory reviews check for them).
|
|
29
29
|
{
|
|
30
30
|
name: "nifra_docs",
|
|
31
|
-
annotations: {
|
|
31
|
+
annotations: {
|
|
32
|
+
title: "Search nifra docs",
|
|
33
|
+
readOnlyHint: true,
|
|
34
|
+
destructiveHint: false,
|
|
35
|
+
idempotentHint: true,
|
|
36
|
+
openWorldHint: false,
|
|
37
|
+
},
|
|
32
38
|
description:
|
|
33
39
|
"Search nifra's framework documentation and get back ONLY the matching sections - auth, uploads, ISR, WebSockets, loaders, deployment, anything. Call with no query for the cheap section index; pass query (e.g. \"isr revalidate\") for the top matching sections. Use this instead of reading llms-full.txt (~150 KB) whole. For the EXACT TypeScript of a type/interface/function, call nifra_types instead (don't read .d.ts files).",
|
|
34
40
|
inputSchema: {
|
|
@@ -57,10 +63,12 @@ export function docsTools(
|
|
|
57
63
|
annotations: {
|
|
58
64
|
title: "Get a verified nifra example",
|
|
59
65
|
readOnlyHint: true,
|
|
66
|
+
destructiveHint: false,
|
|
67
|
+
idempotentHint: true,
|
|
60
68
|
openWorldHint: false,
|
|
61
69
|
},
|
|
62
70
|
description:
|
|
63
|
-
'Get a
|
|
71
|
+
'Get a verified, copy-pasteable Nifra code example for a task - auth route, file upload, ISR page, loader/action, typed client, SSE, deployment, and more. Every snippet is typechecked against the installed Nifra version, so it compiles as-is. Returns code as text for direct use. Call with no query for the grouped index; pass query (e.g. "protected route", "upload", "isr revalidate") for matching snippets.',
|
|
64
72
|
inputSchema: {
|
|
65
73
|
type: "object",
|
|
66
74
|
properties: {
|
|
@@ -84,9 +92,15 @@ export function docsTools(
|
|
|
84
92
|
},
|
|
85
93
|
{
|
|
86
94
|
name: "nifra_types",
|
|
87
|
-
annotations: {
|
|
95
|
+
annotations: {
|
|
96
|
+
title: "Look up nifra API types",
|
|
97
|
+
readOnlyHint: true,
|
|
98
|
+
destructiveHint: false,
|
|
99
|
+
idempotentHint: true,
|
|
100
|
+
openWorldHint: false,
|
|
101
|
+
},
|
|
88
102
|
description:
|
|
89
|
-
'Get the
|
|
103
|
+
'Get the exact TypeScript declaration of any exported @nifrajs/* symbol - interface, type, class, function, or const. Each signature is generated from the package\'s built .d.ts and returns the complete declaration rather than prose. Pass `name` for an exact symbol (e.g. "RateLimitStore", "RouteSchema", "Context", "rateLimit"); pass `query` to search by keyword; omit both for the per-package index of names. A `name` lookup is always the complete declaration; a `query` returns a one-line summary plus the signature, collapsing an oversized body - pass `full: true` to override.',
|
|
90
104
|
inputSchema: {
|
|
91
105
|
type: "object",
|
|
92
106
|
properties: {
|
|
@@ -132,6 +146,8 @@ export function docsTools(
|
|
|
132
146
|
annotations: {
|
|
133
147
|
title: "Guided nifra learning path",
|
|
134
148
|
readOnlyHint: true,
|
|
149
|
+
destructiveHint: false,
|
|
150
|
+
idempotentHint: true,
|
|
135
151
|
openWorldHint: false,
|
|
136
152
|
},
|
|
137
153
|
description:
|
|
@@ -153,10 +169,12 @@ export function docsTools(
|
|
|
153
169
|
annotations: {
|
|
154
170
|
title: "Frontend footgun guidance",
|
|
155
171
|
readOnlyHint: true,
|
|
172
|
+
destructiveHint: false,
|
|
173
|
+
idempotentHint: true,
|
|
156
174
|
openWorldHint: false,
|
|
157
175
|
},
|
|
158
176
|
description:
|
|
159
|
-
'Diagnose a
|
|
177
|
+
'Diagnose a client-side Nifra problem by symptom across every adapter (React, Preact, Solid, Vue, Svelte, and vanilla). Returns the likely cause, a concrete fix, and how to verify it. Covers the Nifra server/client seam, hydration mismatches, duplicated framework runtimes, loader-data typing, and adapter-specific reactivity guidance. Call with no args for the index; pass `symptom` (e.g. "hydration mismatch", "value stopped updating") and/or `adapter` to filter.',
|
|
160
178
|
inputSchema: {
|
|
161
179
|
type: "object",
|
|
162
180
|
properties: {
|