@chemx/starter-kit 26.10.8-344 → 26.10.10-75
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/AGENTS.md +135 -32
- package/README.md +253 -199
- package/blueprints/atoms/a-button.svelte +11 -9
- package/blueprints/atoms/a-button.vue +2 -1
- package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.svelte +8 -4
- package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.tsx +8 -4
- package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.vue +8 -4
- package/blueprints/molecule-capsule/m-sample-card.svelte +2 -1
- package/blueprints/molecule-capsule/m-sample-card.tsx +2 -1
- package/blueprints/molecule-capsule/m-sample-card.vue +4 -2
- package/blueprints/workflows/chemx-audit.yml +15 -46
- package/cli/agent-json.js +40 -0
- package/cli/apply-edits.js +195 -0
- package/cli/audit/ai-slop-detector.js +62 -35
- package/cli/audit/ast-passes.js +52 -0
- package/cli/audit/ast-visitors.js +142 -83
- package/cli/audit/audit-plain.js +26 -0
- package/cli/audit/audit-summary.js +16 -3
- package/cli/audit/autofix-content.js +122 -0
- package/cli/audit/autofix.js +74 -159
- package/cli/audit/budget-rules.js +84 -0
- package/cli/audit/catch-predicates.js +51 -0
- package/cli/audit/chemx-dir.js +76 -0
- package/cli/audit/clone-detector.js +4 -2
- package/cli/audit/comment-regions.js +83 -0
- package/cli/audit/coverage.js +48 -0
- package/cli/audit/csharp-analyzer.js +47 -22
- package/cli/audit/discussion-store.js +9 -4
- package/cli/audit/extended-text-patterns.js +104 -54
- package/cli/audit/extended-visitors.js +26 -12
- package/cli/audit/feed.d.ts +77 -0
- package/cli/audit/feed.js +146 -0
- package/cli/audit/gate-delta.js +91 -0
- package/cli/audit/gate-verdict.js +25 -3
- package/cli/audit/history.d.ts +14 -2
- package/cli/audit/history.js +89 -101
- package/cli/audit/hook-shape-validator.js +18 -9
- package/cli/audit/lexicon-predicates.js +125 -0
- package/cli/audit/lifecycle-predicates.js +106 -0
- package/cli/audit/line-budgets.js +154 -0
- package/cli/audit/metrics.js +56 -37
- package/cli/audit/mutable-ref-predicate.js +54 -0
- package/cli/audit/parseable-code.js +11 -0
- package/cli/audit/pattern-detector.js +56 -28
- package/cli/audit/project-figures.js +28 -0
- package/cli/audit/prompt-rule-lines.js +46 -0
- package/cli/audit/prompts.js +74 -46
- package/cli/audit/prop-surface.js +74 -0
- package/cli/audit/ratchet.js +112 -23
- package/cli/audit/render-depth.js +79 -0
- package/cli/audit/reporter-banner.js +7 -4
- package/cli/audit/reporter-grades.js +52 -34
- package/cli/audit/reporter-grouping.js +10 -5
- package/cli/audit/reporter-markdown.js +16 -8
- package/cli/audit/reporter-sections.js +31 -16
- package/cli/audit/reporter-summary.js +28 -16
- package/cli/audit/reporter-utils.js +54 -30
- package/cli/audit/roadmap.js +20 -15
- package/cli/audit/rule-overrides.js +78 -0
- package/cli/audit/rule-pillars.js +43 -0
- package/cli/audit/rule-revisions.js +42 -0
- package/cli/audit/rules-helpers.js +13 -63
- package/cli/audit/rules-predicates.d.ts +1 -0
- package/cli/audit/rules-predicates.js +84 -64
- package/cli/audit/rules-registry.js +94 -10
- package/cli/audit/rules.js +92 -171
- package/cli/audit/shallow-catch-csharp.js +49 -0
- package/cli/audit/shallow-catch-escalation.js +157 -0
- package/cli/audit/shallow-catch-reads.js +116 -0
- package/cli/audit/social-gh.js +18 -9
- package/cli/audit/social-git.js +82 -40
- package/cli/audit/social-http.js +17 -9
- package/cli/audit/social-publisher.js +14 -7
- package/cli/audit/social.js +39 -30
- package/cli/audit/staged-delta.js +67 -0
- package/cli/audit/status-file.js +67 -0
- package/cli/audit/structural-weight-evaluator.js +17 -29
- package/cli/audit/structural-weight.js +30 -13
- package/cli/audit/suppressions.js +130 -0
- package/cli/audit/template-rules.js +82 -0
- package/cli/audit/types.d.ts +24 -1
- package/cli/audit-engine.js +128 -0
- package/cli/audit-preflight-git.js +15 -1
- package/cli/audit-preflight.js +6 -4
- package/cli/audit-scan.js +180 -0
- package/cli/audit-scope.js +31 -14
- package/cli/audit.js +6 -201
- package/cli/babel-lazy.js +41 -0
- package/cli/badge.js +9 -6
- package/cli/banner.js +37 -0
- package/cli/build/child-registry.js +57 -0
- package/cli/build/executor.js +122 -48
- package/cli/build.js +75 -25
- package/cli/cli-args.js +107 -0
- package/cli/columnar.js +6 -3
- package/cli/commands/cmd-audit-each.js +164 -0
- package/cli/commands/cmd-audit-feed.js +78 -0
- package/cli/commands/cmd-audit.js +136 -57
- package/cli/commands/cmd-batch.js +60 -0
- package/cli/commands/cmd-check.js +74 -0
- package/cli/commands/cmd-project.js +18 -9
- package/cli/commands/cmd-router.js +148 -32
- package/cli/commands/cmd-show.js +56 -0
- package/cli/commands/cmd-staged-delta.js +27 -0
- package/cli/commands/cmd-wrappers-files.js +77 -0
- package/cli/commands/cmd-wrappers-git.js +122 -0
- package/cli/commands/cmd-wrappers-json.js +101 -0
- package/cli/commands/cmd-wrappers.js +7 -322
- package/cli/commands/unknown-flags.js +228 -0
- package/cli/commands-schema-badge.js +73 -0
- package/cli/commands-schema-commit.js +21 -0
- package/cli/commands-schema-edit.js +106 -0
- package/cli/commands-schema-host.js +65 -0
- package/cli/commands-schema-ops.js +127 -0
- package/cli/commands-schema-patterns.js +69 -0
- package/cli/commands-schema-report.js +18 -0
- package/cli/commands-schema-search.js +121 -0
- package/cli/commands-schema-setup.js +30 -0
- package/cli/commands-schema-verify.js +89 -0
- package/cli/commands-schema-wrappers.js +131 -0
- package/cli/commands-schema.js +26 -408
- package/cli/comment-ranges.js +108 -0
- package/cli/commit/commit-args.js +73 -0
- package/cli/commit/commit-cli.js +25 -0
- package/cli/commit/commit-facts.js +49 -0
- package/cli/commit/commit-git.js +98 -0
- package/cli/commit/commit-index.js +35 -0
- package/cli/commit/commit-message.js +52 -0
- package/cli/commit/commit-record.js +68 -0
- package/cli/commit/commit-run.js +160 -0
- package/cli/commit/commit-validate.js +62 -0
- package/cli/config/index.js +54 -8
- package/cli/config/loader.js +77 -8
- package/cli/config/profiles.js +7 -0
- package/cli/conflicts-cli.js +118 -0
- package/cli/conflicts.js +104 -0
- package/cli/create.js +11 -6
- package/cli/db-project-stamp.js +124 -0
- package/cli/docs-check/command-tree.js +27 -0
- package/cli/docs-check/extract.js +151 -0
- package/cli/docs-check/resolve.js +85 -0
- package/cli/docs-check/run.js +88 -0
- package/cli/doctor/check-env.js +43 -0
- package/cli/doctor/check-host.js +115 -0
- package/cli/doctor/check-index.js +93 -0
- package/cli/doctor/check-mcp.js +78 -0
- package/cli/doctor/doctor-cli.js +60 -0
- package/cli/doctor/kit-locate.js +40 -0
- package/cli/doctor/proc-scan.js +66 -0
- package/cli/edit-atomic.js +75 -0
- package/cli/edit-diff.js +98 -0
- package/cli/edit-guardrails.js +77 -0
- package/cli/edit-locks.js +128 -0
- package/cli/embeddings/vectorizer.js +9 -5
- package/cli/errors/catcher.js +45 -52
- package/cli/errors/formatter.js +14 -10
- package/cli/errors/policy.js +26 -0
- package/cli/errors/publisher.js +5 -1
- package/cli/errors/report-output.js +67 -0
- package/cli/errors/sanitizer.js +52 -3
- package/cli/errors/storage.js +17 -5
- package/cli/explode-importers.js +102 -0
- package/cli/explode-path-args.js +115 -0
- package/cli/explode-plan.js +153 -0
- package/cli/explode-prologue.js +43 -0
- package/cli/explode-relocate.js +73 -0
- package/cli/exploder.js +205 -193
- package/cli/forge/anchors.js +50 -0
- package/cli/forge/audit-forge.js +58 -0
- package/cli/forge/binding-walk.js +84 -0
- package/cli/forge/bindings.js +238 -0
- package/cli/forge/blueprint-cli.js +153 -0
- package/cli/forge/blueprint-context.js +69 -0
- package/cli/forge/blueprint-members.js +82 -0
- package/cli/forge/blueprint-store.js +106 -0
- package/cli/forge/blueprint-target.js +44 -0
- package/cli/forge/blueprint.js +276 -0
- package/cli/forge/body-end-cache.js +34 -0
- package/cli/forge/body-ends.js +86 -0
- package/cli/forge/canon-inline.js +342 -0
- package/cli/forge/canon-logic.js +106 -0
- package/cli/forge/canon-nodes.js +270 -0
- package/cli/forge/canon-print.js +120 -0
- package/cli/forge/canonicalize.js +49 -0
- package/cli/forge/conventions.js +51 -0
- package/cli/forge/drift.js +120 -0
- package/cli/forge/exclusions.js +21 -0
- package/cli/forge/exits.js +76 -0
- package/cli/forge/facets.js +86 -0
- package/cli/forge/file-units.js +53 -0
- package/cli/forge/fingerprint-file.js +52 -0
- package/cli/forge/fingerprint-session.js +215 -0
- package/cli/forge/fingerprint-sync.js +74 -0
- package/cli/forge/fingerprint-visitors.js +66 -0
- package/cli/forge/fixtures/heal/a7-dry-run.diff.txt +179 -0
- package/cli/forge/fixtures/heal/cli/commit/commit-facts.js.txt +49 -0
- package/cli/forge/fixtures/heal/cli/doctor/check-host.js.txt +117 -0
- package/cli/forge/fixtures/heal/cli/doctor/kit-locate.js.txt +42 -0
- package/cli/forge/fixtures/heal/cli/hooks/guard-paths.js.txt +80 -0
- package/cli/forge/fixtures/heal/cli/hooks/project-status.js.txt +62 -0
- package/cli/forge/fixtures/heal/cli/main.js.txt +169 -0
- package/cli/forge/fixtures/heal/cli/mcp/context.js.txt +81 -0
- package/cli/forge/fixtures/heal/cli/mcp/installer-report.js.txt +47 -0
- package/cli/forge/fixtures/heal/cli/path-scope.js.txt +169 -0
- package/cli/forge/fixtures/heal/cli/project-detector.js.txt +364 -0
- package/cli/forge/fixtures/heal/cli/project-markers.js.txt +42 -0
- package/cli/forge/fixtures/heal/cli/team/coordination-root.js.txt +161 -0
- package/cli/forge/fixtures/heal/cli/team/usage-reader.js.txt +109 -0
- package/cli/forge/fixtures/heal/cli/test-slots.js.txt +129 -0
- package/cli/forge/fixtures/social-gh.js.txt +131 -0
- package/cli/forge/fixtures/team-flags.js.txt +214 -0
- package/cli/forge/fold.js +199 -0
- package/cli/forge/forge-groups.js +200 -0
- package/cli/forge/forge-report.js +73 -0
- package/cli/forge/fuzz/binding-hook.js +36 -0
- package/cli/forge/fuzz/choices.js +40 -0
- package/cli/forge/fuzz/classes.js +41 -0
- package/cli/forge/fuzz/coverage.js +26 -0
- package/cli/forge/fuzz/gen-alias.js +61 -0
- package/cli/forge/fuzz/gen-control.js +73 -0
- package/cli/forge/fuzz/gen-kit.js +75 -0
- package/cli/forge/fuzz/gen-logic.js +77 -0
- package/cli/forge/fuzz/gen-mixed.js +204 -0
- package/cli/forge/fuzz/gen-modules.js +80 -0
- package/cli/forge/fuzz/gen-objects.js +92 -0
- package/cli/forge/fuzz/gen-scope.js +131 -0
- package/cli/forge/fuzz/gen-templates.js +87 -0
- package/cli/forge/fuzz/harness.js +314 -0
- package/cli/forge/fuzz/pair-check.js +115 -0
- package/cli/forge/fuzz/resolve.js +32 -0
- package/cli/forge/fuzz/run.js +121 -0
- package/cli/forge/fuzz/sandbox.js +108 -0
- package/cli/forge/fuzz/seeds.js +119 -0
- package/cli/forge/fuzz/slow-shard.js +60 -0
- package/cli/forge/fuzz/template-oracle.js +148 -0
- package/cli/forge/fuzz/transpile.js +61 -0
- package/cli/forge/gates.js +115 -0
- package/cli/forge/group-shape.js +137 -0
- package/cli/forge/group-store.js +160 -0
- package/cli/forge/group.js +280 -0
- package/cli/forge/gt-sandbox.js +83 -0
- package/cli/forge/gt-scaffold.js +110 -0
- package/cli/forge/hash-labels.js +117 -0
- package/cli/forge/hash.js +119 -0
- package/cli/forge/heal-apply.js +170 -0
- package/cli/forge/heal-cli.js +132 -0
- package/cli/forge/heal-leases.js +52 -0
- package/cli/forge/heal-locate.js +70 -0
- package/cli/forge/heal-match.js +208 -0
- package/cli/forge/heal-ops.js +197 -0
- package/cli/forge/heal-plan.js +143 -0
- package/cli/forge/heal-sandbox.js +126 -0
- package/cli/forge/heal-specs.js +80 -0
- package/cli/forge/heal-store.js +80 -0
- package/cli/forge/heal-text.js +60 -0
- package/cli/forge/heal-typecheck.js +108 -0
- package/cli/forge/heal-undo.js +74 -0
- package/cli/forge/heal-verify.js +148 -0
- package/cli/forge/hole-kinds.js +126 -0
- package/cli/forge/inline-mode.js +6 -0
- package/cli/forge/inline-on.setup.js +4 -0
- package/cli/forge/lgg-stage.js +125 -0
- package/cli/forge/lgg.js +197 -0
- package/cli/forge/library-match.js +74 -0
- package/cli/forge/murmur.js +86 -0
- package/cli/forge/naming.js +101 -0
- package/cli/forge/pattern-schema-ddl.js +157 -0
- package/cli/forge/placement.js +165 -0
- package/cli/forge/rank.js +174 -0
- package/cli/forge/refine.js +93 -0
- package/cli/forge/rejected-members.js +92 -0
- package/cli/forge/rejects.js +77 -0
- package/cli/forge/root-shapes.js +84 -0
- package/cli/forge/run-cache.js +145 -0
- package/cli/forge/sibling-blocks.js +41 -0
- package/cli/forge/siblings.js +140 -0
- package/cli/forge/store.js +180 -0
- package/cli/forge/template-normalize.js +209 -0
- package/cli/forge/template-roles.js +86 -0
- package/cli/forge/template-units.js +148 -0
- package/cli/forge/templates.js +42 -0
- package/cli/forge/unify-step.js +65 -0
- package/cli/forge/unit-floor.js +74 -0
- package/cli/forge/unit-hash.js +229 -0
- package/cli/forge/unit-trees.js +153 -0
- package/cli/forge/units.js +261 -0
- package/cli/forge/windows.js +129 -0
- package/cli/friction/friction-cli.js +71 -0
- package/cli/friction/friction-export.js +54 -0
- package/cli/friction/friction-log.js +54 -0
- package/cli/friction/usage-classify.js +56 -0
- package/cli/friction/usage-report.js +86 -0
- package/cli/friction/wrong-call.js +12 -0
- package/cli/fs-json.js +17 -0
- package/cli/generator-compact.js +55 -0
- package/cli/generator-help.js +16 -15
- package/cli/generator-indexer.js +2 -1
- package/cli/generator-jig-cli.js +11 -7
- package/cli/generator-jig.js +38 -33
- package/cli/generator-templates/archetypes/index.js +30 -13
- package/cli/generator-templates/archetypes/vector-matcher.js +8 -4
- package/cli/generator-templates/compact.js +4 -2
- package/cli/generator-templates/controller-converters.js +23 -14
- package/cli/generator-templates/controller.js +10 -5
- package/cli/generator-templates/hooks.js +4 -2
- package/cli/generator-templates/naming.js +2 -1
- package/cli/generator-templates/react.js +2 -1
- package/cli/generator-templates/specs.js +4 -2
- package/cli/generator-templates/svelte.js +4 -2
- package/cli/generator-templates/types.js +6 -3
- package/cli/generator-templates/views.js +4 -2
- package/cli/generator-templates/vue.js +4 -2
- package/cli/generator-writes.js +24 -0
- package/cli/generator.d.ts +1 -1
- package/cli/generator.js +86 -127
- package/cli/git-file-listing.js +69 -0
- package/cli/help-args.js +18 -0
- package/cli/help.d.ts +7 -3
- package/cli/help.js +94 -149
- package/cli/hooks/bypass-log.js +44 -0
- package/cli/hooks/claude-post-edit.js +79 -0
- package/cli/hooks/claude-pre-tool.js +178 -0
- package/cli/hooks/claude-settings-merge.js +101 -0
- package/cli/hooks/dispatch-identity.js +125 -0
- package/cli/hooks/entry.js +181 -0
- package/cli/hooks/foreign-identity.js +60 -0
- package/cli/hooks/guard-args.js +55 -0
- package/cli/hooks/guard-cases.js +94 -0
- package/cli/hooks/guard-config.js +43 -0
- package/cli/hooks/guard-invocation.js +106 -0
- package/cli/hooks/guard-paths.js +80 -0
- package/cli/hooks/guard-route.js +211 -0
- package/cli/hooks/guard-rules-nudge.js +37 -0
- package/cli/hooks/guard-rules-reads.js +66 -0
- package/cli/hooks/guard-rules-shell.js +239 -0
- package/cli/hooks/guard-rules.js +114 -0
- package/cli/hooks/hook-commands.js +21 -0
- package/cli/hooks/install-hooks-apply.js +36 -0
- package/cli/hooks/install-hooks-cli.js +100 -0
- package/cli/hooks/install-hooks-config.js +48 -0
- package/cli/hooks/install-hooks-pins.js +66 -0
- package/cli/hooks/install-hooks-plan.js +56 -0
- package/cli/hooks/install-hooks-status.js +7 -0
- package/cli/hooks/interpreter-writes.js +195 -0
- package/cli/hooks/launcher.js +60 -0
- package/cli/hooks/mcp-json-merge.js +39 -0
- package/cli/hooks/native-edit-lock.js +40 -0
- package/cli/hooks/native-tool-policy.js +152 -0
- package/cli/hooks/out-of-band-edits.js +141 -0
- package/cli/hooks/post-edit-scope.js +43 -0
- package/cli/hooks/project-status.js +59 -0
- package/cli/hooks/repo-membership.js +32 -0
- package/cli/hooks/run-hook.js +79 -0
- package/cli/hooks/session-identity.js +153 -0
- package/cli/hooks/session-start.js +50 -0
- package/cli/hooks/shell-lexer-readers.js +76 -0
- package/cli/hooks/shell-lexer-state.js +59 -0
- package/cli/hooks/shell-lexer.js +110 -0
- package/cli/hooks/shell-parse.js +232 -0
- package/cli/hooks/statusline.js +27 -0
- package/cli/hooks/xatoms-hints.js +38 -0
- package/cli/host-protocol.js +109 -0
- package/cli/host-shims.js +9 -8
- package/cli/index-admission.js +41 -0
- package/cli/index-freshness.js +83 -0
- package/cli/index-row-check.js +58 -0
- package/cli/index.js +52 -150
- package/cli/installer-templates.js +67 -75
- package/cli/installer.d.ts +16 -1
- package/cli/installer.js +159 -58
- package/cli/languages.js +8 -4
- package/cli/lattice-directives.js +24 -0
- package/cli/lattice-payload.js +34 -0
- package/cli/{tesseract-state.js → lattice-state.js} +8 -4
- package/cli/lease-view.js +48 -0
- package/cli/library/entry-fp.js +42 -0
- package/cli/library/entry-schema.js +90 -0
- package/cli/library/library-tasks.js +25 -0
- package/cli/library/registry.js +56 -0
- package/cli/library/reverify.js +56 -0
- package/cli/library/verify-checks.js +89 -0
- package/cli/library/verify.js +81 -0
- package/cli/license-config.js +124 -0
- package/cli/license.js +168 -135
- package/cli/line-count.js +23 -0
- package/cli/literal-replace.js +107 -0
- package/cli/main.js +166 -0
- package/cli/mcp/antigravity.js +11 -9
- package/cli/mcp/batch.js +59 -0
- package/cli/mcp/call-args.js +75 -0
- package/cli/mcp/call-effects.js +57 -0
- package/cli/mcp/call-scope.js +70 -62
- package/cli/mcp/context.js +81 -0
- package/cli/mcp/envelope.js +62 -0
- package/cli/mcp/fingerprint.js +26 -0
- package/cli/mcp/fresh-entry.js +34 -0
- package/cli/mcp/fresh-protocol.js +37 -0
- package/cli/mcp/fresh-runner.js +40 -0
- package/cli/mcp/help.js +53 -0
- package/cli/mcp/index.js +9 -5
- package/cli/mcp/inflight.js +47 -0
- package/cli/mcp/installer-jsonc.js +64 -0
- package/cli/mcp/installer-package.js +54 -0
- package/cli/mcp/installer-report.js +47 -0
- package/cli/mcp/installer-write.js +117 -0
- package/cli/mcp/installer.js +88 -187
- package/cli/mcp/manifests-analysis.js +156 -0
- package/cli/mcp/manifests-runners.js +48 -0
- package/cli/mcp/manifests-team.js +100 -0
- package/cli/mcp/manifests.js +69 -309
- package/cli/mcp/offload-worker.js +11 -0
- package/cli/mcp/offload.js +18 -0
- package/cli/mcp/patterns-cli.js +76 -0
- package/cli/mcp/patterns-forge-cli.js +120 -0
- package/cli/mcp/prompts.js +13 -10
- package/cli/mcp/resources.js +8 -35
- package/cli/mcp/roots.js +46 -0
- package/cli/mcp/scorecard-worker.js +7 -0
- package/cli/mcp/scorecard.js +42 -0
- package/cli/mcp/server-info.js +28 -0
- package/cli/mcp/server.js +91 -291
- package/cli/mcp/spec-harness.js +62 -0
- package/cli/mcp/stale-exec.js +48 -0
- package/cli/mcp/staleness.js +26 -0
- package/cli/mcp/stdio.js +141 -0
- package/cli/mcp/tool-call.js +73 -0
- package/cli/mcp/tools-audit-feed.js +33 -0
- package/cli/mcp/tools-audit.js +13 -7
- package/cli/mcp/tools-generate.js +3 -3
- package/cli/mcp/tools-lazy.js +39 -0
- package/cli/mcp/tools-patch.js +93 -21
- package/cli/mcp/tools-patterns.js +6 -3
- package/cli/mcp/tools-project.js +8 -4
- package/cli/mcp/tools-q-modes.js +15 -14
- package/cli/mcp/tools-q.js +94 -33
- package/cli/mcp/tools-read.js +30 -80
- package/cli/mcp/tools-team-feed.js +10 -8
- package/cli/mcp/tools-team-locks.js +22 -10
- package/cli/mcp/tools-team-mailbox.js +8 -7
- package/cli/mcp/tools-team-tasks.js +125 -38
- package/cli/mcp/tools-team.js +14 -7
- package/cli/mcp/tools-verify.js +44 -15
- package/cli/mcp/tools-write.js +11 -16
- package/cli/mcp/tools.js +202 -68
- package/cli/mcp/worker-run.js +24 -0
- package/cli/missing-imports.js +52 -0
- package/cli/mutation-args.js +96 -0
- package/cli/mutators.js +164 -61
- package/cli/navigator-actions.js +19 -13
- package/cli/navigator-banner-helpers.js +1 -26
- package/cli/navigator-banner.js +5 -3
- package/cli/navigator-conversion.js +23 -12
- package/cli/navigator-grades.js +70 -35
- package/cli/navigator-guide.js +13 -11
- package/cli/navigator-menu.js +80 -166
- package/cli/navigator-paged.js +15 -8
- package/cli/navigator-sections.js +32 -0
- package/cli/navigator-share.js +53 -39
- package/cli/navigator.d.ts +25 -5
- package/cli/navigator.js +14 -41
- package/cli/network-policy.js +72 -0
- package/cli/network-recorder.fixture.mjs +35 -0
- package/cli/outline/ast-outline.js +120 -0
- package/cli/outline/style-outline.js +42 -0
- package/cli/outline/vue-outline.js +88 -0
- package/cli/patcher-cli.js +255 -0
- package/cli/patcher.d.ts +55 -24
- package/cli/patcher.js +167 -358
- package/cli/path-scope.js +78 -22
- package/cli/patterns/fixtures/gt/A1.txt +109 -0
- package/cli/patterns/fixtures/gt/A10.txt +13 -0
- package/cli/patterns/fixtures/gt/A11.txt +10 -0
- package/cli/patterns/fixtures/gt/A12.txt +29 -0
- package/cli/patterns/fixtures/gt/A13.txt +39 -0
- package/cli/patterns/fixtures/gt/A14.txt +45 -0
- package/cli/patterns/fixtures/gt/A15.txt +70 -0
- package/cli/patterns/fixtures/gt/A16.txt +59 -0
- package/cli/patterns/fixtures/gt/A17.txt +29 -0
- package/cli/patterns/fixtures/gt/A18.txt +9 -0
- package/cli/patterns/fixtures/gt/A19.txt +29 -0
- package/cli/patterns/fixtures/gt/A2.txt +28 -0
- package/cli/patterns/fixtures/gt/A20.txt +83 -0
- package/cli/patterns/fixtures/gt/A21.txt +138 -0
- package/cli/patterns/fixtures/gt/A22.txt +32 -0
- package/cli/patterns/fixtures/gt/A23.txt +37 -0
- package/cli/patterns/fixtures/gt/A24.txt +67 -0
- package/cli/patterns/fixtures/gt/A25.txt +41 -0
- package/cli/patterns/fixtures/gt/A26.txt +29 -0
- package/cli/patterns/fixtures/gt/A3.txt +17 -0
- package/cli/patterns/fixtures/gt/A4.txt +44 -0
- package/cli/patterns/fixtures/gt/A5.txt +32 -0
- package/cli/patterns/fixtures/gt/A6.txt +15 -0
- package/cli/patterns/fixtures/gt/A7.txt +79 -0
- package/cli/patterns/fixtures/gt/A8.txt +41 -0
- package/cli/patterns/fixtures/gt/A9.txt +42 -0
- package/cli/patterns/fixtures/gt/B1.txt +11 -0
- package/cli/patterns/fixtures/gt/B10.txt +41 -0
- package/cli/patterns/fixtures/gt/B11.txt +7 -0
- package/cli/patterns/fixtures/gt/B2.txt +19 -0
- package/cli/patterns/fixtures/gt/B3.txt +70 -0
- package/cli/patterns/fixtures/gt/B4.txt +55 -0
- package/cli/patterns/fixtures/gt/B5.txt +21 -0
- package/cli/patterns/fixtures/gt/B6.txt +15 -0
- package/cli/patterns/fixtures/gt/B7.txt +24 -0
- package/cli/patterns/fixtures/gt/B8.txt +40 -0
- package/cli/patterns/fixtures/gt/B9.txt +18 -0
- package/cli/patterns/fixtures/gt/C1.txt +26 -0
- package/cli/patterns/fixtures/gt/C2.txt +33 -0
- package/cli/patterns/fixtures/gt/C3.txt +26 -0
- package/cli/patterns/fixtures/gt/C4.txt +7 -0
- package/cli/patterns/fixtures/gt/C5.txt +29 -0
- package/cli/patterns/fixtures/gt/C6.txt +7 -0
- package/cli/patterns/fixtures/gt/C7.txt +18 -0
- package/cli/patterns/fixtures/gt/labels.json +1837 -0
- package/cli/patterns/gt-build.js +55 -0
- package/cli/patterns/gt-locations.js +76 -0
- package/cli/patterns/gt-resolve.js +66 -0
- package/cli/patterns/gt-score-cli.js +80 -0
- package/cli/patterns/gt-score.js +188 -0
- package/cli/patterns/gt-text.js +79 -0
- package/cli/pillars-wizard.js +53 -24
- package/cli/pillars-write-guard.js +4 -1
- package/cli/postinstall.js +22 -19
- package/cli/project-detector.js +60 -35
- package/cli/project-markers.js +42 -0
- package/cli/project-scope.js +20 -0
- package/cli/read-revision.js +70 -0
- package/cli/reader-cards-gate.js +29 -0
- package/cli/reader-cards.js +153 -0
- package/cli/reader-cli.js +45 -18
- package/cli/reader-format.js +48 -0
- package/cli/reader-logic.js +128 -250
- package/cli/reader.js +210 -221
- package/cli/request-context.js +9 -0
- package/cli/result-status.js +23 -0
- package/cli/rules.js +81 -0
- package/cli/scaffold.js +80 -51
- package/cli/scan-ignore.js +12 -0
- package/cli/search-args.js +102 -0
- package/cli/search-ast-module.js +103 -0
- package/cli/search-ast.js +75 -112
- package/cli/search-commands-blast.js +70 -0
- package/cli/search-commands-graph.js +57 -105
- package/cli/search-commands-hazards.js +105 -0
- package/cli/search-commands-literal.js +102 -0
- package/cli/search-commands-semantic.js +39 -27
- package/cli/search-commands.js +93 -186
- package/cli/search-db.js +52 -252
- package/cli/search-debug.js +13 -0
- package/cli/search-graph-edges.js +112 -0
- package/cli/search-index-meta.js +66 -0
- package/cli/search-index-write.js +153 -0
- package/cli/search-literal-files.js +64 -0
- package/cli/search-literal.js +150 -0
- package/cli/search-output.js +88 -0
- package/cli/search-queries-graph.js +120 -193
- package/cli/search-queries-semantic.js +41 -14
- package/cli/search-queries-similar.js +2 -1
- package/cli/search-queries.js +46 -21
- package/cli/search-query-view.js +67 -0
- package/cli/search-rank.js +54 -0
- package/cli/search-replace-blocks.js +120 -0
- package/cli/search-resolve.js +52 -0
- package/cli/search-root.js +78 -0
- package/cli/search-scan.js +102 -0
- package/cli/search-schema-ddl.js +172 -0
- package/cli/search-schema.js +135 -220
- package/cli/search-session.js +11 -0
- package/cli/search-sync-paths.js +78 -0
- package/cli/search-sync-plan.js +63 -0
- package/cli/search-sync-rows.js +96 -0
- package/cli/search-sync.js +128 -0
- package/cli/search-tokenizer.js +8 -4
- package/cli/search.d.ts +26 -5
- package/cli/search.js +156 -410
- package/cli/sfc/component-resolver.js +96 -0
- package/cli/sfc/import-coverage.js +24 -0
- package/cli/sfc/module-aliases.js +102 -0
- package/cli/sfc/script-asts.js +51 -0
- package/cli/sfc/sfc-metadata.js +104 -0
- package/cli/sfc/sfc-parse.js +101 -0
- package/cli/sfc/svelte-parse.js +46 -0
- package/cli/sfc/template-walk.js +78 -0
- package/cli/sfc-scripts.js +60 -0
- package/cli/share-consent.js +49 -0
- package/cli/share-outcome.js +57 -0
- package/cli/silence-warnings.js +2 -1
- package/cli/source-parse.js +130 -0
- package/cli/spec-isolated-home.js +17 -0
- package/cli/spec-support/import-trace.mjs +22 -0
- package/cli/spec-support/run-cli.js +58 -0
- package/cli/sqlite-memory.js +47 -0
- package/cli/status-command.js +107 -0
- package/cli/symbol-locator.js +121 -0
- package/cli/team/agent-identity.js +58 -0
- package/cli/team/audit-run-adoption.js +117 -0
- package/cli/team/audit-run-bypass.js +177 -0
- package/cli/team/audit-run-calls.js +108 -0
- package/cli/team/audit-run-db.js +181 -0
- package/cli/team/audit-run-identity.js +80 -0
- package/cli/team/audit-run-invocations.js +130 -0
- package/cli/team/audit-run-protocol.js +258 -0
- package/cli/team/audit-run-render.js +107 -0
- package/cli/team/audit-run-shas.js +179 -0
- package/cli/team/audit-run.js +203 -0
- package/cli/team/claim-ignore-deps.js +16 -0
- package/cli/team/coordination-busy.js +164 -0
- package/cli/team/coordination-db.js +85 -0
- package/cli/team/coordination-fixture.js +53 -0
- package/cli/team/coordination-repos.js +72 -0
- package/cli/team/coordination-root.js +161 -0
- package/cli/team/coordination-target.js +114 -0
- package/cli/team/dispatch-templates/dispatch-v1.js +228 -0
- package/cli/team/edit-shas.js +45 -0
- package/cli/team/lease-activity.js +130 -0
- package/cli/team/lease-cap.js +67 -0
- package/cli/team/lease-contention.js +30 -0
- package/cli/team/lease-key.js +46 -0
- package/cli/team/lease-lapse.js +83 -0
- package/cli/team/lease-reacquire.js +99 -0
- package/cli/team/lease-renew.js +97 -0
- package/cli/team/lease-roots.js +30 -0
- package/cli/team/lease-waiter-notice.js +48 -0
- package/cli/team/staged-leases.js +64 -0
- package/cli/team/task-completion-output.js +78 -0
- package/cli/team/task-detail-sections.js +86 -0
- package/cli/team/task-list-view.js +10 -1
- package/cli/team/task-ref.js +73 -0
- package/cli/team/task-target.js +53 -0
- package/cli/team/task-tier.js +17 -0
- package/cli/team/team-brief.js +78 -0
- package/cli/team/team-commands-board.js +118 -0
- package/cli/team/team-commands-dispatch.js +85 -0
- package/cli/team/team-commands-lock-staged.js +87 -0
- package/cli/team/team-commands-lock-views.js +144 -0
- package/cli/team/team-commands-lock.js +115 -26
- package/cli/team/team-commands-migrate.js +171 -0
- package/cli/team/team-commands-profile.js +44 -0
- package/cli/team/team-commands-repo.js +114 -0
- package/cli/team/team-commands-task.js +312 -0
- package/cli/team/team-commands-tokens.js +57 -0
- package/cli/team/team-commands-triage.js +43 -0
- package/cli/team/team-commands-vds.js +2 -1
- package/cli/team/team-commands.js +108 -463
- package/cli/team/team-db-agents.js +12 -6
- package/cli/team/team-db-feed.js +25 -13
- package/cli/team/team-db-lock-promotion.js +95 -51
- package/cli/team/team-db-locks.js +167 -35
- package/cli/team/team-db-mailbox.js +15 -8
- package/cli/team/team-db-readonly.js +76 -0
- package/cli/team/team-db-task-helpers.js +68 -19
- package/cli/team/team-db-tasks.js +21 -9
- package/cli/team/team-db-transaction.js +20 -5
- package/cli/team/team-db.js +11 -4
- package/cli/team/team-dispatch-batches.js +271 -0
- package/cli/team/team-dispatch-prompts.js +152 -0
- package/cli/team/team-dispatch-render.js +154 -0
- package/cli/team/team-dispatch-run-cli.js +154 -0
- package/cli/team/team-dispatch-runs.js +155 -0
- package/cli/team/team-dispatch-script.js +67 -0
- package/cli/team/team-dispatch-select.js +220 -0
- package/cli/team/team-dispatch-v2-fixture.js +62 -0
- package/cli/team/team-dispatch-v2.js +207 -0
- package/cli/team/team-dispatch.js +229 -0
- package/cli/team/team-dispatcher-authority.js +43 -0
- package/cli/team/team-flags.js +222 -48
- package/cli/team/team-format.js +80 -26
- package/cli/team/team-handoff-hint.js +9 -0
- package/cli/team/team-migrate-agents.js +52 -0
- package/cli/team/team-migrate-apply.js +178 -0
- package/cli/team/team-migrate-junk.js +55 -0
- package/cli/team/team-migrate-metadata.js +80 -0
- package/cli/team/team-migrate-paths.js +50 -0
- package/cli/team/team-migrate-plan.js +85 -0
- package/cli/team/team-migrate-report.js +89 -0
- package/cli/team/team-migrate-tables.js +85 -0
- package/cli/team/team-migrate.js +193 -0
- package/cli/team/team-needs.js +99 -0
- package/cli/team/team-profile-format.js +126 -0
- package/cli/team/team-profile.js +144 -0
- package/cli/team/team-projects-coordinator.js +54 -33
- package/cli/team/team-projects-format.js +8 -4
- package/cli/team/team-projects-memory.js +6 -3
- package/cli/team/team-projects.js +2 -1
- package/cli/team/team-release-train.js +2 -1
- package/cli/team/team-route.js +132 -0
- package/cli/team/team-schema-repo.js +48 -0
- package/cli/team/team-schema-usage.js +25 -0
- package/cli/team/team-schema-vds.js +7 -3
- package/cli/team/team-schema.js +24 -4
- package/cli/team/team-task-close.js +132 -0
- package/cli/team/team-task-guard.js +24 -0
- package/cli/team/team-task-handoff.js +122 -0
- package/cli/team/team-task-ownership.js +74 -0
- package/cli/team/team-task-rules.js +21 -0
- package/cli/team/team-task-uncommitted.js +49 -0
- package/cli/team/team-telemetry.js +44 -32
- package/cli/team/team-tokens.js +4 -1
- package/cli/team/team-triage-generate.js +212 -0
- package/cli/team/team-triage-log.js +11 -0
- package/cli/team/team-triage-verify.js +153 -0
- package/cli/team/team-triage.js +92 -339
- package/cli/team/team-vds.js +2 -1
- package/cli/team/usage-compute.js +70 -0
- package/cli/team/usage-pricing.js +62 -0
- package/cli/team/usage-reader.js +106 -0
- package/cli/team/usage-render.js +72 -0
- package/cli/team/usage-store.js +56 -0
- package/cli/team/usage-transcript.js +127 -0
- package/cli/team/write-lock-guard.js +48 -0
- package/cli/telemetry/call-ledger.js +264 -0
- package/cli/telemetry/call-schema.js +53 -0
- package/cli/telemetry/savings-dbs.js +111 -0
- package/cli/telemetry/savings-render.js +68 -0
- package/cli/telemetry/savings-report.js +101 -0
- package/cli/telemetry/savings-routing.js +66 -0
- package/cli/telemetry/savings-tooling.js +140 -0
- package/cli/terminal.js +38 -40
- package/cli/tesseract-manifesto.js +2 -17
- package/cli/tesseract.js +8 -21
- package/cli/test-audit.js +218 -0
- package/cli/test-changes.js +60 -0
- package/cli/test-command.js +201 -0
- package/cli/test-depth.js +43 -0
- package/cli/test-failures.js +170 -0
- package/cli/test-graph-open.js +150 -0
- package/cli/test-graph.js +188 -0
- package/cli/test-lanes.js +117 -0
- package/cli/test-output.js +155 -0
- package/cli/test-paths.js +51 -0
- package/cli/test-profile.js +64 -0
- package/cli/test-report.js +112 -0
- package/cli/test-run.js +53 -0
- package/cli/test-scope.js +55 -0
- package/cli/test-select.js +107 -0
- package/cli/test-slots.js +276 -0
- package/cli/test-workers.js +45 -0
- package/cli/test-workspace.js +83 -0
- package/cli/theme.js +6 -3
- package/cli/timers.js +6 -0
- package/cli/trend.js +10 -5
- package/cli/tsconfig-shape.js +20 -0
- package/cli/tty-banner.js +11 -0
- package/cli/typecheck-audit.js +146 -0
- package/cli/typecheck-command.js +94 -0
- package/cli/typecheck-sandbox.js +93 -0
- package/cli/ui-actions-codebase.js +20 -7
- package/cli/ui-actions-helpers.js +9 -0
- package/cli/ui-actions-studio.js +11 -7
- package/cli/ui-actions-tasks.js +3 -2
- package/cli/ui-actions-vds.js +2 -1
- package/cli/ui-actions.js +4 -2
- package/cli/ui-attention.js +28 -12
- package/cli/ui-auth-hosts.js +46 -0
- package/cli/ui-auth.js +89 -0
- package/cli/ui-client-cert.js +2 -1
- package/cli/ui-client-filetree.js +2 -2
- package/cli/ui-db-studio.js +23 -33
- package/cli/ui-dev-watcher.js +2 -1
- package/cli/ui-forum-data.js +45 -46
- package/cli/ui-forum-topics.js +6 -3
- package/cli/ui-fresh-index.js +11 -0
- package/cli/ui-handlers.js +6 -6
- package/cli/ui-html.js +6 -12
- package/cli/ui-index.html +3 -3
- package/cli/ui-route-helpers.js +10 -0
- package/cli/ui-server-routes.js +15 -17
- package/cli/ui-server.js +49 -50
- package/cli/ui-sql-guard.js +76 -0
- package/cli/ui-sql-mask.js +61 -0
- package/cli/ui-sse.js +5 -4
- package/cli/ui-template-filetree.js +4 -4
- package/cli/ui-template-kanban.js +4 -7
- package/cli/verify-changed.js +43 -0
- package/cli/verify-helpers.js +21 -207
- package/cli/verify-lint.js +54 -20
- package/cli/verify-report.js +117 -0
- package/cli/verify-steps.js +71 -0
- package/cli/verify.js +111 -372
- package/cli/wait-command.js +147 -0
- package/cli/workspace-run.js +153 -0
- package/cli/workspace.js +147 -0
- package/cli/write-append.js +28 -0
- package/library/node-js/is-path-inside/entry.json +57 -0
- package/library/node-js/is-path-inside/negative/path-scope.js.txt +13 -0
- package/library/node-js/is-path-inside/piece.js +15 -0
- package/library/node-js/is-path-inside/piece.spec.js +38 -0
- package/library/node-js/read-json-or/entry.json +70 -0
- package/library/node-js/read-json-or/negative/ratchet.js.txt +14 -0
- package/library/node-js/read-json-or/negative/workspace.js.txt +8 -0
- package/library/node-js/read-json-or/piece.js +17 -0
- package/library/node-js/read-json-or/piece.spec.js +36 -0
- package/library/vue-ts/nullable-timer-handle/ambient.d.ts +5 -0
- package/library/vue-ts/nullable-timer-handle/entry.json +84 -0
- package/library/vue-ts/nullable-timer-handle/negative/const-alias.ts.txt +17 -0
- package/library/vue-ts/nullable-timer-handle/negative/inline-guard.ts.txt +15 -0
- package/library/vue-ts/nullable-timer-handle/piece.spec.js +48 -0
- package/library/vue-ts/nullable-timer-handle/piece.ts +41 -0
- package/package.json +9 -3
- package/scripts/check-framework-generation.mjs +32 -12
- package/scripts/direct-run.mjs +9 -0
- package/scripts/pre-commit.sh +48 -74
- package/scripts/publish-both.mjs +24 -16
- package/scripts/publish-summary.mjs +14 -0
- package/scripts/postinstall.mjs +0 -27
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// The one project-root resolver for MCP calls and wrappers.
|
|
2
|
+
// Order: explicit projectRoot > MCP roots > server dir > CHEMX_PROJECT_ROOT
|
|
3
|
+
// > boot dir with a .chemx/.chemxrc marker. Anything else is refused, never guessed.
|
|
4
|
+
// A target path never picks the root (that let any package.json ancestor, even $HOME, become one).
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { LOADED_VERSION } from './server-info.js';
|
|
8
|
+
import { findChemxDir } from '../audit/chemx-dir.js';
|
|
9
|
+
|
|
10
|
+
const CHEMX_MARKERS = ['.chemxrc', '.chemxrc.json', '.chemx'];
|
|
11
|
+
const PROJECT_MARKERS = [...CHEMX_MARKERS, 'package.json', '.git'];
|
|
12
|
+
|
|
13
|
+
const hasAnyMarker = (dir, markers) => markers.some((m) => fs.existsSync(path.join(dir, m)));
|
|
14
|
+
|
|
15
|
+
export const isExistingDir = (p) => typeof p === 'string' && path.isAbsolute(p) && fs.existsSync(p) && fs.statSync(p).isDirectory();
|
|
16
|
+
|
|
17
|
+
export const hasChemxMarker = (dir) => hasAnyMarker(dir, CHEMX_MARKERS);
|
|
18
|
+
|
|
19
|
+
export const looksLikeProject = (dir) => hasAnyMarker(dir, PROJECT_MARKERS);
|
|
20
|
+
|
|
21
|
+
export const isInsideDir = (root, target) => {
|
|
22
|
+
const rel = path.relative(root, target);
|
|
23
|
+
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// realpath of the nearest existing ancestor plus the not-yet-existing tail, so a symlink is followed.
|
|
27
|
+
const realResolve = (target) => {
|
|
28
|
+
const tail = [];
|
|
29
|
+
let current = target;
|
|
30
|
+
while (!fs.existsSync(current)) {
|
|
31
|
+
const parent = path.dirname(current);
|
|
32
|
+
const isFilesystemRoot = parent === current;
|
|
33
|
+
if (isFilesystemRoot) return target;
|
|
34
|
+
tail.unshift(path.basename(current));
|
|
35
|
+
current = parent;
|
|
36
|
+
}
|
|
37
|
+
return path.join(fs.realpathSync(current), ...tail);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Inside both lexically and after following symlinks: a link in the root cannot carry a path out of it.
|
|
41
|
+
export const resolveInsideRoot = (root, requested) => {
|
|
42
|
+
const resolved = path.resolve(root, requested);
|
|
43
|
+
const isInside = isInsideDir(root, resolved) && isInsideDir(realResolve(root), realResolve(resolved));
|
|
44
|
+
return { requested, resolved, isInside };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const parseAllowedRoots =(env) => (env.CHEMX_MCP_ALLOWED_ROOTS || '').split(path.delimiter).filter((p) => path.isAbsolute(p));
|
|
48
|
+
|
|
49
|
+
// When the client sent MCP roots or the server was started with a dir, projectRoot may only narrow them.
|
|
50
|
+
const locateProjectRoot = (projectRoot, declaredRoots) => {
|
|
51
|
+
const isUsableRoot = isExistingDir(projectRoot) && looksLikeProject(projectRoot);
|
|
52
|
+
if (!isUsableRoot) return { ok: false, error: `projectRoot "${projectRoot}" must be an absolute path to an existing project directory (.chemxrc, .chemx, package.json or .git).` };
|
|
53
|
+
const isBounded = declaredRoots.length === 0 || declaredRoots.some((root) => resolveInsideRoot(root, projectRoot).isInside);
|
|
54
|
+
if (!isBounded) return { ok: false, error: `projectRoot "${projectRoot}" is outside the declared roots (${declaredRoots.join(', ')}).` };
|
|
55
|
+
return { ok: true, root: projectRoot, rootSource: 'projectRoot' };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const locateRoot = ({ projectRoot, mcpRoots, serverRoot, envRoot, bootDir }) => {
|
|
59
|
+
const hasProjectRoot = !(projectRoot === undefined || projectRoot === null);
|
|
60
|
+
if (hasProjectRoot) return locateProjectRoot(projectRoot, [...mcpRoots, serverRoot].filter(Boolean));
|
|
61
|
+
const hasMcpRoots = mcpRoots.length > 0;
|
|
62
|
+
if (hasMcpRoots) return { ok: true, root: mcpRoots[0], rootSource: 'mcpRoots' };
|
|
63
|
+
if (serverRoot) return { ok: true, root: serverRoot, rootSource: 'declared' };
|
|
64
|
+
if (envRoot) return { ok: true, root: envRoot, rootSource: 'env' };
|
|
65
|
+
if (bootDir) return { ok: true, root: bootDir, rootSource: 'boot' };
|
|
66
|
+
return { ok: false, error: 'No project root: pass projectRoot, start the server with a directory, set CHEMX_PROJECT_ROOT, or start it inside a project with .chemxrc/.chemx.' };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const resolveContext = ({ cwd = null, projectRoot = null, mcpRoots = [], serverRoot = null, env = process.env } = {}) => {
|
|
70
|
+
const envRoot = isExistingDir(env.CHEMX_PROJECT_ROOT) ? env.CHEMX_PROJECT_ROOT : null;
|
|
71
|
+
const bootDir = cwd && hasChemxMarker(cwd) ? cwd : null;
|
|
72
|
+
const located = locateRoot({ projectRoot, mcpRoots, serverRoot, envRoot, bootDir });
|
|
73
|
+
const hasLocateFailed = !located.ok;
|
|
74
|
+
if (hasLocateFailed) return located;
|
|
75
|
+
const allowedRoots = parseAllowedRoots(env);
|
|
76
|
+
const isPinned = allowedRoots.length > 0;
|
|
77
|
+
const isAllowed = !isPinned || allowedRoots.some((allowed) => isInsideDir(allowed, located.root));
|
|
78
|
+
if (!isAllowed) return { ok: false, error: `Root "${located.root}" is outside CHEMX_MCP_ALLOWED_ROOTS.` };
|
|
79
|
+
// dbPath reports where the index layer will really open the db (it walks up to an existing .chemx).
|
|
80
|
+
return { ...located, dbPath: path.join(findChemxDir(located.root), 'index.db'), version: LOADED_VERSION };
|
|
81
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Turns handler results into MCP content: plain text, no ANSI, no JSON-in-text for wrapper output.
|
|
2
|
+
import { stripAnsi } from '../terminal.js';
|
|
3
|
+
import { STATUS } from '../result-status.js';
|
|
4
|
+
import { LOADED_VERSION } from './server-info.js';
|
|
5
|
+
|
|
6
|
+
const textItem = (text) => ({ type: 'text', text });
|
|
7
|
+
|
|
8
|
+
const stripDeep = (value) => {
|
|
9
|
+
const isString = typeof value === 'string';
|
|
10
|
+
if (isString) return stripAnsi(value);
|
|
11
|
+
if (Array.isArray(value)) return value.map(stripDeep);
|
|
12
|
+
const isPlainObject = value !== null && typeof value === 'object';
|
|
13
|
+
if (!isPlainObject) return value;
|
|
14
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, stripDeep(v)]));
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const isContentEnvelope = (output) => Boolean(output) && Array.isArray(output.content);
|
|
18
|
+
const isWrapperResult = (output) => Boolean(output) && typeof output.output === 'string' && typeof output.code === 'number';
|
|
19
|
+
|
|
20
|
+
const wrapperText = ({ output, code, error }) => {
|
|
21
|
+
const isFailure = code !== 0;
|
|
22
|
+
const body = stripAnsi(output || '');
|
|
23
|
+
if (!isFailure) return body || '(no output)';
|
|
24
|
+
const reason = stripAnsi(error || '').trim();
|
|
25
|
+
return [body.trim(), reason, `exit ${code}`].filter(Boolean).join('\n');
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const KNOWN_STATUSES = new Set(Object.values(STATUS));
|
|
29
|
+
|
|
30
|
+
// The one failure test for single calls and batch items: a tri-state status decides when present,
|
|
31
|
+
// otherwise a nonzero wrapper code, success:false, an error message or isError.
|
|
32
|
+
export const isFailedResult = (output) => {
|
|
33
|
+
const hasKnownStatus = KNOWN_STATUSES.has(output?.status);
|
|
34
|
+
if (hasKnownStatus) return output.status === STATUS.FAIL;
|
|
35
|
+
const hasFailCode = isWrapperResult(output) && output.code !== 0;
|
|
36
|
+
const hasFalseSuccess = output?.success === false;
|
|
37
|
+
const hasErrorMessage = typeof output?.error === 'string' && output.error.length > 0;
|
|
38
|
+
return hasFailCode || hasFalseSuccess || hasErrorMessage || Boolean(output?.isError);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// One handler result to { content, isError }.
|
|
42
|
+
export const toEnvelope = (output) => {
|
|
43
|
+
const isTextOutput = typeof output === 'string';
|
|
44
|
+
if (isTextOutput) return { content: [textItem(stripAnsi(output))], isError: false };
|
|
45
|
+
if (isContentEnvelope(output)) {
|
|
46
|
+
const content = output.content.map((c) => (c.type === 'text' ? textItem(stripAnsi(c.text)) : c));
|
|
47
|
+
return { content, isError: Boolean(output.isError) };
|
|
48
|
+
}
|
|
49
|
+
if (isWrapperResult(output)) return { content: [textItem(wrapperText(output))], isError: output.code !== 0 };
|
|
50
|
+
const serialized = JSON.stringify(stripDeep(output ?? null));
|
|
51
|
+
return { content: [textItem(serialized)], isError: isFailedResult(output) };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const errorEnvelope = (message) => ({ content: [textItem(stripAnsi(message))], isError: true });
|
|
55
|
+
|
|
56
|
+
export const scopeLine = (scope) => {
|
|
57
|
+
const isResolved = Boolean(scope?.root);
|
|
58
|
+
if (!isResolved) return textItem(`chemx root: unresolved v${LOADED_VERSION}`);
|
|
59
|
+
return textItem(`chemx root: ${scope.root} (${scope.rootSource}) v${scope.version}`);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { textItem };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Fingerprint of the cli/ sources on disk: size and mtime of every non-spec .js file, hashed.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
export const CLI_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
8
|
+
const isSourceFile = (name) => name.endsWith('.js') && !name.endsWith('.spec.js');
|
|
9
|
+
|
|
10
|
+
const collectSourceStats = (dir, out) => {
|
|
11
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
12
|
+
const abs = path.join(dir, entry.name);
|
|
13
|
+
if (entry.isDirectory()) collectSourceStats(abs, out);
|
|
14
|
+
else if (isSourceFile(entry.name)) {
|
|
15
|
+
const stat = fs.statSync(abs);
|
|
16
|
+
out.push(`${path.relative(CLI_DIR, abs)}:${stat.size}:${stat.mtimeMs}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const fingerprintCliSources = (dir = CLI_DIR) => crypto
|
|
23
|
+
.createHash('sha1')
|
|
24
|
+
.update(collectSourceStats(dir, []).sort().join('\n'))
|
|
25
|
+
.digest('hex')
|
|
26
|
+
.slice(0, 12);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// One-shot executor for a stale MCP server: runs one tool call with the code on disk.
|
|
2
|
+
// stdin: { toolName, toolArgs, root } as JSON. stdout: one line, RESULT_MARK + JSON of
|
|
3
|
+
// { output } | { error } | { loadFailure }. loadFailure means the code on disk did not load.
|
|
4
|
+
import { RESULT_MARK, LOADED_MARK } from './fresh-protocol.js';
|
|
5
|
+
|
|
6
|
+
const readStdin = async () => {
|
|
7
|
+
const chunks = [];
|
|
8
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
9
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const describe = (err) => (err instanceof Error ? err.message : String(err));
|
|
13
|
+
const emit = (payload) => process.stdout.write(`\n${RESULT_MARK}${JSON.stringify(payload)}\n`);
|
|
14
|
+
|
|
15
|
+
const run = async () => {
|
|
16
|
+
const request = JSON.parse(await readStdin());
|
|
17
|
+
let executeMcpTool = null;
|
|
18
|
+
try {
|
|
19
|
+
({ executeMcpTool } = await import('./tools.js'));
|
|
20
|
+
} catch (err) {
|
|
21
|
+
emit({ loadFailure: describe(err) });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
process.stdout.write(`\n${LOADED_MARK}\n`);
|
|
25
|
+
try {
|
|
26
|
+
const output = await executeMcpTool(request.toolName, request.toolArgs, request.root);
|
|
27
|
+
emit({ output: output ?? null });
|
|
28
|
+
} catch (err) {
|
|
29
|
+
emit({ error: describe(err) });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
await run();
|
|
34
|
+
process.exit(0);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Wire format between a stale MCP server and its one-shot fresh process.
|
|
2
|
+
export const RESULT_MARK = '@@chemx-fresh-result@@';
|
|
3
|
+
// Printed by the child as soon as the code on disk has imported, before any tool runs.
|
|
4
|
+
export const LOADED_MARK = '@@chemx-fresh-loaded@@';
|
|
5
|
+
|
|
6
|
+
// The last marked line of the child's stdout, parsed; null when there is none.
|
|
7
|
+
export const parseFreshResult = (stdout) => {
|
|
8
|
+
const marked = String(stdout).split('\n').filter((line) => line.startsWith(RESULT_MARK));
|
|
9
|
+
const last = marked.at(-1);
|
|
10
|
+
if (!last) return null;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(last.slice(RESULT_MARK.length));
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const tail = (text) => String(text).trim().split('\n').slice(-3).join(' | ');
|
|
19
|
+
|
|
20
|
+
// Child exit to { kind: 'ok', output } | { kind: 'error', error } | { kind: 'load', message } | { kind: 'crash', message }.
|
|
21
|
+
// load: the child died before the code imported, so nothing ran. crash: it imported, then died without
|
|
22
|
+
// a result, so the call may have partly run.
|
|
23
|
+
export const classifyFreshExit = ({ stdout, stderr, code }) => {
|
|
24
|
+
const parsed = parseFreshResult(stdout);
|
|
25
|
+
const hasResult = parsed !== null;
|
|
26
|
+
const isLoadFailure = hasResult && Boolean(parsed.loadFailure);
|
|
27
|
+
const isToolError = hasResult && Boolean(parsed.error);
|
|
28
|
+
const hasLoaded = String(stdout).split('\n').includes(LOADED_MARK);
|
|
29
|
+
const detail = `fresh process exited ${code} without a result: ${tail(stderr) || 'no stderr'}`;
|
|
30
|
+
const isCrash = !hasResult && hasLoaded;
|
|
31
|
+
const isNoLoad = !hasResult && !hasLoaded;
|
|
32
|
+
if (isCrash) return { kind: 'crash', message: detail };
|
|
33
|
+
if (isNoLoad) return { kind: 'load', message: detail };
|
|
34
|
+
if (isLoadFailure) return { kind: 'load', message: parsed.loadFailure };
|
|
35
|
+
if (isToolError) return { kind: 'error', error: parsed.error };
|
|
36
|
+
return { kind: 'ok', output: parsed.output };
|
|
37
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Runs one MCP tool call in a one-shot child process that loads the code on disk.
|
|
2
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { classifyFreshExit } from './fresh-protocol.js';
|
|
6
|
+
|
|
7
|
+
const ENTRY = path.join(path.dirname(fileURLToPath(import.meta.url)), 'fresh-entry.js');
|
|
8
|
+
const DEFAULT_TIMEOUT_SECONDS = 600;
|
|
9
|
+
const GRACE_MS = 5000;
|
|
10
|
+
|
|
11
|
+
// The call's own timeout (seconds, as the verify/test/build actions take it) plus a grace period.
|
|
12
|
+
export const timeoutMsFor = (toolArgs) => {
|
|
13
|
+
const asked = toolArgs?.params?.timeout ?? toolArgs?.timeout;
|
|
14
|
+
const isUsable = typeof asked === 'number' && asked > 0;
|
|
15
|
+
return (isUsable ? asked : DEFAULT_TIMEOUT_SECONDS) * 1000 + GRACE_MS;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Resolves { kind: 'ok', output } | { kind: 'error', error } | { kind: 'load', message } | { kind: 'timeout' }.
|
|
19
|
+
export const createFreshRunner = ({ spawn = nodeSpawn, entry = ENTRY } = {}) => ({ toolName, toolArgs, root, env }) => new Promise((resolve) => {
|
|
20
|
+
const child = spawn(process.execPath, [entry], { cwd: root, env, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
21
|
+
let stdout = '';
|
|
22
|
+
let stderr = '';
|
|
23
|
+
let isDone = false;
|
|
24
|
+
const finish = (result) => {
|
|
25
|
+
if (isDone) return;
|
|
26
|
+
isDone = true;
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
resolve(result);
|
|
29
|
+
};
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
child.kill('SIGKILL');
|
|
32
|
+
finish({ kind: 'timeout' });
|
|
33
|
+
}, timeoutMsFor(toolArgs));
|
|
34
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
35
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
36
|
+
child.on('error', (err) => finish({ kind: 'load', message: `cannot start the fresh process: ${err.message}` }));
|
|
37
|
+
child.on('close', (code) => finish(classifyFreshExit({ stdout, stderr, code })));
|
|
38
|
+
child.stdin.on('error', () => {});
|
|
39
|
+
child.stdin.end(JSON.stringify({ toolName, toolArgs, root }));
|
|
40
|
+
});
|
package/cli/mcp/help.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Compact per-action reference for the master `chemx` MCP tool (action: 'help') and server instructions.
|
|
2
|
+
|
|
3
|
+
export const ACTION_HELP = {
|
|
4
|
+
help: '{ action? } this table, or one row',
|
|
5
|
+
read: '{ path, rev?, symbol?, outline?, logic?, template?, enrich?, startLine?, endLine? } (alias r); rev reads the file at a git revision',
|
|
6
|
+
show: '{ args: [rev, --patch?, --full?] } one commit: subject, author, date, body, stat; --patch is -U0 and collapses like d',
|
|
7
|
+
q: '{ query, literal?, lines?, blastRadius?, trace?, backtrace?, semantic?, hybrid?, tier?, limit?, reindex? } (alias search)',
|
|
8
|
+
patch: '{ path, target|search, replacement|replace, multiple?, dryRun? } writes; needs a declared root',
|
|
9
|
+
write: '{ path, content } writes; needs a declared root',
|
|
10
|
+
check: '{ path } single-file rule check',
|
|
11
|
+
audit: '{ path|dir?, full?, triage? } (triage writes team tasks)',
|
|
12
|
+
audit_feed: '{ view?: history|pillars|scopes, scope?, since?, fullOnly?, limit? } read-only rows from .chemx history and status, newest 100 (max 500), with total and cut; string form `audit --feed=<view>`',
|
|
13
|
+
verify: '{ dir?, includeBuild? }',
|
|
14
|
+
test: '{ dir?, testTarget?, filter?, command? } command runs only if it equals a package.json script or CHEMX_MCP_ALLOW_SHELL=1; testTarget is one path or glob, filter has no $ ` " \\',
|
|
15
|
+
typecheck: '{ dir?, command? } command rule as test',
|
|
16
|
+
build: '{ dir?, command? } command rule as test',
|
|
17
|
+
autofix: '{ path?, dryRun? } writes unless dryRun',
|
|
18
|
+
generate: '{ name, tier?, kind?, jig?, dryRun?, ... } writes',
|
|
19
|
+
patterns: '{ dir?, type?, compact? }',
|
|
20
|
+
trend: '{}',
|
|
21
|
+
d: '{ args?: string[] } git diff -U0 (alias diff); args ["--conflicts"] = combined diff of unmerged files; --output, --no-index, --ext-diff refused',
|
|
22
|
+
conflicts: '{} unmerged paths mid-merge/rebase, with ours/base/theirs lines of each conflict hunk',
|
|
23
|
+
log: '{ args?: string[] } git log --oneline (default 10); --output refused',
|
|
24
|
+
p: '{ query? } package.json: -s scripts, -d deps, or one key (alias pkg)',
|
|
25
|
+
f: '{ filter? } tracked files incl. submodules; filter is a glob (*, **, ?) or a substring (alias ls)',
|
|
26
|
+
j: '{ path } JSON: small files verbatim, larger ones as shape with scalar values (alias json)',
|
|
27
|
+
issue: '{ error, stack?, repo?, autoPost? } autoPost publishes to GitHub; needs a declared root',
|
|
28
|
+
project: '{ subAction, goal?|message? } writes (alias coordinator)',
|
|
29
|
+
tesseract: '{ args? } swarm matrix (aliases cube, matrix)',
|
|
30
|
+
team: '{ subAction? }',
|
|
31
|
+
team_status: '{}',
|
|
32
|
+
team_feed: '{ sinceId?, threadId?, taskId?, agentId?, limit? }',
|
|
33
|
+
team_post: '{ message, authorId?, recipientId?, taskId? } writes',
|
|
34
|
+
team_task: '{ subAction: list|add|claim|done|..., taskId?, title?, needs?: light|standard|deep } non-list sub-actions write',
|
|
35
|
+
team_lock: '{ action: acquire|release, filePath, agentId, purpose?, ttlMs? } writes',
|
|
36
|
+
team_inbox: '{ agentId|as, sinceId?, limit?, markRead? }',
|
|
37
|
+
team_dm: '{ to|recipientId, message, as? } writes'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const renderActionHelp = (actionNames, only = null) => {
|
|
41
|
+
const names = only ? actionNames.filter((name) => name === only) : actionNames;
|
|
42
|
+
const isUnknown = Boolean(only) && names.length === 0;
|
|
43
|
+
if (isUnknown) return `Unknown action "${only}". Actions: ${actionNames.join(', ')}`;
|
|
44
|
+
const rows = names.map((name) => `${name}: ${ACTION_HELP[name] ?? 'see action name'}`);
|
|
45
|
+
const footer = 'Forms: { action, params } | { command: "read src/a.ts --outline" } | { commands: ["d", "p -s"] } | { batch: [{ action, params }] }. Every call accepts projectRoot.';
|
|
46
|
+
return [...rows, footer].join('\n');
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const SERVER_INSTRUCTIONS = [
|
|
50
|
+
'Chemical X: one master tool `chemx`. Call { action: "help" } for the per-action parameter table.',
|
|
51
|
+
'Pass projectRoot (absolute) on every call that targets a specific repo; writes are refused when the root was only guessed from the server start directory. Paths must sit inside the root; an absolute path never selects a root by itself.',
|
|
52
|
+
'Every result ends with a "chemx root:" line naming the root and how it was chosen. A "stale chemx MCP server" line means the code on disk changed after this server started: each call then runs in a fresh process with the code on disk (slower) under a notice on the first line, and mutating calls are refused if that code does not load. Reconnect via /mcp to restore speed.'
|
|
53
|
+
].join(' ');
|
package/cli/mcp/index.js
CHANGED
|
@@ -10,18 +10,21 @@ import {
|
|
|
10
10
|
installAllMcpConfigs
|
|
11
11
|
} from './installer.js';
|
|
12
12
|
import { syncAntigravityMcpSchemas } from './antigravity.js';
|
|
13
|
+
import { describeServerOrigin } from './server-info.js';
|
|
13
14
|
|
|
14
15
|
export const runMcpServer = async (rawArgs = []) => {
|
|
15
|
-
|
|
16
|
+
const hasSyncFlag = rawArgs.includes('--sync');
|
|
17
|
+
if (hasSyncFlag) {
|
|
16
18
|
const targetDir = rawArgs.find((a) => !a.startsWith('-')) || null;
|
|
17
19
|
return syncAntigravityMcpSchemas(targetDir);
|
|
18
20
|
}
|
|
19
|
-
|
|
21
|
+
const hasInstallFlag = rawArgs.includes('--install');
|
|
22
|
+
if (hasInstallFlag) {
|
|
20
23
|
return runMcpInstaller(rawArgs);
|
|
21
24
|
}
|
|
22
25
|
// Stdio server must keep stdout strictly reserved for JSON-RPC messages.
|
|
23
26
|
// Informative logs go to stderr.
|
|
24
|
-
process.stderr.write(
|
|
27
|
+
process.stderr.write(`Chemical X MCP server active (stdio): ${describeServerOrigin()}\n`);
|
|
25
28
|
startStdioServer(resolveServerOptions(rawArgs));
|
|
26
29
|
};
|
|
27
30
|
|
|
@@ -31,8 +34,9 @@ export const resolveServerOptions = (rawArgs = []) => {
|
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
export const runMcpInstaller = async (rawArgs = []) => {
|
|
34
|
-
const targetDir = rawArgs
|
|
35
|
-
|
|
37
|
+
const targetDir = rawArgs.find((a) => !a.startsWith('-')) || process.cwd();
|
|
38
|
+
const includeHome = rawArgs.includes('--global') || rawArgs.includes('--antigravity');
|
|
39
|
+
return installAllMcpConfigs(targetDir, { silent: false, includeHome });
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
export {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// In-flight tools/call tracking: cancellation (notifications/cancelled) and progress heartbeats
|
|
2
|
+
// (notifications/progress when the client sent params._meta.progressToken).
|
|
3
|
+
import { runWithRequestContext } from '../request-context.js';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PROGRESS_INTERVAL_MS = 5000;
|
|
6
|
+
|
|
7
|
+
export const createInflight = ({ notify = null, progressIntervalMs = DEFAULT_PROGRESS_INTERVAL_MS } = {}) => {
|
|
8
|
+
const controllers = new Map();
|
|
9
|
+
|
|
10
|
+
const startHeartbeat = (progressToken, label) => {
|
|
11
|
+
const canReport = typeof notify === 'function' && progressToken !== undefined && progressToken !== null;
|
|
12
|
+
if (!canReport) return () => {};
|
|
13
|
+
const startedAt = Date.now();
|
|
14
|
+
let beats = 0;
|
|
15
|
+
const beat = () => {
|
|
16
|
+
beats += 1;
|
|
17
|
+
const seconds = Math.round((Date.now() - startedAt) / 1000);
|
|
18
|
+
notify('notifications/progress', { progressToken, progress: beats, message: `${label} running (${seconds}s)` });
|
|
19
|
+
};
|
|
20
|
+
beat();
|
|
21
|
+
const timer = setInterval(beat, progressIntervalMs);
|
|
22
|
+
return () => clearInterval(timer);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Runs fn under a cancellable request context. Resolves to { cancelled, value }.
|
|
26
|
+
const run = async (id, params, fn) => {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
controllers.set(id, controller);
|
|
29
|
+
const label = params?.arguments?.action || params?.arguments?.command || params?.name || 'chemx';
|
|
30
|
+
const stopHeartbeat = startHeartbeat(params?._meta?.progressToken, label);
|
|
31
|
+
try {
|
|
32
|
+
const value = await runWithRequestContext({ signal: controller.signal }, fn);
|
|
33
|
+
return { cancelled: controller.signal.aborted, value };
|
|
34
|
+
} finally {
|
|
35
|
+
stopHeartbeat();
|
|
36
|
+
controllers.delete(id);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const cancel = (requestId) => {
|
|
41
|
+
const controller = controllers.get(requestId);
|
|
42
|
+
if (controller) controller.abort();
|
|
43
|
+
return Boolean(controller);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return { run, cancel, size: () => controllers.size };
|
|
47
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal JSONC reader for MCP config files.
|
|
3
|
+
* Strips // and block comments and trailing commas outside strings, then JSON.parse.
|
|
4
|
+
* Reports whether comments were present so callers can refuse a lossy rewrite.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const scanOutsideStrings = (text, onCode) => {
|
|
8
|
+
let out = '';
|
|
9
|
+
let inString = false;
|
|
10
|
+
for (let i = 0; i < text.length; i++) {
|
|
11
|
+
const ch = text[i];
|
|
12
|
+
if (inString) {
|
|
13
|
+
out += ch;
|
|
14
|
+
const isEscape = ch === '\\';
|
|
15
|
+
if (isEscape) { out += text[i + 1] ?? ''; i++; continue; }
|
|
16
|
+
const closesString = ch === '"';
|
|
17
|
+
if (closesString) inString = false;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const opensString = ch === '"';
|
|
21
|
+
if (opensString) { inString = true; out += ch; continue; }
|
|
22
|
+
const step = onCode(text, i);
|
|
23
|
+
out += step.emit;
|
|
24
|
+
i += step.skip;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const stripComments = (text) => {
|
|
30
|
+
let hasComments = false;
|
|
31
|
+
const code = scanOutsideStrings(text, (src, i) => {
|
|
32
|
+
const isLineComment = src.startsWith('//', i);
|
|
33
|
+
const isBlockComment = src.startsWith('/*', i);
|
|
34
|
+
if (isLineComment) {
|
|
35
|
+
hasComments = true;
|
|
36
|
+
const end = src.indexOf('\n', i);
|
|
37
|
+
return { emit: '', skip: (end === -1 ? src.length : end) - i - 1 };
|
|
38
|
+
}
|
|
39
|
+
if (isBlockComment) {
|
|
40
|
+
hasComments = true;
|
|
41
|
+
const end = src.indexOf('*/', i + 2);
|
|
42
|
+
return { emit: ' ', skip: (end === -1 ? src.length : end + 2) - i - 1 };
|
|
43
|
+
}
|
|
44
|
+
return { emit: src[i], skip: 0 };
|
|
45
|
+
});
|
|
46
|
+
return { code, hasComments };
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const stripTrailingCommas = (code) => scanOutsideStrings(code, (src, i) => {
|
|
50
|
+
const isComma = src[i] === ',';
|
|
51
|
+
const nextCode = isComma ? src.slice(i + 1).trimStart()[0] : '';
|
|
52
|
+
const isTrailing = nextCode === '}' || nextCode === ']';
|
|
53
|
+
return { emit: isTrailing ? '' : src[i], skip: 0 };
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/** Returns [value, error, { hasComments }]. Never throws. */
|
|
57
|
+
export const parseJsonc = (text = '') => {
|
|
58
|
+
const { code, hasComments } = stripComments(String(text));
|
|
59
|
+
try {
|
|
60
|
+
return [JSON.parse(stripTrailingCommas(code)), null, { hasComments }];
|
|
61
|
+
} catch (err) {
|
|
62
|
+
return [null, err instanceof Error ? err : new Error(String(err)), { hasComments }];
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds scripts to a consumer package.json without reformatting it or dropping content:
|
|
3
|
+
* - a one-line (minified) file stays one line; otherwise its own indent is kept;
|
|
4
|
+
* - a file with comments or that is not a JSON object is refused and left byte-identical;
|
|
5
|
+
* - only missing script names are added, existing ones are never changed.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import { parseJsonc } from './installer-jsonc.js';
|
|
9
|
+
import { writeFileSafely } from './installer-write.js';
|
|
10
|
+
|
|
11
|
+
const isPlainObject = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
|
|
13
|
+
const detectIndent = (text) => (text.match(/^[ \t]+(?=")/m) || [' '])[0];
|
|
14
|
+
|
|
15
|
+
/** Serializes value in the layout of the original text. */
|
|
16
|
+
const serializeLike = (text, value) => {
|
|
17
|
+
const isSingleLine = !text.trim().includes('\n');
|
|
18
|
+
const body = isSingleLine ? JSON.stringify(value) : JSON.stringify(value, null, detectIndent(text));
|
|
19
|
+
const hasTrailingNewline = text.endsWith('\n');
|
|
20
|
+
return hasTrailingNewline ? `${body}\n` : body;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const refusal = (file, reason) => ({ status: 'refused', file, reason, added: [] });
|
|
24
|
+
|
|
25
|
+
/** Reasons a package.json must not be rewritten, or null when it is safe to edit. */
|
|
26
|
+
const findRefusalReason = (pkg, parseError, meta) => {
|
|
27
|
+
const isObject = !parseError && isPlainObject(pkg);
|
|
28
|
+
if (!isObject) return 'package.json is not a JSON object; left untouched';
|
|
29
|
+
if (meta.hasComments) return 'package.json has comments that a rewrite would drop; left untouched';
|
|
30
|
+
const hasInvalidScripts = pkg.scripts !== undefined && !isPlainObject(pkg.scripts);
|
|
31
|
+
if (hasInvalidScripts) return '"scripts" is not an object; left untouched';
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Adds the scripts that pickWanted(existingScripts) names and package.json lacks.
|
|
37
|
+
* Returns { status: 'absent' | 'refused' | 'unchanged' | 'written', file, reason?, added }.
|
|
38
|
+
*/
|
|
39
|
+
export const addPackageScripts = (pkgPath, pickWanted) => {
|
|
40
|
+
const hasPackage = fs.existsSync(pkgPath);
|
|
41
|
+
if (!hasPackage) return { status: 'absent', file: pkgPath, added: [] };
|
|
42
|
+
const text = fs.readFileSync(pkgPath, 'utf-8');
|
|
43
|
+
const [pkg, parseError, meta] = parseJsonc(text);
|
|
44
|
+
const reason = findRefusalReason(pkg, parseError, meta);
|
|
45
|
+
const isRefused = reason !== null;
|
|
46
|
+
if (isRefused) return refusal(pkgPath, reason);
|
|
47
|
+
const scripts = pkg.scripts || {};
|
|
48
|
+
const missing = Object.entries(pickWanted(scripts)).filter(([name]) => !scripts[name]);
|
|
49
|
+
const hasNothingToAdd = missing.length === 0;
|
|
50
|
+
if (hasNothingToAdd) return { status: 'unchanged', file: pkgPath, added: [] };
|
|
51
|
+
const next = { ...pkg, scripts: { ...scripts, ...Object.fromEntries(missing) } };
|
|
52
|
+
const { status } = writeFileSafely(pkgPath, serializeLike(text, next), { backup: false });
|
|
53
|
+
return { status, file: pkgPath, added: missing.map(([name]) => name) };
|
|
54
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Status predicates and console reporting for the MCP config installer. */
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const isRefusedEntry = (result) => result.status === 'refused';
|
|
5
|
+
export const isSkippedEntry = (result) => result.status === 'skipped';
|
|
6
|
+
export const isWrittenEntry = (result) => result.status === 'written';
|
|
7
|
+
export const isAppliedEntry = (result) => Boolean(result) && !isRefusedEntry(result) && !isSkippedEntry(result);
|
|
8
|
+
|
|
9
|
+
const displayPath = (file) => path.basename(path.dirname(file)) + '/' + path.basename(file);
|
|
10
|
+
|
|
11
|
+
export const reportEntry = (result, label, isSilent) => {
|
|
12
|
+
if (isSilent) return;
|
|
13
|
+
const rel = displayPath(result.file);
|
|
14
|
+
const isNotApplied = isRefusedEntry(result) || isSkippedEntry(result);
|
|
15
|
+
if (isNotApplied) { process.stderr.write(` \x1b[33m⚠\x1b[0m Skipped ${label} (${rel}): ${result.reason}\n`); return; }
|
|
16
|
+
const verb = result.status === 'unchanged' ? 'Already configured' : 'Configured';
|
|
17
|
+
process.stdout.write(` \x1b[32m✔\x1b[0m ${verb} ${label} MCP server in: ${rel}\n`);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const reportPackageScripts = (result, isSilent) => {
|
|
21
|
+
const isWritten = isWrittenEntry(result);
|
|
22
|
+
const shouldAnnounce = isWritten && !isSilent;
|
|
23
|
+
if (shouldAnnounce) process.stdout.write(' \x1b[32m✔\x1b[0m Added chemx verification & MCP scripts to package.json\n');
|
|
24
|
+
const shouldWarn = isRefusedEntry(result) && !isSilent;
|
|
25
|
+
if (shouldWarn) process.stderr.write(` \x1b[33m⚠\x1b[0m Skipped package.json scripts: ${result.reason}\n`);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Path relative to the project, or absolute for files outside it (the home directory). */
|
|
29
|
+
const projectPath = (base, file) => {
|
|
30
|
+
const rel = path.relative(base, file);
|
|
31
|
+
const isOutsideProject = rel.startsWith('..') || path.isAbsolute(rel);
|
|
32
|
+
return isOutsideProject ? file : rel;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const summarizeInstall = (base, { refused, skipped, written }) => {
|
|
36
|
+
const hasRefusals = refused.length > 0;
|
|
37
|
+
const hasPartialApply = hasRefusals && written.length > 0;
|
|
38
|
+
if (hasPartialApply) {
|
|
39
|
+
const wrote = written.map((file) => projectPath(base, file)).join(', ');
|
|
40
|
+
const left = refused.map((r) => projectPath(base, r.file)).join(', ');
|
|
41
|
+
return `\x1b[33m⚠ Partial apply: wrote ${wrote}; refused ${left} (see above).\x1b[0m`;
|
|
42
|
+
}
|
|
43
|
+
if (hasRefusals) return '\x1b[33m⚠ Chemical X MCP server not configured in refused files (see above).\x1b[0m';
|
|
44
|
+
const hasSkips = skipped.length > 0;
|
|
45
|
+
if (hasSkips) return '\x1b[33m⚠ Chemical X MCP server configured, but some requested targets were skipped (see above).\x1b[0m';
|
|
46
|
+
return '\x1b[1m\x1b[32m✔ Chemical X MCP server configured.\x1b[0m';
|
|
47
|
+
};
|