@canonical/pragma-cli 0.38.0 → 0.42.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 +3 -3
- package/dist/package.json +16 -16
- package/dist/pragma.conf.js +273 -176
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
- package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
- package/dist/src/capabilities/capabilities/catalog.js +47 -51
- package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
- package/dist/src/capabilities/capabilities/index.js +2 -0
- package/dist/src/capabilities/capabilities/index.js.map +1 -1
- package/dist/src/capabilities/capabilities/types.js +5 -5
- package/dist/src/capabilities/colophon/colophon.render.js +7 -4
- package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
- package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
- package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
- package/dist/src/capabilities/colophon/markdownTerminal.js.map +1 -1
- package/dist/src/capabilities/config/get.verb.js +2 -0
- package/dist/src/capabilities/config/get.verb.js.map +1 -1
- package/dist/src/capabilities/config/runField.js +2 -2
- package/dist/src/capabilities/config/runField.js.map +1 -1
- package/dist/src/capabilities/config/set.verb.js +2 -0
- package/dist/src/capabilities/config/set.verb.js.map +1 -1
- package/dist/src/capabilities/config/show.render.js.map +1 -1
- package/dist/src/capabilities/config/show.verb.js +1 -0
- package/dist/src/capabilities/config/show.verb.js.map +1 -1
- package/dist/src/capabilities/config/unset.verb.js +2 -0
- package/dist/src/capabilities/config/unset.verb.js.map +1 -1
- package/dist/src/capabilities/create/constants.js +8 -5
- package/dist/src/capabilities/create/constants.js.map +1 -1
- package/dist/src/capabilities/create/create.render.js.map +1 -1
- package/dist/src/capabilities/create/create.verb.js +36 -7
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/create/createSurface.generated.js +22 -0
- package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
- package/dist/src/capabilities/create/index.js +1 -1
- package/dist/src/capabilities/create/pickGenerator.js +1 -0
- package/dist/src/capabilities/create/pickGenerator.js.map +1 -1
- package/dist/src/capabilities/distribution.js +3 -1
- package/dist/src/capabilities/distribution.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
- package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
- package/dist/src/capabilities/graph/connect.verb.js +2 -0
- package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.render.js +7 -1
- package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.verb.js +2 -0
- package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/query.render.js +37 -5
- package/dist/src/capabilities/graph/query.render.js.map +1 -1
- package/dist/src/capabilities/graph/query.verb.js +2 -0
- package/dist/src/capabilities/graph/query.verb.js.map +1 -1
- package/dist/src/capabilities/graph/queryText.js +28 -0
- package/dist/src/capabilities/graph/queryText.js.map +1 -1
- package/dist/src/capabilities/graph/runConnect.js +3 -3
- package/dist/src/capabilities/graph/runConnect.js.map +1 -1
- package/dist/src/capabilities/graph/runQuery.js +8 -6
- package/dist/src/capabilities/graph/runQuery.js.map +1 -1
- package/dist/src/capabilities/info/info.verb.js +2 -0
- package/dist/src/capabilities/info/info.verb.js.map +1 -1
- package/dist/src/capabilities/meta/mcp.verb.js +1 -0
- package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
- package/dist/src/capabilities/ontology/verbs.js +7 -10
- package/dist/src/capabilities/ontology/verbs.js.map +1 -1
- package/dist/src/capabilities/prompt/verbs.js +5 -4
- package/dist/src/capabilities/prompt/verbs.js.map +1 -1
- package/dist/src/capabilities/resources/provider.js +6 -3
- package/dist/src/capabilities/resources/provider.js.map +1 -1
- package/dist/src/capabilities/setup/operations/writability.js.map +1 -1
- package/dist/src/capabilities/setup/plan.render.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +13 -5
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/shared/calls.js +16 -0
- package/dist/src/capabilities/shared/calls.js.map +1 -0
- package/dist/src/capabilities/shared/packageManager.js.map +1 -1
- package/dist/src/capabilities/skill/discover.js.map +1 -1
- package/dist/src/capabilities/skill/render.js +7 -5
- package/dist/src/capabilities/skill/render.js.map +1 -1
- package/dist/src/capabilities/skill/verbs.js +5 -4
- package/dist/src/capabilities/skill/verbs.js.map +1 -1
- package/dist/src/capabilities/sources/collectStatus.js +6 -1
- package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
- package/dist/src/capabilities/sources/index.js +4 -3
- package/dist/src/capabilities/sources/index.js.map +1 -1
- package/dist/src/capabilities/sources/reset.render.js +37 -0
- package/dist/src/capabilities/sources/reset.render.js.map +1 -0
- package/dist/src/capabilities/sources/reset.verb.js +51 -0
- package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
- package/dist/src/capabilities/sources/runReset.js +59 -0
- package/dist/src/capabilities/sources/runReset.js.map +1 -0
- package/dist/src/capabilities/sources/runUpdate.js +7 -6
- package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
- package/dist/src/capabilities/sources/status.render.js +24 -8
- package/dist/src/capabilities/sources/status.render.js.map +1 -1
- package/dist/src/capabilities/sources/status.verb.js +1 -0
- package/dist/src/capabilities/sources/status.verb.js.map +1 -1
- package/dist/src/capabilities/sources/update.verb.js +2 -0
- package/dist/src/capabilities/sources/update.verb.js.map +1 -1
- package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
- package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
- package/dist/src/capabilities/version/version.verb.js +1 -0
- package/dist/src/capabilities/version/version.verb.js.map +1 -1
- package/dist/src/constants.js +4 -3
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/completion/complete.js.map +1 -1
- package/dist/src/kernel/completion/emitScripts.js.map +1 -1
- package/dist/src/kernel/completion/parse.js.map +1 -1
- package/dist/src/kernel/completion/rank.js.map +1 -1
- package/dist/src/kernel/completion/resolve.js.map +1 -1
- package/dist/src/kernel/config/readConfig.js.map +1 -1
- package/dist/src/kernel/error/PragmaError.js.map +1 -1
- package/dist/src/kernel/error/index.js +1 -1
- package/dist/src/kernel/error/index.js.map +1 -1
- package/dist/src/kernel/error/recovery.js +24 -42
- package/dist/src/kernel/error/recovery.js.map +1 -1
- package/dist/src/kernel/packs/collect.js +123 -9
- package/dist/src/kernel/packs/collect.js.map +1 -1
- package/dist/src/kernel/packs/compile.js +74 -15
- package/dist/src/kernel/packs/compile.js.map +1 -1
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
- package/dist/src/kernel/packs/iri.js +2 -4
- package/dist/src/kernel/packs/iri.js.map +1 -1
- package/dist/src/kernel/packs/renderPack.js +90 -50
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +202 -90
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +89 -30
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +66 -12
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
- package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
- package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/packs/storyRules.js +31 -4
- package/dist/src/kernel/packs/storyRules.js.map +1 -1
- package/dist/src/kernel/packs/tierScope.js +3 -3
- package/dist/src/kernel/packs/tierScope.js.map +1 -1
- package/dist/src/kernel/packs/types.js +4 -0
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +4 -0
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +32 -8
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
- package/dist/src/kernel/project/cli/suggestNames.js +44 -1
- package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
- package/dist/src/kernel/project/cli/verbHelp.js +17 -18
- package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
- package/dist/src/kernel/project/mcp/buildServer.js +11 -4
- package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
- package/dist/src/kernel/project/mcp/envelope.js +3 -2
- package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
- package/dist/src/kernel/project/mcp/instructions.js +101 -31
- package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/envelope.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +3 -3
- package/dist/src/kernel/render/renderers.js.map +1 -1
- package/dist/src/kernel/render/turtle.js +21 -4
- package/dist/src/kernel/render/turtle.js.map +1 -1
- package/dist/src/kernel/runtime/boot.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/build.js +54 -1
- package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
- package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +3 -5
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
- package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +8 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/packVersion.js +75 -0
- package/dist/src/kernel/runtime/packVersion.js.map +1 -0
- package/dist/src/kernel/runtime/readEntity.js +43 -4
- package/dist/src/kernel/runtime/readEntity.js.map +1 -1
- package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
- package/dist/src/kernel/runtime/resolveSources.js +61 -9
- package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
- package/dist/src/kernel/runtime/storeReadiness.js +3 -2
- package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
- package/dist/src/kernel/spec/call.js +167 -0
- package/dist/src/kernel/spec/call.js.map +1 -0
- package/dist/src/kernel/spec/emitReference.js +9 -13
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/dist/src/kernel/spec/emitSurface.js +8 -1
- package/dist/src/kernel/spec/emitSurface.js.map +1 -1
- package/dist/src/kernel/spec/guidance.js +63 -0
- package/dist/src/kernel/spec/guidance.js.map +1 -0
- package/dist/src/kernel/spec/wireType.js +36 -0
- package/dist/src/kernel/spec/wireType.js.map +1 -0
- package/package.json +16 -16
- package/pragma.conf.ts +294 -177
- package/src/capabilities/block.parity.test.ts +1 -1
- package/src/capabilities/callRule.test.ts +147 -0
- package/src/capabilities/capabilities/capabilities.test.ts +47 -19
- package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
- package/src/capabilities/capabilities/catalog.ts +60 -55
- package/src/capabilities/capabilities/index.ts +2 -0
- package/src/capabilities/capabilities/types.ts +16 -19
- package/src/capabilities/colophon/colophon.render.ts +9 -5
- package/src/capabilities/colophon/colophon.verb.ts +1 -0
- package/src/capabilities/config/get.verb.ts +2 -0
- package/src/capabilities/config/runField.ts +3 -3
- package/src/capabilities/config/set.verb.ts +3 -0
- package/src/capabilities/config/show.verb.ts +2 -0
- package/src/capabilities/config/unset.verb.ts +2 -0
- package/src/capabilities/create/constants.ts +8 -5
- package/src/capabilities/create/create.mcp.test.ts +173 -1
- package/src/capabilities/create/create.test.ts +4 -2
- package/src/capabilities/create/create.verb.ts +45 -3
- package/src/capabilities/create/createGrammar.test.ts +41 -5
- package/src/capabilities/create/createSurface.generated.ts +22 -0
- package/src/capabilities/create/crossCli.subprocess.test.ts +53 -14
- package/src/capabilities/create/index.ts +1 -1
- package/src/capabilities/create/pickGenerator.ts +1 -0
- package/src/capabilities/create/shippedCreate.subprocess.test.ts +28 -7
- package/src/capabilities/distribution.test.ts +6 -3
- package/src/capabilities/distribution.ts +6 -0
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
- package/src/capabilities/doctor/doctor.test.ts +38 -1
- package/src/capabilities/doctor/doctor.verb.ts +2 -0
- package/src/capabilities/emptyRecovery.test.ts +5 -0
- package/src/capabilities/graph/connect.verb.ts +2 -0
- package/src/capabilities/graph/inspect.render.ts +9 -1
- package/src/capabilities/graph/inspect.test.ts +73 -0
- package/src/capabilities/graph/inspect.verb.ts +2 -0
- package/src/capabilities/graph/query.render.test.ts +35 -0
- package/src/capabilities/graph/query.render.ts +45 -7
- package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
- package/src/capabilities/graph/query.verb.ts +2 -0
- package/src/capabilities/graph/queryText.test.ts +55 -1
- package/src/capabilities/graph/queryText.ts +47 -0
- package/src/capabilities/graph/runConnect.ts +4 -5
- package/src/capabilities/graph/runQuery.ts +23 -9
- package/src/capabilities/info/info.verb.ts +2 -0
- package/src/capabilities/links.shipped.exec.test.ts +398 -0
- package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
- package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
- package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
- package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
- package/src/capabilities/meta/mcp.verb.ts +2 -0
- package/src/capabilities/ontology/ontology.test.ts +24 -0
- package/src/capabilities/ontology/verbs.ts +16 -12
- package/src/capabilities/prompt/verbs.ts +10 -4
- package/src/capabilities/resources/provider.ts +7 -3
- package/src/capabilities/resources/resources.test.ts +2 -1
- package/src/capabilities/setup/setup.verb.ts +16 -0
- package/src/capabilities/shared/calls.ts +20 -0
- package/src/capabilities/skill/render.ts +9 -6
- package/src/capabilities/skill/verbs.ts +8 -4
- package/src/capabilities/sources/collectStatus.ts +6 -1
- package/src/capabilities/sources/index.ts +4 -3
- package/src/capabilities/sources/reset.render.test.ts +33 -0
- package/src/capabilities/sources/reset.render.ts +43 -0
- package/src/capabilities/sources/reset.verb.ts +59 -0
- package/src/capabilities/sources/runReset.ts +65 -0
- package/src/capabilities/sources/runUpdate.ts +12 -11
- package/src/capabilities/sources/sources.test.ts +158 -3
- package/src/capabilities/sources/status.render.test.ts +63 -1
- package/src/capabilities/sources/status.render.ts +25 -8
- package/src/capabilities/sources/status.verb.ts +2 -0
- package/src/capabilities/sources/types.ts +26 -0
- package/src/capabilities/sources/update.verb.ts +3 -0
- package/src/capabilities/surface.test.ts +29 -4
- package/src/capabilities/token.parity.test.ts +3 -2
- package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
- package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
- package/src/capabilities/variable.parity.test.ts +2 -1
- package/src/capabilities/version/version.verb.ts +1 -0
- package/src/capabilities/wireContract.test.ts +110 -0
- package/src/constants.ts +4 -3
- package/src/kernel/completion/shellDrive.test.ts +2 -2
- package/src/kernel/copy.test.ts +1 -1
- package/src/kernel/error/PragmaError.test.ts +7 -3
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +52 -18
- package/src/kernel/error/recovery.ts +25 -46
- package/src/kernel/error/types.ts +1 -1
- package/src/kernel/packs/collect.test.ts +79 -0
- package/src/kernel/packs/collect.ts +151 -11
- package/src/kernel/packs/compile.test.ts +107 -10
- package/src/kernel/packs/compile.ts +100 -11
- package/src/kernel/packs/filteredEmpty.test.ts +22 -5
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
- package/src/kernel/packs/graphqlSource.test.ts +42 -0
- package/src/kernel/packs/iri.ts +5 -4
- package/src/kernel/packs/listNotice.test.ts +1 -1
- package/src/kernel/packs/nounLink.test.ts +385 -0
- package/src/kernel/packs/renderPack.ts +137 -57
- package/src/kernel/packs/resolveEntity.test.ts +170 -1
- package/src/kernel/packs/resolveEntity.ts +281 -101
- package/src/kernel/packs/runBodies.ts +133 -31
- package/src/kernel/packs/schema.ts +77 -12
- package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
- package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
- package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
- package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
- package/src/kernel/packs/sparql/filterValues.ts +50 -31
- package/src/kernel/packs/sparql/runSelect.ts +4 -6
- package/src/kernel/packs/storyRules.ts +37 -4
- package/src/kernel/packs/tierScope.test.ts +120 -6
- package/src/kernel/packs/tierScope.ts +5 -7
- package/src/kernel/packs/toolDescription.test.ts +59 -74
- package/src/kernel/packs/types.ts +93 -11
- package/src/kernel/project/cli/buildProgram.ts +4 -0
- package/src/kernel/project/cli/dispatch.ts +34 -8
- package/src/kernel/project/cli/suggest.test.ts +77 -0
- package/src/kernel/project/cli/suggestNames.ts +47 -1
- package/src/kernel/project/cli/verbHelp.ts +16 -19
- package/src/kernel/project/mcp/buildServer.ts +15 -4
- package/src/kernel/project/mcp/envelope.ts +3 -2
- package/src/kernel/project/mcp/instructions.ts +123 -35
- package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
- package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
- package/src/kernel/project/mcp/registerVerb.ts +67 -29
- package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
- package/src/kernel/render/renderers.test.ts +2 -2
- package/src/kernel/render/renderers.ts +3 -3
- package/src/kernel/render/turtle.ts +24 -4
- package/src/kernel/runtime/graphpack/build.ts +58 -1
- package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
- package/src/kernel/runtime/graphpack/manifest.ts +15 -0
- package/src/kernel/runtime/graphpack/read.ts +4 -6
- package/src/kernel/runtime/graphpack/schemas.ts +1 -0
- package/src/kernel/runtime/graphpack/types.ts +13 -0
- package/src/kernel/runtime/loadSession.ts +8 -1
- package/src/kernel/runtime/packVersion.test.ts +61 -0
- package/src/kernel/runtime/packVersion.ts +84 -0
- package/src/kernel/runtime/readEntity.ts +61 -6
- package/src/kernel/runtime/resolveSources.test.ts +90 -7
- package/src/kernel/runtime/resolveSources.ts +91 -10
- package/src/kernel/runtime/storeReadiness.ts +4 -4
- package/src/kernel/runtime/types.ts +12 -0
- package/src/kernel/spec/call.test.ts +95 -0
- package/src/kernel/spec/call.ts +186 -0
- package/src/kernel/spec/emitReference.ts +9 -13
- package/src/kernel/spec/emitSurface.ts +9 -1
- package/src/kernel/spec/guidance.test.ts +49 -0
- package/src/kernel/spec/guidance.ts +68 -0
- package/src/kernel/spec/index.ts +4 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/types.ts +64 -7
- package/src/kernel/spec/wireType.ts +53 -0
- package/src/testing/behavioral/README.md +1 -1
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
- package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
- package/src/testing/behavioral/parity.test.ts +40 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
- package/src/testing/eval/cases/assembly.ts +4 -3
- package/src/testing/eval/cases/stable.ts +2 -3
- package/src/testing/helpers/callSchema.ts +40 -0
- package/src/testing/helpers/parity.ts +35 -2
- package/src/testing/setupCallChecking.ts +30 -0
- package/dist/src/capabilities/capabilities/hints.js +0 -223
- package/dist/src/capabilities/capabilities/hints.js.map +0 -1
- package/src/capabilities/capabilities/hints.ts +0 -266
|
@@ -34,6 +34,7 @@ import type {
|
|
|
34
34
|
GlobalFlags,
|
|
35
35
|
InteractionRuntime,
|
|
36
36
|
PragmaRuntime,
|
|
37
|
+
StoreSession,
|
|
37
38
|
} from "../../runtime/types.js";
|
|
38
39
|
import { kebabCase, type ParamSpec, type VerbSpec } from "../../spec/index.js";
|
|
39
40
|
import { EXIT, mapExitCode } from "./exitCodes.js";
|
|
@@ -175,9 +176,10 @@ function renderData(
|
|
|
175
176
|
// is distinguishable from an unbuilt store or a mistyped filter, and an
|
|
176
177
|
// ambiguous lookup hit from an unambiguous one. `data` keeps its uniform
|
|
177
178
|
// shape, and MCP builds the same key from the same seam
|
|
178
|
-
// (`mcp/registerVerb.ts#noticeMeta`)
|
|
179
|
-
//
|
|
180
|
-
|
|
179
|
+
// (`mcp/registerVerb.ts#noticeMeta`). The two envelopes are equal EXCEPT
|
|
180
|
+
// for spelling inside this sentence: a next step reads as a command here
|
|
181
|
+
// and as a tool call there, because each is printed where it will be used.
|
|
182
|
+
const notice = verb.output.formatters.notice?.(data, "cli");
|
|
181
183
|
// The machine half of the same seam: facts the read wants to hand an agent
|
|
182
184
|
// as data rather than prose (`meta.scope`). MCP merges the same seam into
|
|
183
185
|
// the same keys (`mcp/registerVerb.ts#readMeta`).
|
|
@@ -204,7 +206,9 @@ function renderData(
|
|
|
204
206
|
const text = verb.output.formatters.plain(data, context);
|
|
205
207
|
// The calm notice is success-path guidance — `--quiet` mutes it.
|
|
206
208
|
const notice =
|
|
207
|
-
flags.quiet === true
|
|
209
|
+
flags.quiet === true
|
|
210
|
+
? undefined
|
|
211
|
+
: verb.output.formatters.notice?.(data, "cli");
|
|
208
212
|
return {
|
|
209
213
|
stdout: text ? `${text}\n` : "",
|
|
210
214
|
...(notice ? { stderr: `${notice}\n` } : {}),
|
|
@@ -320,9 +324,9 @@ export async function executeVerb(
|
|
|
320
324
|
// The lazy-store seam: boot the store (once, memoized) only for verbs that
|
|
321
325
|
// declare they need it. A storeless verb never reaches the store factory, so
|
|
322
326
|
// the storeless guarantee holds by construction (no STORE_SKIP triage).
|
|
323
|
-
|
|
324
|
-
await runtime.store.get()
|
|
325
|
-
|
|
327
|
+
const session = verb.capability.needsStore
|
|
328
|
+
? await runtime.store.get()
|
|
329
|
+
: undefined;
|
|
326
330
|
|
|
327
331
|
if (verb.capability.mutates) {
|
|
328
332
|
// Tell the verb whether this is a plan-only preview (`--dry-run`) or a real
|
|
@@ -468,7 +472,29 @@ export async function executeVerb(
|
|
|
468
472
|
const data = await Promise.resolve(
|
|
469
473
|
verb.run(params, runtime) as Promise<unknown>,
|
|
470
474
|
);
|
|
471
|
-
return renderData(verb, flags, data,
|
|
475
|
+
return renderData(verb, flags, data, storeMeta(session));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* The envelope facts about the STORE this read was answered from, as data.
|
|
480
|
+
*
|
|
481
|
+
* One fact so far: the project's own built pack, when the boot passed over it
|
|
482
|
+
* because an older CLI built it. An agent that reads `block lookup button` and
|
|
483
|
+
* gets the snapshot's answer has no way to learn from the payload that this
|
|
484
|
+
* directory also holds a pack — so the fact rides `meta`, from the session the
|
|
485
|
+
* answer came from, on every read verb at once.
|
|
486
|
+
*
|
|
487
|
+
* `meta` ONLY, deliberately: the read's answer is not wrong (the snapshot is the
|
|
488
|
+
* current graph, which is the whole point of passing the pack over), so the
|
|
489
|
+
* plain and llm renderings of every read stay byte for byte what they were, and
|
|
490
|
+
* the sentence a person reads stays where a person goes for it — `sources
|
|
491
|
+
* status` and `doctor`.
|
|
492
|
+
*
|
|
493
|
+
* @param session - The booted session, or `undefined` for a storeless verb.
|
|
494
|
+
* @returns The base `meta` for the envelope (empty for an ordinary read).
|
|
495
|
+
*/
|
|
496
|
+
function storeMeta(session: StoreSession | undefined): Record<string, unknown> {
|
|
497
|
+
return session?.ignoredPack ? { ignoredPack: session.ignoredPack } : {};
|
|
472
498
|
}
|
|
473
499
|
|
|
474
500
|
/**
|
|
@@ -118,4 +118,81 @@ describe("suggestNames", () => {
|
|
|
118
118
|
"Timeline ",
|
|
119
119
|
]);
|
|
120
120
|
});
|
|
121
|
+
|
|
122
|
+
describe("a candidate that is a whole-segment part of the query", () => {
|
|
123
|
+
it("is offered for a name with a segment too many", () => {
|
|
124
|
+
// Recorded: `token lookup color.text.primary` suggested NOTHING although
|
|
125
|
+
// `color.text` exists — it is no prefix of the query's, and eight extra
|
|
126
|
+
// characters is past any edit-distance threshold.
|
|
127
|
+
expect(
|
|
128
|
+
suggestNames("color.text.primary", ["color.text", "color.border"]),
|
|
129
|
+
).toEqual(["color.text"]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("is offered for the last segment of a pasted local name", () => {
|
|
133
|
+
expect(
|
|
134
|
+
suggestNames("apps_lxd.component.meter", ["Meter", "Metric"]),
|
|
135
|
+
).toEqual(["Meter"]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("ranks first, longest run first, ahead of a candidate the query prefixes", () => {
|
|
139
|
+
expect(
|
|
140
|
+
suggestNames("color.text.muted", [
|
|
141
|
+
"color.text.muted.hover",
|
|
142
|
+
"color",
|
|
143
|
+
"color.text",
|
|
144
|
+
"text.muted",
|
|
145
|
+
]),
|
|
146
|
+
).toEqual([
|
|
147
|
+
"color.text",
|
|
148
|
+
"text.muted",
|
|
149
|
+
"color",
|
|
150
|
+
"color.text.muted.hover",
|
|
151
|
+
]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("cuts at segment boundaries only", () => {
|
|
155
|
+
expect(
|
|
156
|
+
suggestNames("color.text.primary", ["color.te", "lor.text"]),
|
|
157
|
+
).toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("reads a slash-separated path the same way", () => {
|
|
161
|
+
expect(
|
|
162
|
+
suggestNames("react/component/tsdoc/extra", ["react/component/tsdoc"]),
|
|
163
|
+
).toEqual(["react/component/tsdoc"]);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("reads past a prefix, so a prefixed name suggests like its bare form", () => {
|
|
167
|
+
expect(
|
|
168
|
+
suggestNames("dt:color.text.zzz", ["color.text", "color"]),
|
|
169
|
+
).toEqual(["color.text", "color"]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("builds no runs for a query too long to be a name", () => {
|
|
173
|
+
const parts = Array.from({ length: 3000 }, (_, i) => `s${i}`);
|
|
174
|
+
const started = performance.now();
|
|
175
|
+
expect(suggestNames(parts.join("."), ["s1.s2"])).toEqual([]);
|
|
176
|
+
expect(performance.now() - started).toBeLessThan(100);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("keeps the five-result cap", () => {
|
|
180
|
+
const parts = ["a1", "b2", "c3", "d4", "e5", "f6", "g7"];
|
|
181
|
+
expect(suggestNames(parts.join("."), parts)).toHaveLength(5);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("stays cheap against a pool the size of the shipped pack", () => {
|
|
185
|
+
// The length prefilter exists because ranking 4,380 names once cost
|
|
186
|
+
// 1.2 s; this branch is a map lookup per candidate and must not undo
|
|
187
|
+
// it. The pool is names the prefilter rejects on length, so what is
|
|
188
|
+
// timed is the branch and the prefilter, not the edit distances.
|
|
189
|
+
const pool = Array.from(
|
|
190
|
+
{ length: 5000 },
|
|
191
|
+
(_, i) => `color.group${i}.tone.on.a.path.far.longer.than.the.query.is`,
|
|
192
|
+
);
|
|
193
|
+
const started = performance.now();
|
|
194
|
+
expect(suggestNames("apps_lxd.component.meter", pool)).toEqual([]);
|
|
195
|
+
expect(performance.now() - started).toBeLessThan(100);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
121
198
|
});
|
|
@@ -6,6 +6,40 @@
|
|
|
6
6
|
* by the unknown-command suggester to turn a typo into "Did you mean: …?".
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/** How many suggestions are returned unless a caller asks for fewer or more. */
|
|
10
|
+
export const MAX_SUGGESTIONS = 5;
|
|
11
|
+
|
|
12
|
+
/** What separates the segments of a path-shaped name (`:` ends a prefix). */
|
|
13
|
+
const SEGMENT_SEPARATOR = /[./:]/;
|
|
14
|
+
|
|
15
|
+
/** Past this many segments a query is not a name, and its runs are not built. */
|
|
16
|
+
const MAX_RUN_SEGMENTS = 16;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Every contiguous run of whole segments of `query` shorter than the query,
|
|
20
|
+
* mapped to the number of segments it spans; empty for a query of one segment
|
|
21
|
+
* or of more than {@link MAX_RUN_SEGMENTS}.
|
|
22
|
+
*/
|
|
23
|
+
function segmentRuns(query: string): ReadonlyMap<string, number> {
|
|
24
|
+
if (query.split(SEGMENT_SEPARATOR).length > MAX_RUN_SEGMENTS) {
|
|
25
|
+
return new Map();
|
|
26
|
+
}
|
|
27
|
+
const starts = [0];
|
|
28
|
+
for (const [index, char] of [...query].entries()) {
|
|
29
|
+
if (SEGMENT_SEPARATOR.test(char)) starts.push(index + 1);
|
|
30
|
+
}
|
|
31
|
+
const ends = [...starts.slice(1).map((start) => start - 1), query.length];
|
|
32
|
+
const runs = new Map<string, number>();
|
|
33
|
+
for (const [from, start] of starts.entries()) {
|
|
34
|
+
for (const [to, end] of ends.entries()) {
|
|
35
|
+
const run = query.slice(start, end);
|
|
36
|
+
if (to < from || run === "" || run === query) continue;
|
|
37
|
+
runs.set(run, to - from + 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return runs;
|
|
41
|
+
}
|
|
42
|
+
|
|
9
43
|
/**
|
|
10
44
|
* Return up to {@link maxResults} candidates most similar to `query`.
|
|
11
45
|
*
|
|
@@ -19,6 +53,10 @@
|
|
|
19
53
|
* if a candidate really is the query, the miss is the bug and the suggestion
|
|
20
54
|
* would only hide it.
|
|
21
55
|
*
|
|
56
|
+
* Ranking, best first: a candidate that is a run of the query's whole segments
|
|
57
|
+
* (`color.text` for `color.text.primary`), longest first; then a candidate the
|
|
58
|
+
* query is a prefix of; then edit distance.
|
|
59
|
+
*
|
|
22
60
|
* Scoring reads the same trimmed forms, so padding costs a candidate no edit
|
|
23
61
|
* distance either; what is RETURNED is the candidate verbatim, padding and
|
|
24
62
|
* casing intact, because a suggestion names a value the graph holds.
|
|
@@ -34,7 +72,7 @@ export function suggestNames(
|
|
|
34
72
|
candidates: readonly string[],
|
|
35
73
|
opts?: { maxResults?: number; threshold?: number },
|
|
36
74
|
): string[] {
|
|
37
|
-
const maxResults = opts?.maxResults ??
|
|
75
|
+
const maxResults = opts?.maxResults ?? MAX_SUGGESTIONS;
|
|
38
76
|
const threshold = opts?.threshold ?? 0.4;
|
|
39
77
|
const queryLower = query.trim().toLowerCase();
|
|
40
78
|
|
|
@@ -44,12 +82,20 @@ export function suggestNames(
|
|
|
44
82
|
|
|
45
83
|
type Scored = { name: string; score: number };
|
|
46
84
|
const scored: Scored[] = [];
|
|
85
|
+
const runs = segmentRuns(queryLower);
|
|
47
86
|
|
|
48
87
|
for (const candidate of candidates) {
|
|
49
88
|
const candidateLower = candidate.trim().toLowerCase();
|
|
50
89
|
|
|
51
90
|
if (candidateLower === queryLower) continue;
|
|
52
91
|
|
|
92
|
+
// Below zero: every run outranks a prefix match, the longest run first.
|
|
93
|
+
const spanned = runs.get(candidateLower);
|
|
94
|
+
if (spanned !== undefined) {
|
|
95
|
+
scored.push({ name: candidate, score: -spanned });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
53
99
|
if (candidateLower.startsWith(queryLower)) {
|
|
54
100
|
scored.push({ name: candidate, score: 0 });
|
|
55
101
|
continue;
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* `formatVerbHelp`, retargeted at the grammar.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import { renderCall } from "../../spec/call.js";
|
|
12
|
+
import { exampleCall, useWhenSentence } from "../../spec/guidance.js";
|
|
13
|
+
import type { Example, ParamSpec, VerbSpec } from "../../spec/index.js";
|
|
12
14
|
import { kebabCase } from "../../spec/index.js";
|
|
13
15
|
import { MUTATION_FLAG_DOCS, negationFlagDoc } from "./constants.js";
|
|
14
16
|
import {
|
|
@@ -19,18 +21,6 @@ import {
|
|
|
19
21
|
helpUsage,
|
|
20
22
|
} from "./helpFormat.js";
|
|
21
23
|
|
|
22
|
-
/**
|
|
23
|
-
* A verb's `doc` doubles as its MCP tool description, so a pack's authored
|
|
24
|
-
* `toolDescription` may end with an MCP tool-call example — `Example:
|
|
25
|
-
* token_lookup { names: ["…"] }`. That `noun_verb {…}` syntax is MCP-transport
|
|
26
|
-
* shape, meaningless on the CLI, so the CLI projector drops the trailing example
|
|
27
|
-
* sentence; the MCP projector keeps the authored text whole. Hand-written docs
|
|
28
|
-
* carry no such example and pass through untouched.
|
|
29
|
-
*/
|
|
30
|
-
function stripToolCallExample(doc: string): string {
|
|
31
|
-
return doc.replace(/\s*Example:\s+[a-z][a-z0-9_]*\s*\{[^{}]*\}\.?\s*$/, "");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
/** The positional usage token for a param (`<name>` / `[name]`, `...` variadic). */
|
|
35
25
|
function positionalToken(param: ParamSpec): string {
|
|
36
26
|
const variadic = param.kind === "string[]" ? "..." : "";
|
|
@@ -69,10 +59,9 @@ export function formatVerbHelp(programName: string, verb: VerbSpec): string {
|
|
|
69
59
|
verb.summary,
|
|
70
60
|
];
|
|
71
61
|
|
|
72
|
-
if (verb.doc)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
62
|
+
if (verb.doc) lines.push("", verb.doc);
|
|
63
|
+
const question = useWhenSentence(verb);
|
|
64
|
+
if (question) lines.push("", question);
|
|
76
65
|
|
|
77
66
|
// Every flag the command PARSES is rendered, from the same spec facts
|
|
78
67
|
// registration reads: the declared params, each default-true boolean's
|
|
@@ -97,9 +86,17 @@ export function formatVerbHelp(programName: string, verb: VerbSpec): string {
|
|
|
97
86
|
lines.push("", helpHeading("Flags"), ...helpColumns(flagRows));
|
|
98
87
|
}
|
|
99
88
|
|
|
100
|
-
|
|
89
|
+
// The verb's declared example leads, spelled as a command by the same
|
|
90
|
+
// renderer that spells it as a tool call in the MCP description.
|
|
91
|
+
const declaredExample = exampleCall(verb);
|
|
92
|
+
const generated = declaredExample && renderCall(declaredExample, "cli");
|
|
93
|
+
const examples = [
|
|
94
|
+
...(generated ? [{ cmd: generated } as Example] : []),
|
|
95
|
+
...(verb.examples ?? []).filter((example) => example.cmd !== generated),
|
|
96
|
+
];
|
|
97
|
+
if (examples.length > 0) {
|
|
101
98
|
lines.push("", helpHeading("Examples"));
|
|
102
|
-
for (const example of
|
|
99
|
+
for (const example of examples) {
|
|
103
100
|
lines.push(` ${helpTerm(example.cmd)}`);
|
|
104
101
|
if (example.note) lines.push(` ${helpDim(example.note)}`);
|
|
105
102
|
}
|
|
@@ -18,8 +18,9 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
18
18
|
import { MCP_SERVER_NAME, VERSION } from "../../../constants.js";
|
|
19
19
|
import type { GlobalFlags } from "../../runtime/index.js";
|
|
20
20
|
import { bootRuntime } from "../../runtime/index.js";
|
|
21
|
+
import { declareVerbs } from "../../spec/call.js";
|
|
21
22
|
import type { CapabilityModule } from "../../spec/index.js";
|
|
22
|
-
import { buildInstructions } from "./instructions.js";
|
|
23
|
+
import { buildInstructions, INSTRUCTIONS_MAX_CHARS } from "./instructions.js";
|
|
23
24
|
import { registerVerb } from "./registerVerb.js";
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -53,11 +54,21 @@ export async function buildServer(
|
|
|
53
54
|
): Promise<McpServer> {
|
|
54
55
|
const server = new McpServer(
|
|
55
56
|
{ name: MCP_SERVER_NAME, version: VERSION },
|
|
56
|
-
// Handshake orientation (sent once at initialize)
|
|
57
|
-
// conventions
|
|
58
|
-
|
|
57
|
+
// Handshake orientation (sent once at initialize): the distribution's
|
|
58
|
+
// conventions plus a question → tool index generated from the registry. It
|
|
59
|
+
// is FITTED to the length clients keep; when a project's packs push it over,
|
|
60
|
+
// the dropped index lines are noted on stderr (stdout is the JSON-RPC frame).
|
|
61
|
+
{
|
|
62
|
+
instructions: buildInstructions(modules, (dropped) =>
|
|
63
|
+
process.stderr.write(
|
|
64
|
+
`MCP instructions: ${dropped} index line(s) did not fit ${INSTRUCTIONS_MAX_CHARS} characters and were left to the capabilities catalogue.\n`,
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
},
|
|
59
68
|
);
|
|
60
69
|
const runtime = bootRuntime(MCP_FLAGS, cwd);
|
|
70
|
+
// Tells the call renderer which verbs exist (see `spec/call.ts`).
|
|
71
|
+
declareVerbs(modules.flatMap((module) => module.verbs));
|
|
61
72
|
|
|
62
73
|
for (const module of modules) {
|
|
63
74
|
for (const verb of module.verbs) {
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* The MCP projector returns the *same* `{ ok, data, meta }` / `{ ok, error }`
|
|
5
5
|
* envelope the CLI emits under `--format json` — built by the shared
|
|
6
6
|
* `successEnvelope` / `errorEnvelope` — serialized into a single text content
|
|
7
|
-
* block (D4). Reusing the shared builders is what
|
|
8
|
-
*
|
|
7
|
+
* block (D4). Reusing the shared builders is what keeps CLI-JSON and MCP output
|
|
8
|
+
* the same envelope — equal in everything but the spelling of a next step
|
|
9
|
+
* inside `meta.notice`, which follows the surface it is printed on.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -2,52 +2,140 @@
|
|
|
2
2
|
* The MCP server `instructions` string — orientation sent ONCE in the
|
|
3
3
|
* initialize handshake (not per tool call), so agents arrive oriented.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
5
|
+
* A client may defer tools: the agent then sees only tool NAMES and this text
|
|
6
|
+
* until it searches for a tool by name, so a description it never loads cannot
|
|
7
|
+
* steer it. The instructions therefore carry a generated "question → tool"
|
|
8
|
+
* index, built from the SAME `useWhen` each verb declares for its description:
|
|
9
|
+
*
|
|
10
|
+
* - the distribution's conventions, handed over as data (`mcpOrientation` —
|
|
11
|
+
* they name its tiers and nouns, so the kernel writes none of them);
|
|
12
|
+
* - one line per read tool that is not part of a noun's list/lookup/sample
|
|
13
|
+
* trio, `tool — useWhen`;
|
|
14
|
+
* - the trio explained ONCE, with the nouns that have each verb enumerated;
|
|
15
|
+
* - the writes on one shared, plan-first line.
|
|
16
|
+
*
|
|
17
|
+
* Everything listed is derived from the registry: a new verb adds itself. Live
|
|
18
|
+
* numbers (tier/channel/entity counts) are deliberately absent — they would
|
|
19
|
+
* need a store boot at handshake.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { CapabilityModule, VerbSpec } from "../../spec/index.js";
|
|
23
|
+
import { emitSurface, toolName } from "../../spec/index.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* HARD ceiling on the instructions length, unlike the catalogue budget: clients
|
|
27
|
+
* cap server instructions at about 2 KB and cut the rest, so text past this is
|
|
28
|
+
* text no agent reads. Fit by tightening sentences, never by raising it.
|
|
14
29
|
*/
|
|
30
|
+
export const INSTRUCTIONS_MAX_CHARS = 2000;
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
CONVENTIONS,
|
|
19
|
-
} from "../../../capabilities/capabilities/catalog.js";
|
|
20
|
-
import type { CapabilityModule } from "../../spec/index.js";
|
|
21
|
-
import { emitSurface } from "../../spec/index.js";
|
|
32
|
+
/** The verbs every read story compiles to; explained once rather than per noun. */
|
|
33
|
+
const TRIO = ["list", "lookup", "sample"] as const;
|
|
22
34
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
35
|
+
/** How most `useWhen` clauses open; the index says it once. */
|
|
36
|
+
const ASKED = /^when asked /;
|
|
37
|
+
|
|
38
|
+
/** Which of the trio a verb is, or nothing for a verb that stands alone. */
|
|
39
|
+
function trioVerbOf(verb: VerbSpec): (typeof TRIO)[number] | undefined {
|
|
40
|
+
return TRIO.find((name) => verb.path[1] === name);
|
|
41
|
+
}
|
|
25
42
|
|
|
26
43
|
/**
|
|
27
44
|
* Build the handshake orientation string from the live capability modules.
|
|
28
45
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
46
|
+
* The registry is not fixed — a project's packs add verbs at runtime — so the
|
|
47
|
+
* text is FITTED, not merely measured: everything that is one line whatever the
|
|
48
|
+
* registry holds comes first, the index comes last, and index lines are dropped
|
|
49
|
+
* from the end until the whole fits, the last line saying how many were dropped
|
|
50
|
+
* and where the full list is. A client would otherwise cut the tail silently.
|
|
51
|
+
*
|
|
52
|
+
* @param modules - The capability modules; the one declaring `mcpOrientation`
|
|
53
|
+
* supplies the conventions, the registry everything else.
|
|
54
|
+
* @param onTruncate - Told how many index lines did not fit, when any did not.
|
|
55
|
+
* @returns The orientation (≤ {@link INSTRUCTIONS_MAX_CHARS}); empty when no
|
|
56
|
+
* module declares one.
|
|
31
57
|
*/
|
|
32
58
|
export function buildInstructions(
|
|
33
59
|
modules: readonly CapabilityModule[],
|
|
60
|
+
onTruncate?: (dropped: number) => void,
|
|
34
61
|
): string {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
62
|
+
const orientation = modules.find(
|
|
63
|
+
(module) => module.mcpOrientation,
|
|
64
|
+
)?.mcpOrientation;
|
|
65
|
+
if (!orientation) return "";
|
|
66
|
+
const tools = modules
|
|
67
|
+
.flatMap((module) => module.verbs)
|
|
68
|
+
.filter((verb) => !verb.hidden && verb.capability.mcp.expose);
|
|
69
|
+
const reads = tools.filter((verb) => !verb.capability.mutates);
|
|
70
|
+
|
|
71
|
+
// The trio, once: every noun that has any of the three, marked for what it
|
|
72
|
+
// has beyond `list` — a noun is never promised a verb it lacks.
|
|
73
|
+
const has = (noun: string, name: (typeof TRIO)[number]): boolean =>
|
|
74
|
+
reads.some((verb) => verb.path[0] === noun && trioVerbOf(verb) === name);
|
|
75
|
+
const nouns = [
|
|
76
|
+
...new Set(reads.filter(trioVerbOf).map((verb) => verb.path[0])),
|
|
77
|
+
].map(
|
|
78
|
+
(noun) =>
|
|
79
|
+
`${noun}${has(noun, "lookup") ? "" : "†"}${has(noun, "sample") ? "*" : ""}`,
|
|
80
|
+
);
|
|
81
|
+
const writes = tools
|
|
82
|
+
.filter((verb) => verb.capability.mutates)
|
|
83
|
+
.map((verb) => toolName(verb.path))
|
|
84
|
+
.join(", ");
|
|
85
|
+
// The resource templates come from the emitted surface: the scheme is
|
|
86
|
+
// declared by the module that serves it and frozen in the covenant.
|
|
87
|
+
const templates = emitSurface(modules)
|
|
88
|
+
.mcpSurface.resources.map((template) => `\`${template}\``)
|
|
89
|
+
.join(", ");
|
|
90
|
+
const fixed = [
|
|
91
|
+
orientation.conventions.join(" "),
|
|
47
92
|
"",
|
|
48
|
-
|
|
49
|
-
|
|
93
|
+
`Per noun, <noun>_list finds entries and <noun>_lookup reads them by name († has no lookup); * also has <noun>_sample, which shows real data shapes: ${nouns.join(", ")}.`,
|
|
94
|
+
`Writes are plan-first — the first call returns a plan; repeat it with confirm: true to apply: ${writes}.`,
|
|
95
|
+
`Entity detail: read ${templates} resources.`,
|
|
50
96
|
"",
|
|
51
|
-
|
|
52
|
-
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// The index. Verbs that read the store come first — they are the ones that
|
|
100
|
+
// answer questions about the data — so what an overflow drops is the
|
|
101
|
+
// administrative tail. A verb declaring no `useWhen` has no line to give.
|
|
102
|
+
const standalone = reads
|
|
103
|
+
.filter((verb) => !trioVerbOf(verb) && verb.useWhen)
|
|
104
|
+
.sort(
|
|
105
|
+
(a, b) =>
|
|
106
|
+
Number(b.capability.needsStore) - Number(a.capability.needsStore),
|
|
107
|
+
);
|
|
108
|
+
// Most clauses open "when asked …"; saying that once, as a heading, is what
|
|
109
|
+
// lets every question fit under the ceiling.
|
|
110
|
+
const lineFor = (verb: VerbSpec): string =>
|
|
111
|
+
`${toolName(verb.path)} — ${verb.useWhen?.replace(ASKED, "")}`;
|
|
112
|
+
const group = (heading: string, verbs: readonly VerbSpec[]): string[] =>
|
|
113
|
+
verbs.length > 0 ? [heading, ...verbs.map(lineFor)] : [];
|
|
114
|
+
const index = [
|
|
115
|
+
...group(
|
|
116
|
+
"When asked:",
|
|
117
|
+
standalone.filter((verb) => ASKED.test(verb.useWhen ?? "")),
|
|
118
|
+
),
|
|
119
|
+
...group(
|
|
120
|
+
"Also:",
|
|
121
|
+
standalone.filter((verb) => !ASKED.test(verb.useWhen ?? "")),
|
|
122
|
+
),
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// Where the full list lives: the distribution's own orientation tool.
|
|
126
|
+
const catalogue = tools.find((verb) => verb.category === "orientation");
|
|
127
|
+
const render = (kept: number): string => {
|
|
128
|
+
const dropped = index.length - kept;
|
|
129
|
+
const more =
|
|
130
|
+
dropped === 0
|
|
131
|
+
? []
|
|
132
|
+
: [
|
|
133
|
+
`… and ${dropped} more${catalogue ? `: call ${toolName(catalogue.path)}` : ""}.`,
|
|
134
|
+
];
|
|
135
|
+
return [...fixed, ...index.slice(0, kept), ...more].join("\n");
|
|
136
|
+
};
|
|
137
|
+
let kept = index.length;
|
|
138
|
+
while (kept > 0 && render(kept).length > INSTRUCTIONS_MAX_CHARS) kept--;
|
|
139
|
+
if (kept < index.length) onTruncate?.(index.length - kept);
|
|
140
|
+
return render(kept);
|
|
53
141
|
}
|