contextpro-mcp 0.0.0-stage → 1.4.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/CHANGELOG.md +416 -0
- package/INSTALL.md +128 -0
- package/INSTRUCTIONS.md +33 -0
- package/LICENSE +21 -0
- package/NOTICE +12 -0
- package/README.md +164 -2
- package/SECURITY.md +27 -0
- package/agent-guide.md +323 -0
- package/agent-instructions.md +1 -0
- package/build/core/agent-setup.d.ts +5 -0
- package/build/core/agent-setup.d.ts.map +1 -0
- package/build/core/agent-setup.js +94 -0
- package/build/core/agent-setup.js.map +1 -0
- package/build/core/artifacts.d.ts +64 -0
- package/build/core/artifacts.d.ts.map +1 -0
- package/build/core/artifacts.js +370 -0
- package/build/core/artifacts.js.map +1 -0
- package/build/core/bounded-source.d.ts +15 -0
- package/build/core/bounded-source.d.ts.map +1 -0
- package/build/core/bounded-source.js +78 -0
- package/build/core/bounded-source.js.map +1 -0
- package/build/core/changed-since.d.ts +20 -0
- package/build/core/changed-since.d.ts.map +1 -0
- package/build/core/changed-since.js +43 -0
- package/build/core/changed-since.js.map +1 -0
- package/build/core/chat-model.d.ts +8 -0
- package/build/core/chat-model.d.ts.map +1 -0
- package/build/core/chat-model.js +135 -0
- package/build/core/chat-model.js.map +1 -0
- package/build/core/clustering.d.ts +6 -0
- package/build/core/clustering.d.ts.map +1 -0
- package/build/core/clustering.js +201 -0
- package/build/core/clustering.js.map +1 -0
- package/build/core/context-metadata.d.ts +50 -0
- package/build/core/context-metadata.d.ts.map +1 -0
- package/build/core/context-metadata.js +118 -0
- package/build/core/context-metadata.js.map +1 -0
- package/build/core/data-dir.d.ts +5 -0
- package/build/core/data-dir.d.ts.map +1 -0
- package/build/core/data-dir.js +53 -0
- package/build/core/data-dir.js.map +1 -0
- package/build/core/db-native.d.ts +28 -0
- package/build/core/db-native.d.ts.map +1 -0
- package/build/core/db-native.js +120 -0
- package/build/core/db-native.js.map +1 -0
- package/build/core/db.d.ts +294 -0
- package/build/core/db.d.ts.map +1 -0
- package/build/core/db.js +1047 -0
- package/build/core/db.js.map +1 -0
- package/build/core/doctor.d.ts +13 -0
- package/build/core/doctor.d.ts.map +1 -0
- package/build/core/doctor.js +55 -0
- package/build/core/doctor.js.map +1 -0
- package/build/core/efficiency.d.ts +16 -0
- package/build/core/efficiency.d.ts.map +1 -0
- package/build/core/efficiency.js +170 -0
- package/build/core/efficiency.js.map +1 -0
- package/build/core/embedding-tracker.d.ts +25 -0
- package/build/core/embedding-tracker.d.ts.map +1 -0
- package/build/core/embedding-tracker.js +228 -0
- package/build/core/embedding-tracker.js.map +1 -0
- package/build/core/embeddings.d.ts +90 -0
- package/build/core/embeddings.d.ts.map +1 -0
- package/build/core/embeddings.js +693 -0
- package/build/core/embeddings.js.map +1 -0
- package/build/core/evidence-state.d.ts +42 -0
- package/build/core/evidence-state.d.ts.map +1 -0
- package/build/core/evidence-state.js +153 -0
- package/build/core/evidence-state.js.map +1 -0
- package/build/core/extractor-stamp.d.ts +10 -0
- package/build/core/extractor-stamp.d.ts.map +1 -0
- package/build/core/extractor-stamp.js +51 -0
- package/build/core/extractor-stamp.js.map +1 -0
- package/build/core/feature-brief-cache.d.ts +37 -0
- package/build/core/feature-brief-cache.d.ts.map +1 -0
- package/build/core/feature-brief-cache.js +100 -0
- package/build/core/feature-brief-cache.js.map +1 -0
- package/build/core/file-fingerprint.d.ts +15 -0
- package/build/core/file-fingerprint.d.ts.map +1 -0
- package/build/core/file-fingerprint.js +24 -0
- package/build/core/file-fingerprint.js.map +1 -0
- package/build/core/graph-analytics.d.ts +46 -0
- package/build/core/graph-analytics.d.ts.map +1 -0
- package/build/core/graph-analytics.js +371 -0
- package/build/core/graph-analytics.js.map +1 -0
- package/build/core/hub.d.ts +23 -0
- package/build/core/hub.d.ts.map +1 -0
- package/build/core/hub.js +123 -0
- package/build/core/hub.js.map +1 -0
- package/build/core/ignore-dirs.d.ts +7 -0
- package/build/core/ignore-dirs.d.ts.map +1 -0
- package/build/core/ignore-dirs.js +84 -0
- package/build/core/ignore-dirs.js.map +1 -0
- package/build/core/index-freshness.d.ts +15 -0
- package/build/core/index-freshness.d.ts.map +1 -0
- package/build/core/index-freshness.js +96 -0
- package/build/core/index-freshness.js.map +1 -0
- package/build/core/index-reconcile.d.ts +28 -0
- package/build/core/index-reconcile.d.ts.map +1 -0
- package/build/core/index-reconcile.js +129 -0
- package/build/core/index-reconcile.js.map +1 -0
- package/build/core/local-daemon.d.ts +5 -0
- package/build/core/local-daemon.d.ts.map +1 -0
- package/build/core/local-daemon.js +282 -0
- package/build/core/local-daemon.js.map +1 -0
- package/build/core/memory-graph.d.ts +68 -0
- package/build/core/memory-graph.d.ts.map +1 -0
- package/build/core/memory-graph.js +152 -0
- package/build/core/memory-graph.js.map +1 -0
- package/build/core/native-repair.d.ts +12 -0
- package/build/core/native-repair.d.ts.map +1 -0
- package/build/core/native-repair.js +167 -0
- package/build/core/native-repair.js.map +1 -0
- package/build/core/native-runtime.d.ts +12 -0
- package/build/core/native-runtime.d.ts.map +1 -0
- package/build/core/native-runtime.js +38 -0
- package/build/core/native-runtime.js.map +1 -0
- package/build/core/pagerank.d.ts +29 -0
- package/build/core/pagerank.d.ts.map +1 -0
- package/build/core/pagerank.js +90 -0
- package/build/core/pagerank.js.map +1 -0
- package/build/core/parser-process.d.ts +21 -0
- package/build/core/parser-process.d.ts.map +1 -0
- package/build/core/parser-process.js +103 -0
- package/build/core/parser-process.js.map +1 -0
- package/build/core/parser-worker.d.ts +2 -0
- package/build/core/parser-worker.d.ts.map +1 -0
- package/build/core/parser-worker.js +17 -0
- package/build/core/parser-worker.js.map +1 -0
- package/build/core/parser.d.ts +66 -0
- package/build/core/parser.d.ts.map +1 -0
- package/build/core/parser.js +750 -0
- package/build/core/parser.js.map +1 -0
- package/build/core/path-guard.d.ts +6 -0
- package/build/core/path-guard.d.ts.map +1 -0
- package/build/core/path-guard.js +55 -0
- package/build/core/path-guard.js.map +1 -0
- package/build/core/process-lifecycle.d.ts +30 -0
- package/build/core/process-lifecycle.d.ts.map +1 -0
- package/build/core/process-lifecycle.js +129 -0
- package/build/core/process-lifecycle.js.map +1 -0
- package/build/core/project-config.d.ts +83 -0
- package/build/core/project-config.d.ts.map +1 -0
- package/build/core/project-config.js +302 -0
- package/build/core/project-config.js.map +1 -0
- package/build/core/project-graph.d.ts +69 -0
- package/build/core/project-graph.d.ts.map +1 -0
- package/build/core/project-graph.js +874 -0
- package/build/core/project-graph.js.map +1 -0
- package/build/core/provider-config.d.ts +15 -0
- package/build/core/provider-config.d.ts.map +1 -0
- package/build/core/provider-config.js +67 -0
- package/build/core/provider-config.js.map +1 -0
- package/build/core/provider-usage.d.ts +21 -0
- package/build/core/provider-usage.d.ts.map +1 -0
- package/build/core/provider-usage.js +52 -0
- package/build/core/provider-usage.js.map +1 -0
- package/build/core/python-language-server.d.ts +23 -0
- package/build/core/python-language-server.d.ts.map +1 -0
- package/build/core/python-language-server.js +217 -0
- package/build/core/python-language-server.js.map +1 -0
- package/build/core/python-runtime-identity.d.ts +3 -0
- package/build/core/python-runtime-identity.d.ts.map +1 -0
- package/build/core/python-runtime-identity.js +53 -0
- package/build/core/python-runtime-identity.js.map +1 -0
- package/build/core/reconcile-scheduler.d.ts +20 -0
- package/build/core/reconcile-scheduler.d.ts.map +1 -0
- package/build/core/reconcile-scheduler.js +58 -0
- package/build/core/reconcile-scheduler.js.map +1 -0
- package/build/core/request-context.d.ts +7 -0
- package/build/core/request-context.d.ts.map +1 -0
- package/build/core/request-context.js +17 -0
- package/build/core/request-context.js.map +1 -0
- package/build/core/request-root.d.ts +19 -0
- package/build/core/request-root.d.ts.map +1 -0
- package/build/core/request-root.js +26 -0
- package/build/core/request-root.js.map +1 -0
- package/build/core/root-guard.d.ts +8 -0
- package/build/core/root-guard.d.ts.map +1 -0
- package/build/core/root-guard.js +53 -0
- package/build/core/root-guard.js.map +1 -0
- package/build/core/roots-detect.d.ts +17 -0
- package/build/core/roots-detect.d.ts.map +1 -0
- package/build/core/roots-detect.js +63 -0
- package/build/core/roots-detect.js.map +1 -0
- package/build/core/runtime-session.d.ts +5 -0
- package/build/core/runtime-session.d.ts.map +1 -0
- package/build/core/runtime-session.js +10 -0
- package/build/core/runtime-session.js.map +1 -0
- package/build/core/scip-index.d.ts +71 -0
- package/build/core/scip-index.d.ts.map +1 -0
- package/build/core/scip-index.js +343 -0
- package/build/core/scip-index.js.map +1 -0
- package/build/core/semantic-diff.d.ts +32 -0
- package/build/core/semantic-diff.d.ts.map +1 -0
- package/build/core/semantic-diff.js +259 -0
- package/build/core/semantic-diff.js.map +1 -0
- package/build/core/source-context.d.ts +3 -0
- package/build/core/source-context.d.ts.map +1 -0
- package/build/core/source-context.js +86 -0
- package/build/core/source-context.js.map +1 -0
- package/build/core/source-mask.d.ts +8 -0
- package/build/core/source-mask.d.ts.map +1 -0
- package/build/core/source-mask.js +156 -0
- package/build/core/source-mask.js.map +1 -0
- package/build/core/source-mutation.d.ts +16 -0
- package/build/core/source-mutation.d.ts.map +1 -0
- package/build/core/source-mutation.js +193 -0
- package/build/core/source-mutation.js.map +1 -0
- package/build/core/sql-schema.d.ts +59 -0
- package/build/core/sql-schema.d.ts.map +1 -0
- package/build/core/sql-schema.js +218 -0
- package/build/core/sql-schema.js.map +1 -0
- package/build/core/startup-health.d.ts +40 -0
- package/build/core/startup-health.d.ts.map +1 -0
- package/build/core/startup-health.js +132 -0
- package/build/core/startup-health.js.map +1 -0
- package/build/core/team-ledger.d.ts +60 -0
- package/build/core/team-ledger.d.ts.map +1 -0
- package/build/core/team-ledger.js +171 -0
- package/build/core/team-ledger.js.map +1 -0
- package/build/core/test-impact.d.ts +23 -0
- package/build/core/test-impact.d.ts.map +1 -0
- package/build/core/test-impact.js +131 -0
- package/build/core/test-impact.js.map +1 -0
- package/build/core/tool-metrics.d.ts +29 -0
- package/build/core/tool-metrics.d.ts.map +1 -0
- package/build/core/tool-metrics.js +74 -0
- package/build/core/tool-metrics.js.map +1 -0
- package/build/core/tool-profile.d.ts +12 -0
- package/build/core/tool-profile.d.ts.map +1 -0
- package/build/core/tool-profile.js +102 -0
- package/build/core/tool-profile.js.map +1 -0
- package/build/core/tool-trace.d.ts +8 -0
- package/build/core/tool-trace.d.ts.map +1 -0
- package/build/core/tool-trace.js +70 -0
- package/build/core/tool-trace.js.map +1 -0
- package/build/core/tree-sitter-runtime.d.ts +20 -0
- package/build/core/tree-sitter-runtime.d.ts.map +1 -0
- package/build/core/tree-sitter-runtime.js +324 -0
- package/build/core/tree-sitter-runtime.js.map +1 -0
- package/build/core/tree-sitter.d.ts +6 -0
- package/build/core/tree-sitter.d.ts.map +1 -0
- package/build/core/tree-sitter.js +24 -0
- package/build/core/tree-sitter.js.map +1 -0
- package/build/core/typescript-reference-process.d.ts +30 -0
- package/build/core/typescript-reference-process.d.ts.map +1 -0
- package/build/core/typescript-reference-process.js +124 -0
- package/build/core/typescript-reference-process.js.map +1 -0
- package/build/core/typescript-reference-worker.d.ts +2 -0
- package/build/core/typescript-reference-worker.d.ts.map +1 -0
- package/build/core/typescript-reference-worker.js +14 -0
- package/build/core/typescript-reference-worker.js.map +1 -0
- package/build/core/typescript-references.d.ts +71 -0
- package/build/core/typescript-references.d.ts.map +1 -0
- package/build/core/typescript-references.js +392 -0
- package/build/core/typescript-references.js.map +1 -0
- package/build/core/typescript-skeleton.d.ts +5 -0
- package/build/core/typescript-skeleton.d.ts.map +1 -0
- package/build/core/typescript-skeleton.js +49 -0
- package/build/core/typescript-skeleton.js.map +1 -0
- package/build/core/walker.d.ts +16 -0
- package/build/core/walker.d.ts.map +1 -0
- package/build/core/walker.js +152 -0
- package/build/core/walker.js.map +1 -0
- package/build/core/workspace-router.d.ts +28 -0
- package/build/core/workspace-router.d.ts.map +1 -0
- package/build/core/workspace-router.js +93 -0
- package/build/core/workspace-router.js.map +1 -0
- package/build/core/worktree-gc.d.ts +16 -0
- package/build/core/worktree-gc.d.ts.map +1 -0
- package/build/core/worktree-gc.js +69 -0
- package/build/core/worktree-gc.js.map +1 -0
- package/build/core/worktree-overlay.d.ts +22 -0
- package/build/core/worktree-overlay.d.ts.map +1 -0
- package/build/core/worktree-overlay.js +84 -0
- package/build/core/worktree-overlay.js.map +1 -0
- package/build/core/worktree-root-guard.d.ts +8 -0
- package/build/core/worktree-root-guard.d.ts.map +1 -0
- package/build/core/worktree-root-guard.js +82 -0
- package/build/core/worktree-root-guard.js.map +1 -0
- package/build/core/worktree-seed.d.ts +14 -0
- package/build/core/worktree-seed.d.ts.map +1 -0
- package/build/core/worktree-seed.js +98 -0
- package/build/core/worktree-seed.js.map +1 -0
- package/build/git/shadow.d.ts +40 -0
- package/build/git/shadow.d.ts.map +1 -0
- package/build/git/shadow.js +298 -0
- package/build/git/shadow.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +599 -0
- package/build/index.js.map +1 -0
- package/build/tools/agentic-context.d.ts +86 -0
- package/build/tools/agentic-context.d.ts.map +1 -0
- package/build/tools/agentic-context.js +1546 -0
- package/build/tools/agentic-context.js.map +1 -0
- package/build/tools/blast-radius.d.ts +24 -0
- package/build/tools/blast-radius.d.ts.map +1 -0
- package/build/tools/blast-radius.js +231 -0
- package/build/tools/blast-radius.js.map +1 -0
- package/build/tools/changed-since.d.ts +7 -0
- package/build/tools/changed-since.d.ts.map +1 -0
- package/build/tools/changed-since.js +68 -0
- package/build/tools/changed-since.js.map +1 -0
- package/build/tools/compiler-actions.d.ts +26 -0
- package/build/tools/compiler-actions.d.ts.map +1 -0
- package/build/tools/compiler-actions.js +121 -0
- package/build/tools/compiler-actions.js.map +1 -0
- package/build/tools/context-tree.d.ts +9 -0
- package/build/tools/context-tree.d.ts.map +1 -0
- package/build/tools/context-tree.js +131 -0
- package/build/tools/context-tree.js.map +1 -0
- package/build/tools/context.d.ts +37 -0
- package/build/tools/context.d.ts.map +1 -0
- package/build/tools/context.js +800 -0
- package/build/tools/context.js.map +1 -0
- package/build/tools/diff.d.ts +9 -0
- package/build/tools/diff.d.ts.map +1 -0
- package/build/tools/diff.js +176 -0
- package/build/tools/diff.js.map +1 -0
- package/build/tools/directory-prior.d.ts +16 -0
- package/build/tools/directory-prior.d.ts.map +1 -0
- package/build/tools/directory-prior.js +121 -0
- package/build/tools/directory-prior.js.map +1 -0
- package/build/tools/discovery-documents.d.ts +11 -0
- package/build/tools/discovery-documents.d.ts.map +1 -0
- package/build/tools/discovery-documents.js +71 -0
- package/build/tools/discovery-documents.js.map +1 -0
- package/build/tools/discovery-hints.d.ts +25 -0
- package/build/tools/discovery-hints.d.ts.map +1 -0
- package/build/tools/discovery-hints.js +329 -0
- package/build/tools/discovery-hints.js.map +1 -0
- package/build/tools/efficiency-report.d.ts +13 -0
- package/build/tools/efficiency-report.d.ts.map +1 -0
- package/build/tools/efficiency-report.js +78 -0
- package/build/tools/efficiency-report.js.map +1 -0
- package/build/tools/evidence-retrieval.d.ts +33 -0
- package/build/tools/evidence-retrieval.d.ts.map +1 -0
- package/build/tools/evidence-retrieval.js +200 -0
- package/build/tools/evidence-retrieval.js.map +1 -0
- package/build/tools/exact-references.d.ts +10 -0
- package/build/tools/exact-references.d.ts.map +1 -0
- package/build/tools/exact-references.js +349 -0
- package/build/tools/exact-references.js.map +1 -0
- package/build/tools/explain.d.ts +14 -0
- package/build/tools/explain.d.ts.map +1 -0
- package/build/tools/explain.js +370 -0
- package/build/tools/explain.js.map +1 -0
- package/build/tools/feature-brief.d.ts +7 -0
- package/build/tools/feature-brief.d.ts.map +1 -0
- package/build/tools/feature-brief.js +89 -0
- package/build/tools/feature-brief.js.map +1 -0
- package/build/tools/feature-hub.d.ts +15 -0
- package/build/tools/feature-hub.d.ts.map +1 -0
- package/build/tools/feature-hub.js +404 -0
- package/build/tools/feature-hub.js.map +1 -0
- package/build/tools/file-skeleton.d.ts +7 -0
- package/build/tools/file-skeleton.d.ts.map +1 -0
- package/build/tools/file-skeleton.js +190 -0
- package/build/tools/file-skeleton.js.map +1 -0
- package/build/tools/grep.d.ts +25 -0
- package/build/tools/grep.d.ts.map +1 -0
- package/build/tools/grep.js +341 -0
- package/build/tools/grep.js.map +1 -0
- package/build/tools/impact-delta.d.ts +7 -0
- package/build/tools/impact-delta.d.ts.map +1 -0
- package/build/tools/impact-delta.js +102 -0
- package/build/tools/impact-delta.js.map +1 -0
- package/build/tools/memory-tools.d.ts +69 -0
- package/build/tools/memory-tools.d.ts.map +1 -0
- package/build/tools/memory-tools.js +366 -0
- package/build/tools/memory-tools.js.map +1 -0
- package/build/tools/planning-helpers.d.ts +26 -0
- package/build/tools/planning-helpers.d.ts.map +1 -0
- package/build/tools/planning-helpers.js +269 -0
- package/build/tools/planning-helpers.js.map +1 -0
- package/build/tools/project-graph.d.ts +19 -0
- package/build/tools/project-graph.d.ts.map +1 -0
- package/build/tools/project-graph.js +259 -0
- package/build/tools/project-graph.js.map +1 -0
- package/build/tools/propose-commit.d.ts +9 -0
- package/build/tools/propose-commit.d.ts.map +1 -0
- package/build/tools/propose-commit.js +156 -0
- package/build/tools/propose-commit.js.map +1 -0
- package/build/tools/register-tools.d.ts +18 -0
- package/build/tools/register-tools.d.ts.map +1 -0
- package/build/tools/register-tools.js +1131 -0
- package/build/tools/register-tools.js.map +1 -0
- package/build/tools/reindex.d.ts +76 -0
- package/build/tools/reindex.d.ts.map +1 -0
- package/build/tools/reindex.js +871 -0
- package/build/tools/reindex.js.map +1 -0
- package/build/tools/related.d.ts +6 -0
- package/build/tools/related.d.ts.map +1 -0
- package/build/tools/related.js +143 -0
- package/build/tools/related.js.map +1 -0
- package/build/tools/repo-map.d.ts +59 -0
- package/build/tools/repo-map.d.ts.map +1 -0
- package/build/tools/repo-map.js +270 -0
- package/build/tools/repo-map.js.map +1 -0
- package/build/tools/resolve-symbol.d.ts +41 -0
- package/build/tools/resolve-symbol.d.ts.map +1 -0
- package/build/tools/resolve-symbol.js +250 -0
- package/build/tools/resolve-symbol.js.map +1 -0
- package/build/tools/retrieval-helpers.d.ts +35 -0
- package/build/tools/retrieval-helpers.d.ts.map +1 -0
- package/build/tools/retrieval-helpers.js +302 -0
- package/build/tools/retrieval-helpers.js.map +1 -0
- package/build/tools/scip-index.d.ts +12 -0
- package/build/tools/scip-index.d.ts.map +1 -0
- package/build/tools/scip-index.js +66 -0
- package/build/tools/scip-index.js.map +1 -0
- package/build/tools/scoped-discovery.d.ts +7 -0
- package/build/tools/scoped-discovery.d.ts.map +1 -0
- package/build/tools/scoped-discovery.js +89 -0
- package/build/tools/scoped-discovery.js.map +1 -0
- package/build/tools/search.d.ts +62 -0
- package/build/tools/search.d.ts.map +1 -0
- package/build/tools/search.js +419 -0
- package/build/tools/search.js.map +1 -0
- package/build/tools/semantic-diff.d.ts +7 -0
- package/build/tools/semantic-diff.d.ts.map +1 -0
- package/build/tools/semantic-diff.js +45 -0
- package/build/tools/semantic-diff.js.map +1 -0
- package/build/tools/semantic-identifiers.d.ts +51 -0
- package/build/tools/semantic-identifiers.d.ts.map +1 -0
- package/build/tools/semantic-identifiers.js +522 -0
- package/build/tools/semantic-identifiers.js.map +1 -0
- package/build/tools/semantic-navigate.d.ts +18 -0
- package/build/tools/semantic-navigate.d.ts.map +1 -0
- package/build/tools/semantic-navigate.js +413 -0
- package/build/tools/semantic-navigate.js.map +1 -0
- package/build/tools/semantic-search.d.ts +30 -0
- package/build/tools/semantic-search.d.ts.map +1 -0
- package/build/tools/semantic-search.js +466 -0
- package/build/tools/semantic-search.js.map +1 -0
- package/build/tools/sql-schema.d.ts +23 -0
- package/build/tools/sql-schema.d.ts.map +1 -0
- package/build/tools/sql-schema.js +155 -0
- package/build/tools/sql-schema.js.map +1 -0
- package/build/tools/static-analysis.d.ts +7 -0
- package/build/tools/static-analysis.d.ts.map +1 -0
- package/build/tools/static-analysis.js +331 -0
- package/build/tools/static-analysis.js.map +1 -0
- package/build/tools/status.d.ts +74 -0
- package/build/tools/status.d.ts.map +1 -0
- package/build/tools/status.js +227 -0
- package/build/tools/status.js.map +1 -0
- package/build/tools/symbolic-edit.d.ts +57 -0
- package/build/tools/symbolic-edit.d.ts.map +1 -0
- package/build/tools/symbolic-edit.js +500 -0
- package/build/tools/symbolic-edit.js.map +1 -0
- package/build/tools/test-impact.d.ts +7 -0
- package/build/tools/test-impact.d.ts.map +1 -0
- package/build/tools/test-impact.js +34 -0
- package/build/tools/test-impact.js.map +1 -0
- package/build/tools/test-map.d.ts +8 -0
- package/build/tools/test-map.d.ts.map +1 -0
- package/build/tools/test-map.js +75 -0
- package/build/tools/test-map.js.map +1 -0
- package/docs/README.md +26 -0
- package/docs/architecture.md +35 -0
- package/docs/configuration.md +178 -0
- package/docs/maintenance.md +77 -0
- package/package.json +80 -4
|
@@ -0,0 +1,1131 @@
|
|
|
1
|
+
// MCP tool registration — wires all server.tool() calls to their handlers
|
|
2
|
+
// FEATURE: Tool registration layer extracted from index.ts for readability
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
5
|
+
import { runtimeSessionId, runtimeSessionStartedAt, setToolSchemaTokens } from "../core/runtime-session.js";
|
|
6
|
+
import { getContextTree } from "./context-tree.js";
|
|
7
|
+
import { getFileSkeleton } from "./file-skeleton.js";
|
|
8
|
+
import { unifiedSearch, invalidateSearchCache, invalidateIdentifierSearchCache } from "./search.js";
|
|
9
|
+
import { getBlastRadius } from "./blast-radius.js";
|
|
10
|
+
import { runStaticAnalysis } from "./static-analysis.js";
|
|
11
|
+
import { proposeCommit } from "./propose-commit.js";
|
|
12
|
+
import { listRestorePoints, restorePoint, currentSessionId, garbageCollectRestorePoints } from "../git/shadow.js";
|
|
13
|
+
import { semanticNavigate } from "./semantic-navigate.js";
|
|
14
|
+
import { getFeatureHub } from "./feature-hub.js";
|
|
15
|
+
import { toolUpsertMemoryNode, toolCreateRelation, toolSearchMemoryGraph, toolAddInterlinkedContext, toolRetrieveWithTraversal, toolDeleteMemoryNode, toolListMemoryNodes } from "./memory-tools.js";
|
|
16
|
+
import { reindex } from "./reindex.js";
|
|
17
|
+
import { getStatus } from "./status.js";
|
|
18
|
+
import { getDiff } from "./diff.js";
|
|
19
|
+
import { getSemanticDiff } from "./semantic-diff.js";
|
|
20
|
+
import { getSqlSchema } from "./sql-schema.js";
|
|
21
|
+
import { getTestImpact } from "./test-impact.js";
|
|
22
|
+
import { getChangedSince } from "./changed-since.js";
|
|
23
|
+
import { getRelated } from "./related.js";
|
|
24
|
+
import { getTestMap } from "./test-map.js";
|
|
25
|
+
import { explain } from "./explain.js";
|
|
26
|
+
import { getContext } from "./context.js";
|
|
27
|
+
import { recordValidation, validationState, memoryEvidenceStatus } from "../core/evidence-state.js";
|
|
28
|
+
import { getDB } from "../core/db.js";
|
|
29
|
+
import { teamStatus, teamTask } from "../core/team-ledger.js";
|
|
30
|
+
import { compilerDiagnostics, previewRename, applyRename } from "./compiler-actions.js";
|
|
31
|
+
import { getFeatureBrief } from "./feature-brief.js";
|
|
32
|
+
import { getRepoMap } from "./repo-map.js";
|
|
33
|
+
import { resolveSymbol } from "./resolve-symbol.js";
|
|
34
|
+
import { grepProject } from "./grep.js";
|
|
35
|
+
import { exportProjectGraphTool, queryProjectGraphTool } from "./project-graph.js";
|
|
36
|
+
import { impactDeltaTool } from "./impact-delta.js";
|
|
37
|
+
import { exactReferencesTool } from "./exact-references.js";
|
|
38
|
+
import { scipImportTool, scipStatusTool } from "./scip-index.js";
|
|
39
|
+
import { buildTaskPack, buildChangeImpact, buildEditScope, buildDecisionBrief, buildValidateScope, recordTaskMemory, getWorkspaceContext, promotePattern } from "./agentic-context.js";
|
|
40
|
+
import { findSymbol, replaceSymbolBody, insertAroundSymbol, renameSymbol } from "./symbolic-edit.js";
|
|
41
|
+
import { listArtifacts, readArtifact, garbageCollectArtifacts } from "../core/artifacts.js";
|
|
42
|
+
import { formatStartupHealthReport, runStartupHealthCheck } from "../core/startup-health.js";
|
|
43
|
+
import { currentRoot } from "../core/request-context.js";
|
|
44
|
+
import { annotationsForTool, isToolEnabled, resolveToolProfile } from "../core/tool-profile.js";
|
|
45
|
+
import { garbageCollectDeadWorktrees } from "../core/worktree-gc.js";
|
|
46
|
+
import { getEfficiencyReport } from "./efficiency-report.js";
|
|
47
|
+
const MAX_ARTIFACT_LIST_KEY_CHARS = 240;
|
|
48
|
+
/** Keep discovery output bounded when a cache key contains a full user task. */
|
|
49
|
+
export function compactArtifactListKeys(artifacts) {
|
|
50
|
+
return artifacts.map((artifact) => {
|
|
51
|
+
if (artifact.key.length <= MAX_ARTIFACT_LIST_KEY_CHARS)
|
|
52
|
+
return artifact;
|
|
53
|
+
return {
|
|
54
|
+
...artifact,
|
|
55
|
+
key: `${artifact.key.slice(0, MAX_ARTIFACT_LIST_KEY_CHARS - 3)}...`,
|
|
56
|
+
keyTruncated: true,
|
|
57
|
+
keyLength: artifact.key.length,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** Optional per-request root override, injected into every tool schema. */
|
|
62
|
+
const rootArg = z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Absolute project root. Required with linked worktrees; subagents must pass their own worktree root on every call.");
|
|
66
|
+
export function registerTools(serverInput, bootRoot, withRequestActivity) {
|
|
67
|
+
// Per-request effective root, evaluated at handler execution time.
|
|
68
|
+
// withRequestActivity establishes the resolved worktree root via
|
|
69
|
+
// AsyncLocalStorage for the duration of each handler; outside a request
|
|
70
|
+
// (CLI/direct callers) this falls back to the server's boot root.
|
|
71
|
+
const rootDir = () => currentRoot(bootRoot);
|
|
72
|
+
// Wrap server.tool so every registered tool automatically accepts the optional
|
|
73
|
+
// `root` argument without repeating it across 50+ schemas. The wrapper merges
|
|
74
|
+
// `root: rootArg` into each tool's Zod shape; withRequestActivity reads and
|
|
75
|
+
// strips it before the handler runs.
|
|
76
|
+
const toolProfile = resolveToolProfile(process.env.CONTEXTPRO_TOOL_PROFILE ?? process.env.CONTEXTPLUS_TOOL_PROFILE);
|
|
77
|
+
const sessionStartedAt = runtimeSessionStartedAt;
|
|
78
|
+
const catalog = new Map();
|
|
79
|
+
const active = new Set();
|
|
80
|
+
const handles = new Map();
|
|
81
|
+
const invocations = new Map();
|
|
82
|
+
// This compatibility route must never dispatch source edits or destructive
|
|
83
|
+
// maintenance under discovery's non-destructive annotation.
|
|
84
|
+
const callable = new Set(["context", "exact_references", "search", "grep", "skeleton", "explain", "tree", "status", "health", "reindex", "blast_radius", "validate_scope", "efficiency_report", "task_pack", "repo_map", "feature_brief", "artifact_read", "team_status", "compiler_diagnostics", "rename_preview"]);
|
|
85
|
+
const refreshCatalogCost = () => setToolSchemaTokens(Buffer.byteLength(JSON.stringify({ tools: [...catalog.values()].filter(tool => active.has(tool.name)) }), "utf8") / 4);
|
|
86
|
+
const noteSchema = (name, description, shape, output) => {
|
|
87
|
+
const entry = { name, description, inputSchema: toJsonSchemaCompat(z.object({ ...shape, root: rootArg }).strict()), annotations: annotationsForTool(name),
|
|
88
|
+
...(output ? { outputSchema: toJsonSchemaCompat(output) } : {}) };
|
|
89
|
+
catalog.set(name, entry);
|
|
90
|
+
refreshCatalogCost();
|
|
91
|
+
};
|
|
92
|
+
const server = {
|
|
93
|
+
tool: (name, description, shape, handler) => {
|
|
94
|
+
if (isToolEnabled(name, toolProfile))
|
|
95
|
+
active.add(name);
|
|
96
|
+
noteSchema(name, description, shape);
|
|
97
|
+
const schema = z.object({ ...shape, root: rootArg }).strict();
|
|
98
|
+
invocations.set(name, { schema, handler: handler });
|
|
99
|
+
const registered = serverInput.registerTool(name, { description, inputSchema: schema, annotations: annotationsForTool(name) }, handler);
|
|
100
|
+
if (registered)
|
|
101
|
+
handles.set(name, registered);
|
|
102
|
+
if (!active.has(name))
|
|
103
|
+
registered?.disable?.();
|
|
104
|
+
return registered;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const registerStructuredTool = (name, description, inputSchema, outputSchema, handler) => {
|
|
108
|
+
if (isToolEnabled(name, toolProfile))
|
|
109
|
+
active.add(name);
|
|
110
|
+
noteSchema(name, description, inputSchema, outputSchema);
|
|
111
|
+
const schema = z.object({ ...inputSchema, root: rootArg }).strict();
|
|
112
|
+
invocations.set(name, { schema, handler: handler });
|
|
113
|
+
const registered = serverInput.registerTool(name, {
|
|
114
|
+
description,
|
|
115
|
+
inputSchema: schema,
|
|
116
|
+
outputSchema,
|
|
117
|
+
annotations: annotationsForTool(name),
|
|
118
|
+
}, handler);
|
|
119
|
+
if (registered)
|
|
120
|
+
handles.set(name, registered);
|
|
121
|
+
if (!active.has(name))
|
|
122
|
+
registered?.disable?.();
|
|
123
|
+
return registered;
|
|
124
|
+
};
|
|
125
|
+
server.tool("context", "Read up to six known files, symbols, or line ranges together with targets:[{file,symbol?,start?,end?}], without search or embeddings. Use query for discovery plus source in one call. Returns bounded, deduplicated source with explicit omissions. Pass the absolute project root. Prefer a batch over separate reads when useful; stop when evidence suffices.", {
|
|
126
|
+
intent: z.enum(["auto", "lookup", "change", "debug", "review", "overview"]).optional().describe("Query-only opt-in adaptive retrieval. Change/debug/review include bounded connected evidence; auto infers intent."),
|
|
127
|
+
query: z.string().max(2000).optional().describe("Discovery query, at most 2000 characters. Use a literal symbol name to prioritize its definition."),
|
|
128
|
+
targets: z.array(z.object({ file: z.string().max(240), symbol: z.string().max(200).optional(), start: z.number().int().min(1).optional(), end: z.number().int().min(1).optional() }).strict()).min(1).max(6).optional().describe("Exact reads: 1–6 file/symbol/line targets per call. Split larger batches."),
|
|
129
|
+
max_tokens: z.number().int().min(512).max(12000).optional().describe("Integer response budget: 512–12000, UTF-8 bytes/4; default 4000. Raise within this range or narrow targets if omitted."),
|
|
130
|
+
previous_context_id: z.string().regex(/^source_context-[a-f0-9]{14}$/).optional().describe("Query mode only: source_context ID from a complete packet you still hold. Returns source changes; omit after compaction. Exact targets are stateless."),
|
|
131
|
+
}, withRequestActivity(async ({ query, targets, max_tokens, previous_context_id, intent }) => ({
|
|
132
|
+
content: [{ type: "text", text: await getContext({ rootDir: rootDir(), query, targets, intent, maxTokens: max_tokens, previousContextId: previous_context_id }) }],
|
|
133
|
+
}), { toolName: "context", waitForIndexing: false }));
|
|
134
|
+
server.tool("feature_brief", "Explain connected implementation and tests with source citations. Reuses hash-validated artifacts. Deterministic by default; model summaries require CONTEXTPRO_FEATURE_SUMMARIES=true.", {
|
|
135
|
+
query: z.string().min(1).max(2000).describe("Feature or task to explain, 1–2000 characters."),
|
|
136
|
+
refresh: z.boolean().optional().describe("Recompute the cached brief. Model generation still requires explicit configuration. Default false."),
|
|
137
|
+
max_tokens: z.number().int().min(512).max(12000).optional().describe("Integer response budget 512–12000, UTF-8 bytes/4; default 2000."),
|
|
138
|
+
}, withRequestActivity(async ({ query, refresh, max_tokens }) => ({ content: [{ type: "text",
|
|
139
|
+
text: await getFeatureBrief({ rootDir: rootDir(), query, refresh, maxTokens: max_tokens }) }],
|
|
140
|
+
}), { toolName: "feature_brief", waitForIndexing: false }));
|
|
141
|
+
const discoveryShape = {
|
|
142
|
+
query: z.string().max(500).optional().describe("Purpose, e.g. rename, tests, or memory."),
|
|
143
|
+
enable: z.array(z.string()).max(6).optional().describe("Exact tool names to add to this session's catalog."),
|
|
144
|
+
limit: z.number().int().min(1).max(10).optional().describe("Maximum discovery matches; default 5."),
|
|
145
|
+
call: z.object({ name: z.string(), arguments: z.record(z.string(), z.unknown()).optional() }).strict().optional().describe("If the client cannot refresh tools, execute a supported read/index tool using its discovered schema. Pass root outside call; source edits are excluded."),
|
|
146
|
+
};
|
|
147
|
+
const discover = withRequestActivity(async ({ query, enable, limit }) => {
|
|
148
|
+
const unknown = (enable ?? []).filter(name => !catalog.has(name));
|
|
149
|
+
if (unknown.length)
|
|
150
|
+
return { isError: true, content: [{ type: "text", text: `Unknown tools: ${unknown.join(", ")}. Search by query first.` }] };
|
|
151
|
+
for (const name of enable ?? []) {
|
|
152
|
+
handles.get(name)?.enable?.();
|
|
153
|
+
active.add(name);
|
|
154
|
+
}
|
|
155
|
+
refreshCatalogCost();
|
|
156
|
+
const normalizedQuery = (query ?? "").trim().toLowerCase();
|
|
157
|
+
const names = new Set(normalizedQuery.match(/\w+/g) ?? []);
|
|
158
|
+
const terms = normalizedQuery.split(/[\s_]+/).filter(Boolean);
|
|
159
|
+
const found = [...catalog.values()].map(tool => ({ tool, score: (names.has(tool.name) ? 100 : 0)
|
|
160
|
+
+ terms.reduce((n, term) => n + (tool.name.includes(term) ? 3 : 0) + (tool.description.toLowerCase().includes(term) ? 1 : 0), 0) }))
|
|
161
|
+
.filter(({ score }) => !terms.length || score > 0).sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name))
|
|
162
|
+
.slice(0, limit ?? 5).map(({ tool }) => ({ name: tool.name, description: tool.description.slice(0, 180), enabled: active.has(tool.name) }));
|
|
163
|
+
return { content: [{ type: "text", text: JSON.stringify({ enabled: enable ?? [], matches: found,
|
|
164
|
+
schemas: (enable ?? []).map(name => ({ ...catalog.get(name), callable: callable.has(name) })),
|
|
165
|
+
next: "Call an enabled tool normally. If unavailable in your client, use discover_tools({root,call:{name,arguments}}) for tools marked callable." }) }] };
|
|
166
|
+
}, { toolName: "discover_tools", waitForIndexing: false, skipProjectVerification: true });
|
|
167
|
+
server.tool("discover_tools", "Discover or enable experts and return their schemas. If the client cannot refresh its catalog, call runs a supported read/index tool; never source edits.", discoveryShape, async (args) => {
|
|
168
|
+
if (!args.call)
|
|
169
|
+
return discover(args);
|
|
170
|
+
const fail = (text) => ({ isError: true, content: [{ type: "text", text }] });
|
|
171
|
+
if (args.enable || args.query || args.limit !== undefined)
|
|
172
|
+
return fail("Use call alone with root; discover the schema first.");
|
|
173
|
+
if (!callable.has(args.call.name))
|
|
174
|
+
return fail("This tool cannot be dispatched. Enable it and use a client with tool refresh or the full profile.");
|
|
175
|
+
const invocation = invocations.get(args.call.name);
|
|
176
|
+
if (Object.hasOwn(args.call.arguments ?? {}, "root"))
|
|
177
|
+
return fail("Pass root at the top level, not inside call.arguments.");
|
|
178
|
+
const parsed = invocation.schema.safeParse({ ...args.call.arguments, root: args.root });
|
|
179
|
+
if (!parsed.success)
|
|
180
|
+
return fail(`Invalid arguments for ${args.call.name}: ${parsed.error.message}`);
|
|
181
|
+
// Use the target's normal wrapper once: identical routing, freshness,
|
|
182
|
+
// errors and metrics to a direct call, without a second discovery record.
|
|
183
|
+
return invocation.handler(parsed.data);
|
|
184
|
+
});
|
|
185
|
+
// Deprecated tool names (start_task, impact, plan_edit, validate, remember) delegate to
|
|
186
|
+
// their replacement's exact schema + implementation instead of re-declaring both, so the
|
|
187
|
+
// two can never silently drift apart (as `impact` had from `change_impact`, missing
|
|
188
|
+
// include_recent_churn). The alias still gets its own tool-metrics attribution so usage of
|
|
189
|
+
// deprecated names stays visible ahead of removing them in 2.0.
|
|
190
|
+
const registerDeprecatedAlias = (aliasName, canonicalName, summary, shape, run, options = {}) => {
|
|
191
|
+
// The handler's arg type is genuinely tied to `shape` at each call site, but `shape`'s
|
|
192
|
+
// static type here is an erased `Record<string, unknown>` (matching how `server` itself
|
|
193
|
+
// is already loosely cast above) — TS can't correlate a free `TArgs` with that, so this
|
|
194
|
+
// one call needs the same pragmatic cast the outer server wrapper already uses.
|
|
195
|
+
server.tool(aliasName, `[deprecated, prefer ${canonicalName}] ${summary}`, shape, withRequestActivity(async (args) => ({
|
|
196
|
+
content: [{ type: "text", text: await run(args) }],
|
|
197
|
+
}), { ...options, toolName: aliasName }));
|
|
198
|
+
};
|
|
199
|
+
server.tool("health", "Report ContextPro startup health, including active root, project identity, Node ABI, native storage status, and database readiness.", {
|
|
200
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
201
|
+
},
|
|
202
|
+
// Health must report broken project/storage state, not fail before diagnosis.
|
|
203
|
+
withRequestActivity(async ({ format }) => {
|
|
204
|
+
const report = await runStartupHealthCheck(rootDir());
|
|
205
|
+
return {
|
|
206
|
+
content: [{
|
|
207
|
+
type: "text",
|
|
208
|
+
text: format === "json" ? JSON.stringify(report, null, 2) : formatStartupHealthReport(report),
|
|
209
|
+
}],
|
|
210
|
+
};
|
|
211
|
+
}, { toolName: "health", waitForIndexing: false, skipProjectVerification: true }));
|
|
212
|
+
server.tool("artifact_read", "Load a previously generated context artifact by ID so agents can reuse earlier task packets, repo maps, and decisions without regenerating them.", {
|
|
213
|
+
artifact_id: z.string().min(1).describe("Artifact ID returned by repo_map, search, task_pack, change_impact, edit_scope, or decision_brief."),
|
|
214
|
+
}, withRequestActivity(async ({ artifact_id }) => {
|
|
215
|
+
const artifact = await readArtifact(rootDir(), artifact_id);
|
|
216
|
+
let applicability;
|
|
217
|
+
if (artifact?.kind === "validation_result")
|
|
218
|
+
applicability = await validationState(rootDir(), artifact_id);
|
|
219
|
+
if (artifact?.kind === "handoff") {
|
|
220
|
+
const payload = artifact.payload;
|
|
221
|
+
const node = payload.taskMemoryId ? (await getDB(rootDir())).getMemoryNode(payload.taskMemoryId) : null;
|
|
222
|
+
applicability = { sourceEvidence: node ? await memoryEvidenceStatus(rootDir(), node) : "unverified",
|
|
223
|
+
validation: await Promise.all((payload.validation ?? []).map(async ({ id }) => ({ id, state: await validationState(rootDir(), id) }))) };
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
content: [{
|
|
227
|
+
type: "text",
|
|
228
|
+
text: artifact ? JSON.stringify({ ...artifact, ...applicability ? { currentApplicability: applicability } : {} }, null, 2) : JSON.stringify({ error: `Artifact not found: ${artifact_id}` }, null, 2),
|
|
229
|
+
}],
|
|
230
|
+
};
|
|
231
|
+
}, { toolName: "artifact_read", waitForIndexing: false }));
|
|
232
|
+
server.tool("artifact_list", "List recent reusable context artifacts generated for this project. Use it to discover cached repo maps, task packs, and impact summaries. Each entry reports its on-disk size (sizeBytes).", {
|
|
233
|
+
kind: z.string().optional().describe("Optional artifact kind filter, e.g. repo_map, search, task_pack."),
|
|
234
|
+
limit: z.number().optional().describe("Maximum artifacts to return. Default: 20."),
|
|
235
|
+
}, withRequestActivity(async ({ kind, limit }) => {
|
|
236
|
+
const artifacts = await listArtifacts(rootDir(), kind, limit);
|
|
237
|
+
const compact = compactArtifactListKeys(artifacts);
|
|
238
|
+
return {
|
|
239
|
+
content: [{
|
|
240
|
+
type: "text",
|
|
241
|
+
text: JSON.stringify(compact, null, 2),
|
|
242
|
+
}],
|
|
243
|
+
};
|
|
244
|
+
}, { toolName: "artifact_list", waitForIndexing: false }));
|
|
245
|
+
server.tool("gc", "Garbage-collect orphaned on-disk data: artifact .json payloads and restore-point backup folders " +
|
|
246
|
+
"that have been evicted from their manifests (by the LRU caps) but never deleted from disk, plus " +
|
|
247
|
+
"indexes of git worktrees that no longer exist. Safe — only removes files no longer referenced by " +
|
|
248
|
+
"any manifest or live worktree. Reports files removed and bytes reclaimed.", {}, withRequestActivity(async () => {
|
|
249
|
+
const [artifacts, restorePoints, deadWorktrees] = await Promise.all([
|
|
250
|
+
garbageCollectArtifacts(rootDir()),
|
|
251
|
+
garbageCollectRestorePoints(rootDir()),
|
|
252
|
+
garbageCollectDeadWorktrees(rootDir()),
|
|
253
|
+
]);
|
|
254
|
+
const freed = artifacts.freedBytes + restorePoints.freedBytes;
|
|
255
|
+
const kb = (freed / 1024).toFixed(1);
|
|
256
|
+
return {
|
|
257
|
+
content: [{
|
|
258
|
+
type: "text",
|
|
259
|
+
text: `Garbage collection complete.\n`
|
|
260
|
+
+ ` Artifacts: removed ${artifacts.deletedFiles} orphaned file(s), freed ${(artifacts.freedBytes / 1024).toFixed(1)} KB\n`
|
|
261
|
+
+ ` Restore points: removed ${restorePoints.deletedPoints} orphaned backup(s), freed ${(restorePoints.freedBytes / 1024).toFixed(1)} KB\n`
|
|
262
|
+
+ ` Dead worktrees: removed ${deadWorktrees.removedWorkspaces} stale index(es)\n`
|
|
263
|
+
+ ` Total reclaimed: ${kb} KB`,
|
|
264
|
+
}],
|
|
265
|
+
};
|
|
266
|
+
}, { toolName: "gc", waitForIndexing: false }));
|
|
267
|
+
server.tool("repo_map", "Return a compact repository map with important files and top-level symbols. Optimized for first-turn agent orientation with low token usage.", {
|
|
268
|
+
target_path: z.string().optional().describe("Optional subdirectory prefix to scope the map."),
|
|
269
|
+
include_signatures: z.boolean().optional().describe("Include symbol signatures. Defaults to true."),
|
|
270
|
+
max_files: z.number().optional().describe("Maximum files to include. Default: 40."),
|
|
271
|
+
max_symbols_per_file: z.number().optional().describe("Maximum top-level symbols per file. Default: 8."),
|
|
272
|
+
max_tokens: z.number().optional().describe("Approximate token budget for text output. Default: 2000."),
|
|
273
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
274
|
+
focus_files: z.array(z.string()).optional().describe("Files to bias importance ranking toward for this call (e.g. files already found relevant to the current task) — reseeds PageRank per call instead of one static global ranking, Aider-repomap-style."),
|
|
275
|
+
focus_identifiers: z.array(z.string()).optional().describe("Identifiers mentioned in the current task/query — files that define one get an importance boost on top of any focus_files boost."),
|
|
276
|
+
}, withRequestActivity(async ({ target_path, include_signatures, max_files, max_symbols_per_file, max_tokens, format, focus_files, focus_identifiers }) => ({
|
|
277
|
+
content: [{
|
|
278
|
+
type: "text",
|
|
279
|
+
text: await getRepoMap({
|
|
280
|
+
rootDir: rootDir(),
|
|
281
|
+
targetPath: target_path,
|
|
282
|
+
includeSignatures: include_signatures,
|
|
283
|
+
maxFiles: max_files,
|
|
284
|
+
maxSymbolsPerFile: max_symbols_per_file,
|
|
285
|
+
maxTokens: max_tokens,
|
|
286
|
+
format,
|
|
287
|
+
focusFiles: focus_files,
|
|
288
|
+
focusIdentifiers: focus_identifiers,
|
|
289
|
+
}),
|
|
290
|
+
}],
|
|
291
|
+
}), { toolName: "repo_map", ensureFreshIndex: true }));
|
|
292
|
+
server.tool("graph_export", "Export the indexed project graph to portable artifacts: .contextpro/graph.json and .contextpro/GRAPH_REPORT.md. " +
|
|
293
|
+
"Use after reindex to share exact file, symbol, import, call, test, and confidence metadata across agents.", {
|
|
294
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
295
|
+
}, withRequestActivity(async ({ format }) => ({
|
|
296
|
+
content: [{
|
|
297
|
+
type: "text",
|
|
298
|
+
text: await exportProjectGraphTool({ rootDir: rootDir(), format }),
|
|
299
|
+
}],
|
|
300
|
+
}), { toolName: "graph_export", ensureFreshIndex: true }));
|
|
301
|
+
registerStructuredTool("graph_query", "Query the indexed project graph without reading source files. Supports scoped neighborhoods, transitive impact, confidence-weighted shortest paths, PageRank hotspots, and structural communities.", {
|
|
302
|
+
kind: z.enum(["summary", "file", "symbol", "impact", "path", "hotspots", "communities"]).optional().describe("Graph view to return. Default: summary."),
|
|
303
|
+
file: z.string().optional().describe("File path for file or impact views."),
|
|
304
|
+
symbol: z.string().optional().describe("Exact symbol name for symbol views."),
|
|
305
|
+
source: z.string().optional().describe("Starting node for path view: exact node id, project-relative path, or unambiguous symbol label."),
|
|
306
|
+
target: z.string().optional().describe("Destination node for path view: exact node id, project-relative path, or unambiguous symbol label."),
|
|
307
|
+
max_hops: z.number().optional().describe("Maximum path length for path view. Default: 8, max: 20."),
|
|
308
|
+
limit: z.number().optional().describe("Maximum nodes/edges to return. Default: 50, max: 200."),
|
|
309
|
+
max_tokens: z.number().optional().describe("Approximate response budget. Default: 2500, range: 256-12000."),
|
|
310
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
311
|
+
}, z.object({
|
|
312
|
+
graph: z.record(z.string(), z.unknown()).optional(),
|
|
313
|
+
nodes: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
314
|
+
edges: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
315
|
+
analysis: z.record(z.string(), z.unknown()).optional(),
|
|
316
|
+
fuzzyNote: z.string().nullable().optional(),
|
|
317
|
+
truncation: z.record(z.string(), z.unknown()).optional(),
|
|
318
|
+
error: z.string().optional(),
|
|
319
|
+
}), withRequestActivity(async (args) => {
|
|
320
|
+
const { kind, file, symbol, source, target, max_hops, limit, max_tokens, format } = args;
|
|
321
|
+
const options = { rootDir: rootDir(), kind, file, symbol, source, target, maxHops: max_hops, limit, maxTokens: max_tokens };
|
|
322
|
+
const jsonText = await queryProjectGraphTool({ ...options, format: "json" });
|
|
323
|
+
let structuredContent;
|
|
324
|
+
try {
|
|
325
|
+
structuredContent = JSON.parse(jsonText);
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
structuredContent = { error: jsonText };
|
|
329
|
+
}
|
|
330
|
+
const text = format === "json" ? jsonText : await queryProjectGraphTool({ ...options, format: "text" });
|
|
331
|
+
return {
|
|
332
|
+
content: [{ type: "text", text }],
|
|
333
|
+
structuredContent,
|
|
334
|
+
};
|
|
335
|
+
}, { toolName: "graph_query", ensureFreshIndex: true }));
|
|
336
|
+
server.tool("resolve_symbol", "Resolve an identifier to the most likely definition with ambiguity scoring. Use this before explain/refactor work when a symbol name appears in multiple files. " +
|
|
337
|
+
"This is a best-guess ranking (call-graph proximity + locality), not a compiler resolution — if an LSP-backed tool (e.g. Serena) is available for this language, prefer it when the ambiguity flag here is set.", {
|
|
338
|
+
symbol_name: z.string().min(1).describe("The symbol or identifier name to resolve."),
|
|
339
|
+
file_context: z.string().optional().describe("Optional current file path to bias resolution using import context and locality."),
|
|
340
|
+
kind: z.string().optional().describe("Optional expected kind, e.g. function, method, class, interface."),
|
|
341
|
+
limit: z.number().optional().describe("Maximum candidates to return. Default: 8."),
|
|
342
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
343
|
+
}, withRequestActivity(async ({ symbol_name, file_context, kind, limit, format }) => ({
|
|
344
|
+
content: [{
|
|
345
|
+
type: "text",
|
|
346
|
+
text: await resolveSymbol({
|
|
347
|
+
rootDir: rootDir(),
|
|
348
|
+
symbolName: symbol_name,
|
|
349
|
+
fileContext: file_context,
|
|
350
|
+
kind,
|
|
351
|
+
limit,
|
|
352
|
+
format,
|
|
353
|
+
}),
|
|
354
|
+
}],
|
|
355
|
+
}), { toolName: "resolve_symbol", ensureFreshIndex: true }));
|
|
356
|
+
server.tool("exact_references", "Find who calls or references a symbol. TS/JS compiler bindings exclude same-named unrelated symbols; results label calls/imports and give per-file call totals before limits. Check scope, ambiguity and warnings. Python is heuristic; imported SCIP freshness is unverified.", {
|
|
357
|
+
symbol_name: z.string().min(1),
|
|
358
|
+
file: z.string().optional().describe("Definition file; omit to infer."),
|
|
359
|
+
line: z.number().int().min(1).optional().describe("1-based symbol line. Omit to infer a declaration; when several match, the response warns about the chosen candidate. Pass file and line to disambiguate."),
|
|
360
|
+
limit: z.number().int().min(1).max(500).optional().describe("Shown occurrences; default 100."),
|
|
361
|
+
format: z.enum(["text", "json"]).optional().describe("Default text."),
|
|
362
|
+
}, withRequestActivity(async ({ symbol_name, file, line, limit, format }) => ({
|
|
363
|
+
content: [{
|
|
364
|
+
type: "text",
|
|
365
|
+
text: await exactReferencesTool({ rootDir: rootDir(), symbolName: symbol_name, file, line, limit, format }),
|
|
366
|
+
}],
|
|
367
|
+
}), { toolName: "exact_references", ensureFreshIndex: true }));
|
|
368
|
+
server.tool("scip_import", "Import and normalize a project-provided SCIP JSON index into .contextpro/scip-index.json so graph_export, graph_query, and exact_references can use SCIP_RESOLVED evidence.", {
|
|
369
|
+
index_path: z.string().min(1).describe("Project-relative path to a SCIP JSON index, for example .contextpro/index.scip.json."),
|
|
370
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
371
|
+
}, withRequestActivity(async ({ index_path, format }) => ({
|
|
372
|
+
content: [{
|
|
373
|
+
type: "text",
|
|
374
|
+
text: await scipImportTool({ rootDir: rootDir(), indexPath: index_path, format }),
|
|
375
|
+
}],
|
|
376
|
+
}), { toolName: "scip_import", waitForIndexing: false }));
|
|
377
|
+
server.tool("scip_status", "Report whether this project has an imported SCIP JSON index available for exact cross-language reference evidence.", {
|
|
378
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
379
|
+
}, withRequestActivity(async ({ format }) => ({
|
|
380
|
+
content: [{
|
|
381
|
+
type: "text",
|
|
382
|
+
text: await scipStatusTool({ rootDir: rootDir(), format }),
|
|
383
|
+
}],
|
|
384
|
+
}), { toolName: "scip_status", waitForIndexing: false }));
|
|
385
|
+
server.tool("tree", "Get the structural tree of the project with file headers, function names, classes, enums, and line ranges. " +
|
|
386
|
+
"Automatically reads 2-line headers for file purpose. Dynamic token-aware pruning: " +
|
|
387
|
+
"Level 2 (deep symbols) -> Level 1 (headers only) -> Level 0 (file names only) based on project size.", {
|
|
388
|
+
target_path: z.string().optional().describe("Specific directory or file to analyze (relative to project root). Defaults to root."),
|
|
389
|
+
depth_limit: z.number().optional().describe("How many folder levels deep to scan. Use 1-2 for large projects."),
|
|
390
|
+
include_symbols: z.boolean().optional().describe("Include function/class/enum names in the tree. Defaults to true."),
|
|
391
|
+
max_tokens: z.number().optional().describe("Maximum tokens for output. Auto-prunes if exceeded. Default: 20000."),
|
|
392
|
+
}, withRequestActivity(async ({ target_path, depth_limit, include_symbols, max_tokens }) => ({
|
|
393
|
+
content: [{
|
|
394
|
+
type: "text",
|
|
395
|
+
text: await getContextTree({
|
|
396
|
+
rootDir: rootDir(),
|
|
397
|
+
targetPath: target_path,
|
|
398
|
+
depthLimit: depth_limit,
|
|
399
|
+
includeSymbols: include_symbols,
|
|
400
|
+
maxTokens: max_tokens,
|
|
401
|
+
}),
|
|
402
|
+
}],
|
|
403
|
+
}), { toolName: "tree" }));
|
|
404
|
+
server.tool("skeleton", "Get detailed function signatures, class methods, and type definitions of a specific file WITHOUT reading the full body. " +
|
|
405
|
+
"Shows the API surface and line ranges. Signatures are normalized summaries, not exact edit anchors; use context(targets:[{file,start,end}]) for source text before editing.", {
|
|
406
|
+
file_path: z.string().min(1).describe("Path to the file to inspect (relative to project root)."),
|
|
407
|
+
exported_only: z.boolean().optional().describe("Only public declarations (TS/JS). Default: false."),
|
|
408
|
+
}, withRequestActivity(async ({ file_path, exported_only }) => ({
|
|
409
|
+
content: [{
|
|
410
|
+
type: "text",
|
|
411
|
+
text: await getFileSkeleton({ rootDir: rootDir(), filePath: file_path, exportedOnly: exported_only }),
|
|
412
|
+
}],
|
|
413
|
+
}), { toolName: "skeleton", ensureFreshIndex: true }));
|
|
414
|
+
server.tool("search", "Search the codebase by MEANING. Modes: 'file' searches file headers/symbols, 'identifier' searches function/class definitions with call chains, " +
|
|
415
|
+
"'hybrid' (default) runs both and returns combined results. Uses embeddings for semantic matching plus keyword overlap scoring. " +
|
|
416
|
+
"For an exact known symbol name in a language with a real language server available (e.g. via an LSP-backed MCP like Serena), prefer that for go-to-definition — this tool is best for meaning-based / fuzzy discovery across the whole repo.", {
|
|
417
|
+
query: z.string().min(1).max(8000).describe("Natural language description of what you're looking for."),
|
|
418
|
+
profile: z.boolean().optional().describe("Include phase timings in JSON/compact output for diagnosis."),
|
|
419
|
+
include_dependencies: z.boolean().optional().describe("Add up to three import pointers from the top two file matches to check supporting code."),
|
|
420
|
+
mode: z.enum(["file", "identifier", "hybrid"]).optional().describe("Search mode: 'file' for files, 'identifier' for functions/classes, 'hybrid' for both. Default: hybrid."),
|
|
421
|
+
preset: z.enum(["fast", "balanced", "exhaustive", "semantic_only", "keyword_only"]).optional().describe("Ranking preset. fast favors cheap lexical recall, balanced is default, exhaustive returns a wider set, semantic_only ignores keyword overlap (pure embedding similarity), keyword_only ignores embedding similarity (pure lexical match)."),
|
|
422
|
+
format: z.enum(["text", "json", "compact"]).optional().describe("text (default), compact JSON for agents, or verbose json for diagnostics."),
|
|
423
|
+
top_k: z.number().optional().describe("Number of matches to return. Default: 5."),
|
|
424
|
+
top_calls_per_identifier: z.number().optional().describe("How many ranked call sites per identifier (identifier/hybrid mode). Default: 10."),
|
|
425
|
+
include_kinds: z.array(z.string()).optional().describe("Filter identifier kinds, e.g. [\"function\", \"method\"]. Only for identifier/hybrid mode."),
|
|
426
|
+
semantic_weight: z.number().optional().describe("Weight for embedding similarity. Default: 0.72."),
|
|
427
|
+
keyword_weight: z.number().optional().describe("Weight for keyword overlap. Default: 0.28."),
|
|
428
|
+
min_semantic_score: z.number().optional().describe("Minimum semantic score filter. Accepts 0-1 or 0-100."),
|
|
429
|
+
min_keyword_score: z.number().optional().describe("Minimum keyword score filter. Accepts 0-1 or 0-100."),
|
|
430
|
+
min_combined_score: z.number().optional().describe("Minimum final score filter. Accepts 0-1 or 0-100."),
|
|
431
|
+
require_keyword_match: z.boolean().optional().describe("When true, only return results with keyword overlap."),
|
|
432
|
+
require_semantic_match: z.boolean().optional().describe("When true, only return results with positive semantic similarity."),
|
|
433
|
+
fusion: z.enum(["weighted", "rrf"]).optional().describe("How to combine semantic + keyword scores in file mode. 'rrf' (default) fuses rank positions and is robust when signal scales differ. 'weighted' is an opt-out that blends by semantic_weight/keyword_weight."),
|
|
434
|
+
inline_code: z.enum(["off", "crux", "full"]).optional().describe("Inline source with file matches. 'crux' (default) returns an ~8-line relevant span, 'full' returns the matched definition body, and 'off' returns pointers only."),
|
|
435
|
+
}, withRequestActivity(async (args) => ({
|
|
436
|
+
content: [{
|
|
437
|
+
type: "text",
|
|
438
|
+
text: await unifiedSearch({
|
|
439
|
+
rootDir: rootDir(),
|
|
440
|
+
query: args.query,
|
|
441
|
+
profile: args.profile,
|
|
442
|
+
includeDependencies: args.include_dependencies,
|
|
443
|
+
mode: args.mode,
|
|
444
|
+
preset: args.preset,
|
|
445
|
+
format: args.format,
|
|
446
|
+
topK: args.top_k,
|
|
447
|
+
topCallsPerIdentifier: args.top_calls_per_identifier,
|
|
448
|
+
includeKinds: args.include_kinds,
|
|
449
|
+
semanticWeight: args.semantic_weight,
|
|
450
|
+
keywordWeight: args.keyword_weight,
|
|
451
|
+
minSemanticScore: args.min_semantic_score,
|
|
452
|
+
minKeywordScore: args.min_keyword_score,
|
|
453
|
+
minCombinedScore: args.min_combined_score,
|
|
454
|
+
requireKeywordMatch: args.require_keyword_match,
|
|
455
|
+
requireSemanticMatch: args.require_semantic_match,
|
|
456
|
+
fusion: args.fusion,
|
|
457
|
+
inlineCode: args.inline_code,
|
|
458
|
+
}),
|
|
459
|
+
}],
|
|
460
|
+
}), { useEmbeddingTracker: true, toolName: "search" }));
|
|
461
|
+
server.tool("grep", "Fast literal or regex string search across the project's indexed file set. Use this for log-message lookups, config-key searches, error-message tracing, or any time you know the exact characters you want to find. Use 'search' instead for semantic intent queries. Skips binary files; respects .gitignore via the indexed walker.", {
|
|
462
|
+
pattern: z.string().min(1).describe("Literal string to search for (or regex when regex=true)."),
|
|
463
|
+
regex: z.boolean().optional().describe("Treat pattern as a line-based regular expression (set true for A|B alternation). Default: false, literal match."),
|
|
464
|
+
case_sensitive: z.boolean().optional().describe("Match case-sensitively. Default: false."),
|
|
465
|
+
target_path: z.string().optional().describe("Optional subdirectory to scope the search."),
|
|
466
|
+
glob: z.string().optional().describe("Optional file glob filter, e.g. \"src/**/*.ts\" or \"*.md\". Comma-separated for multiple."),
|
|
467
|
+
max_matches_per_file: z.number().optional().describe("Max matches per file. Default: 10."),
|
|
468
|
+
max_total_matches: z.number().optional().describe("Max total matches across all files. Default: 200."),
|
|
469
|
+
max_file_bytes: z.number().optional().describe("Skip files larger than this (bytes). Default: 1000000."),
|
|
470
|
+
context_chars: z.number().int().min(40).max(400).optional().describe("Character window within the matched line; surrounding lines are clipped to this width. 40–400, default: 160. Use context_lines for nearby lines."),
|
|
471
|
+
context_lines: z.number().int().min(0).max(10).optional().describe("Lines before and after each match (0–10, default: 0). Overlapping ranges are deduplicated; L<line>- marks context, L<line>:<column> marks a hit."),
|
|
472
|
+
}, withRequestActivity(async (args) => ({
|
|
473
|
+
content: [{
|
|
474
|
+
type: "text",
|
|
475
|
+
text: await grepProject({
|
|
476
|
+
rootDir: rootDir(),
|
|
477
|
+
pattern: args.pattern,
|
|
478
|
+
regex: args.regex,
|
|
479
|
+
caseSensitive: args.case_sensitive,
|
|
480
|
+
targetPath: args.target_path,
|
|
481
|
+
glob: args.glob,
|
|
482
|
+
maxMatchesPerFile: args.max_matches_per_file,
|
|
483
|
+
maxTotalMatches: args.max_total_matches,
|
|
484
|
+
maxFileBytes: args.max_file_bytes,
|
|
485
|
+
contextChars: args.context_chars,
|
|
486
|
+
contextLines: args.context_lines,
|
|
487
|
+
}),
|
|
488
|
+
}],
|
|
489
|
+
}), { toolName: "grep", waitForIndexing: false }));
|
|
490
|
+
server.tool("blast_radius", "Find textual usage candidates with counts by kind. Use kind:call for call-containing lines. Includes same-name symbols; use exact_references for binding-resolved TS/JS rename checks.", {
|
|
491
|
+
symbol_name: z.string().min(1).describe("The function, class, or variable name to trace across the codebase."),
|
|
492
|
+
file_context: z.string().optional().describe("The file where the symbol is defined. Excludes the definition line from results."),
|
|
493
|
+
mode: z.enum(["summary", "detailed"]).optional().describe("Summary groups usages by containing symbol (default); detailed returns every matching line."),
|
|
494
|
+
kind: z.enum(["call", "import", "type-reference", "mock/stub", "comment/string"]).optional().describe("Return only this usage kind; totals still show all kinds."),
|
|
495
|
+
}, withRequestActivity(async ({ symbol_name, file_context, mode, kind }) => ({
|
|
496
|
+
content: [{
|
|
497
|
+
type: "text",
|
|
498
|
+
text: await getBlastRadius({ rootDir: rootDir(), symbolName: symbol_name, fileContext: file_context, mode, kind }),
|
|
499
|
+
}],
|
|
500
|
+
}), { toolName: "blast_radius", ensureFreshIndex: true }));
|
|
501
|
+
server.tool("lint", "Auto-detects and runs project linters (tsc, eslint, ruff, cargo check, go vet). " +
|
|
502
|
+
"Also checks code skill score: 2-line headers, no inline comments, nesting depth, file length. " +
|
|
503
|
+
"By default returns the top 10 worst-scoring files; pass verbose: true to see all.", {
|
|
504
|
+
target_path: z.string().optional().describe("Specific file or folder to lint (relative to root). Omit for full project."),
|
|
505
|
+
verbose: z.boolean().optional().describe("Return all files with issues. Default: false (top 10 by lowest score)."),
|
|
506
|
+
}, withRequestActivity(async ({ target_path, verbose }) => ({
|
|
507
|
+
content: [{
|
|
508
|
+
type: "text",
|
|
509
|
+
text: await runStaticAnalysis({ rootDir: rootDir(), targetPath: target_path, verbose }),
|
|
510
|
+
}],
|
|
511
|
+
}), { toolName: "lint" }));
|
|
512
|
+
server.tool("checkpoint", "Save code with optional validation. Creates a shadow restore point before writing. " +
|
|
513
|
+
"Use during long work sessions for safe mid-work saves. Pass strict: true to enforce style rules (header, no inline comments, nesting, length); otherwise issues are advisory warnings only.", {
|
|
514
|
+
file_path: z.string().min(1).describe("Where to save the file (relative to project root)."),
|
|
515
|
+
new_content: z.string().min(1).describe("The complete file content to save."),
|
|
516
|
+
expected_content_hash: z.string().regex(/^[a-f0-9]{64}$/).optional().describe("Expected current file SHA-256; reject stale edits. Omit for compatibility."),
|
|
517
|
+
strict: z.boolean().optional().describe("If true, reject on any style violation. Default: false (warnings only)."),
|
|
518
|
+
}, withRequestActivity(async ({ file_path, new_content, strict, expected_content_hash }) => {
|
|
519
|
+
invalidateSearchCache();
|
|
520
|
+
invalidateIdentifierSearchCache();
|
|
521
|
+
return {
|
|
522
|
+
content: [{
|
|
523
|
+
type: "text",
|
|
524
|
+
text: await proposeCommit({ rootDir: rootDir(), filePath: file_path, newContent: new_content, strict, expectedContentHash: expected_content_hash }),
|
|
525
|
+
}],
|
|
526
|
+
};
|
|
527
|
+
}, { toolName: "checkpoint" }));
|
|
528
|
+
server.tool("checkpoints", "List all shadow restore points created by checkpoint. Each point captures the file state before changes. " +
|
|
529
|
+
"Use this to find a restore point ID for undoing a bad change.", {}, withRequestActivity(async () => {
|
|
530
|
+
const points = await listRestorePoints(rootDir());
|
|
531
|
+
if (points.length === 0)
|
|
532
|
+
return { content: [{ type: "text", text: "No restore points found." }] };
|
|
533
|
+
const session = currentSessionId();
|
|
534
|
+
const lines = points.map((p) => {
|
|
535
|
+
const foreign = p.session && p.session !== session ? " [OTHER SESSION]" : "";
|
|
536
|
+
// Loud, unambiguous: restoring a point whose file was new at checkpoint time DELETES that file.
|
|
537
|
+
const tomb = p.tombstoneFiles && p.tombstoneFiles.length > 0
|
|
538
|
+
? ` [⚠ RESTORE DELETES: ${p.tombstoneFiles.join(", ")} — file(s) did not exist at checkpoint]`
|
|
539
|
+
: "";
|
|
540
|
+
return `${p.id} | ${new Date(p.timestamp).toISOString()} | ${p.files.join(", ")} | ${p.message}${foreign}${tomb}`;
|
|
541
|
+
});
|
|
542
|
+
const ownCount = points.filter((p) => !p.session || p.session === session).length;
|
|
543
|
+
const header = `Restore Points (${points.length} total, ${ownCount} from this session):`;
|
|
544
|
+
return { content: [{ type: "text", text: `${header}\n\n${lines.join("\n")}` }] };
|
|
545
|
+
}, { toolName: "checkpoints" }));
|
|
546
|
+
server.tool("restore", "Restore files to their state at a checkpoint. Uses the shadow restore point system (does NOT affect git history). " +
|
|
547
|
+
"Call checkpoints first to find the point ID. SAFE BY DEFAULT: a pre-restore safety snapshot is taken before any " +
|
|
548
|
+
"file is overwritten or deleted, so a wrong restore is reversible. Pass preview:true to see exactly what would " +
|
|
549
|
+
"change (including any file the restore would DELETE) without writing anything.", {
|
|
550
|
+
point_id: z.string().min(1).describe("The restore point ID (format: rp-timestamp-hash). Get it from checkpoints."),
|
|
551
|
+
preview: z.boolean().optional().describe("Dry run: show what would change (overwrite/create/delete) without writing. Recommended before a real restore."),
|
|
552
|
+
}, withRequestActivity(async ({ point_id, preview }) => {
|
|
553
|
+
const result = await restorePoint(rootDir(), point_id, { preview: preview === true });
|
|
554
|
+
const renderPlan = () => {
|
|
555
|
+
const icon = { overwrite: "✏ overwrite", create: "+ create", delete: "⚠ DELETE", "missing-backup": "? skip (backup missing)" };
|
|
556
|
+
return result.plan.plans.map((p) => ` ${icon[p.action]} ${p.file}${p.destructive ? " (destroys current content)" : ""}`).join("\n");
|
|
557
|
+
};
|
|
558
|
+
const destructiveCount = result.plan.plans.filter((p) => p.destructive).length;
|
|
559
|
+
if (result.preview) {
|
|
560
|
+
const willDelete = result.plan.plans.filter((p) => p.action === "delete").length;
|
|
561
|
+
const note = willDelete > 0
|
|
562
|
+
? `\n\n⚠ ${willDelete} file(s) would be DELETED (they did not exist at checkpoint time). `
|
|
563
|
+
+ `Restoring the earliest point of a newly-created file removes that file.`
|
|
564
|
+
: "";
|
|
565
|
+
return {
|
|
566
|
+
content: [{
|
|
567
|
+
type: "text",
|
|
568
|
+
text: `PREVIEW — restoring ${point_id} ("${result.plan.message}") would affect ${result.plan.plans.length} file(s); `
|
|
569
|
+
+ `${destructiveCount} destructive:\n${renderPlan()}${note}\n\nRe-call restore without preview to apply. `
|
|
570
|
+
+ `A safety snapshot will be taken first so you can undo.`,
|
|
571
|
+
}],
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
invalidateSearchCache();
|
|
575
|
+
invalidateIdentifierSearchCache();
|
|
576
|
+
const undo = result.safetyPointId
|
|
577
|
+
? `\n\nUndo this restore with: restore point_id="${result.safetyPointId}"`
|
|
578
|
+
: "";
|
|
579
|
+
return {
|
|
580
|
+
content: [{
|
|
581
|
+
type: "text",
|
|
582
|
+
text: result.restoredFiles.length > 0
|
|
583
|
+
? `Restored ${result.restoredFiles.length} file(s):\n${result.restoredFiles.join("\n")}${undo}`
|
|
584
|
+
: `No files were restored. The backup may be empty or its files were not found.${undo}`,
|
|
585
|
+
}],
|
|
586
|
+
};
|
|
587
|
+
}, { toolName: "restore" }));
|
|
588
|
+
server.tool("cluster", "Browse the codebase by MEANING, not directory structure. Uses spectral clustering on embeddings to group " +
|
|
589
|
+
"semantically related files into labeled clusters. Requires an embedding model and a chat model for labeling.", {
|
|
590
|
+
max_depth: z.number().optional().describe("Maximum nesting depth of clusters. Default: 3."),
|
|
591
|
+
max_clusters: z.number().optional().describe("Maximum sub-clusters per level. Default: 20."),
|
|
592
|
+
}, withRequestActivity(async ({ max_depth, max_clusters }) => ({
|
|
593
|
+
content: [{
|
|
594
|
+
type: "text",
|
|
595
|
+
text: await semanticNavigate({ rootDir: rootDir(), maxDepth: max_depth, maxClusters: max_clusters }),
|
|
596
|
+
}],
|
|
597
|
+
}), { toolName: "cluster" }));
|
|
598
|
+
server.tool("hub", "Feature hub navigator. Hub files are .md files containing [[path/to/file]] wikilinks that act as a Map of Content. " +
|
|
599
|
+
"No args = list all hubs. hub_path or feature_name = show hub with bundled skeletons. show_orphans = find unlinked files.", {
|
|
600
|
+
hub_path: z.string().optional().describe("Path to a specific hub .md file (relative to root)."),
|
|
601
|
+
feature_name: z.string().optional().describe("Feature name to search for. Finds matching hub file automatically."),
|
|
602
|
+
show_orphans: z.boolean().optional().describe("If true, lists all source files not linked to any feature hub."),
|
|
603
|
+
query: z.string().optional().describe("Search/filter query. When listing all hubs, ranks and marks hubs matching this term by title, path, and linked files."),
|
|
604
|
+
}, withRequestActivity(async ({ hub_path, feature_name, show_orphans, query }) => ({
|
|
605
|
+
content: [{
|
|
606
|
+
type: "text",
|
|
607
|
+
text: await getFeatureHub({
|
|
608
|
+
rootDir: rootDir(),
|
|
609
|
+
hubPath: hub_path,
|
|
610
|
+
featureName: feature_name,
|
|
611
|
+
showOrphans: show_orphans,
|
|
612
|
+
query,
|
|
613
|
+
}),
|
|
614
|
+
}],
|
|
615
|
+
}), { toolName: "hub" }));
|
|
616
|
+
server.tool("memory_create", "Create or update a memory node. Nodes represent concepts, files, symbols, or notes with auto-generated embeddings. " +
|
|
617
|
+
"If a node with the same label and type exists, it updates content. Returns the node ID for use in memory_link.", {
|
|
618
|
+
type: z.enum(["concept", "file", "symbol", "note"]).describe("Node type: concept (abstract ideas), file (source files), symbol (functions/classes), note (free-form)."),
|
|
619
|
+
label: z.string().min(1).max(500).describe("Short identifier for the node. Used for deduplication with type."),
|
|
620
|
+
content: z.string().min(1).max(16000).describe("Detailed content for the node. Used for embedding generation."),
|
|
621
|
+
metadata: z.record(z.string(), z.string()).optional().describe("Optional key-value metadata pairs."),
|
|
622
|
+
scope: z.enum(["session", "project", "client", "global"]).optional().describe("Memory scope. Defaults to project."),
|
|
623
|
+
}, withRequestActivity(async ({ type, label, content, metadata, scope }) => ({
|
|
624
|
+
content: [{
|
|
625
|
+
type: "text",
|
|
626
|
+
text: await toolUpsertMemoryNode({ rootDir: rootDir(), type, label, content, metadata, scope }),
|
|
627
|
+
}],
|
|
628
|
+
})));
|
|
629
|
+
server.tool("memory_link", "Create a typed edge between two memory nodes. Supports: relates_to, depends_on, implements, references, similar_to, contains, resolved_by, contradicts. " +
|
|
630
|
+
"Edges have weights (0-1) that decay over time. Duplicate edges update weight.", {
|
|
631
|
+
source_id: z.string().min(1).describe("ID of the source memory node."),
|
|
632
|
+
target_id: z.string().min(1).describe("ID of the target memory node."),
|
|
633
|
+
relation: z.enum(["relates_to", "depends_on", "implements", "references", "similar_to", "contains", "resolved_by", "contradicts"]).describe("Relationship type between nodes."),
|
|
634
|
+
weight: z.number().optional().describe("Edge weight 0-1. Higher = stronger relationship. Default: 1.0."),
|
|
635
|
+
metadata: z.record(z.string(), z.string()).optional().describe("Optional key-value metadata for the edge."),
|
|
636
|
+
}, withRequestActivity(async ({ source_id, target_id, relation, weight, metadata }) => ({
|
|
637
|
+
content: [{
|
|
638
|
+
type: "text",
|
|
639
|
+
text: await toolCreateRelation({ rootDir: rootDir(), sourceId: source_id, targetId: target_id, relation, weight, metadata }),
|
|
640
|
+
}],
|
|
641
|
+
})));
|
|
642
|
+
server.tool("memory_search", "Search the memory graph by meaning with graph traversal. Finds direct matches via embedding similarity, " +
|
|
643
|
+
"then traverses neighbors to discover linked context. Returns hits and graph-connected neighbors with relevance scores.", {
|
|
644
|
+
query: z.string().min(1).max(8000).describe("Natural language query to search the memory graph."),
|
|
645
|
+
max_depth: z.number().optional().describe("How many hops to traverse from direct matches. Default: 1."),
|
|
646
|
+
top_k: z.number().optional().describe("Number of direct matches to return. Default: 5."),
|
|
647
|
+
scope: z.enum(["session", "project", "client", "global"]).optional().describe("Retrieval scope. Defaults to project."),
|
|
648
|
+
edge_filter: z.array(z.enum(["relates_to", "depends_on", "implements", "references", "similar_to", "contains", "resolved_by", "contradicts"])).optional()
|
|
649
|
+
.describe("Only traverse edges of these types. Omit for all types."),
|
|
650
|
+
}, withRequestActivity(async ({ query, max_depth, top_k, edge_filter, scope }) => ({
|
|
651
|
+
content: [{
|
|
652
|
+
type: "text",
|
|
653
|
+
text: await toolSearchMemoryGraph({ rootDir: rootDir(), query, maxDepth: max_depth, topK: top_k, edgeFilter: edge_filter, scope }),
|
|
654
|
+
}],
|
|
655
|
+
})));
|
|
656
|
+
server.tool("memory_bulk", "Bulk-add multiple memory nodes with automatic similarity linking. Creates similarity edges between pairs " +
|
|
657
|
+
"with cosine similarity ≥ 0.72. Ideal for importing related concepts, files, or notes at once.", {
|
|
658
|
+
items: z.array(z.object({
|
|
659
|
+
type: z.enum(["concept", "file", "symbol", "note"]),
|
|
660
|
+
label: z.string(),
|
|
661
|
+
content: z.string(),
|
|
662
|
+
metadata: z.record(z.string(), z.string()).optional(),
|
|
663
|
+
})).describe("Array of nodes to add. Each needs type, label, and content."),
|
|
664
|
+
auto_link: z.boolean().optional().describe("Whether to auto-create similarity edges. Default: true."),
|
|
665
|
+
scope: z.enum(["session", "project", "client", "global"]).optional().describe("Memory scope. Defaults to project."),
|
|
666
|
+
}, withRequestActivity(async ({ items, auto_link, scope }) => ({
|
|
667
|
+
content: [{
|
|
668
|
+
type: "text",
|
|
669
|
+
text: await toolAddInterlinkedContext({ rootDir: rootDir(), items, autoLink: auto_link, scope }),
|
|
670
|
+
}],
|
|
671
|
+
})));
|
|
672
|
+
server.tool("memory_explore", "Start from a memory node and traverse outward. Returns the starting node plus all reachable neighbors " +
|
|
673
|
+
"within the depth limit, scored by edge weight decay and depth penalty. Use after memory_search to explore a node's neighborhood.", {
|
|
674
|
+
start_node_id: z.string().min(1).describe("ID of the memory node to start traversal from."),
|
|
675
|
+
max_depth: z.number().optional().describe("Maximum traversal depth from start node. Default: 2."),
|
|
676
|
+
edge_filter: z.array(z.enum(["relates_to", "depends_on", "implements", "references", "similar_to", "contains", "resolved_by", "contradicts"])).optional()
|
|
677
|
+
.describe("Only traverse edges of these types. Omit for all."),
|
|
678
|
+
}, withRequestActivity(async ({ start_node_id, max_depth, edge_filter }) => ({
|
|
679
|
+
content: [{
|
|
680
|
+
type: "text",
|
|
681
|
+
text: await toolRetrieveWithTraversal({ rootDir: rootDir(), startNodeId: start_node_id, maxDepth: max_depth, edgeFilter: edge_filter }),
|
|
682
|
+
}],
|
|
683
|
+
})));
|
|
684
|
+
server.tool("memory_delete", "Delete a memory node and all its edges from the memory graph by node ID. " +
|
|
685
|
+
"Use to remove stale, incorrect, or outdated nodes that are polluting search results.", {
|
|
686
|
+
node_id: z.string().min(1).describe("ID of the memory node to delete."),
|
|
687
|
+
}, withRequestActivity(async ({ node_id }) => ({
|
|
688
|
+
content: [{
|
|
689
|
+
type: "text",
|
|
690
|
+
text: await toolDeleteMemoryNode({ rootDir: rootDir(), nodeId: node_id }),
|
|
691
|
+
}],
|
|
692
|
+
})));
|
|
693
|
+
server.tool("memory_list", "List all memory nodes filtered by scope and/or kind, sorted by recency. " +
|
|
694
|
+
"Use to see what's stored in memory, audit stale nodes, or find IDs before deleting.", {
|
|
695
|
+
scope: z.enum(["session", "project", "client", "global"]).optional()
|
|
696
|
+
.describe("Memory scope to filter by. Default: project."),
|
|
697
|
+
kind: z.string().optional()
|
|
698
|
+
.describe("Filter by node type or record_kind (e.g. 'task', 'file', 'concept', 'note')."),
|
|
699
|
+
limit: z.number().optional().describe("Max nodes to return. Default: 20, max: 100."),
|
|
700
|
+
}, withRequestActivity(async ({ scope, kind, limit }) => ({
|
|
701
|
+
content: [{
|
|
702
|
+
type: "text",
|
|
703
|
+
text: await toolListMemoryNodes({ rootDir: rootDir(), scope, kind, limit }),
|
|
704
|
+
}],
|
|
705
|
+
})));
|
|
706
|
+
server.tool("reindex", "Re-walk, re-parse, and re-index project files into the database. Detects changed files via content hash and only re-processes what changed. " +
|
|
707
|
+
"Eagerly populates file and identifier embeddings unless no_embed=true or CONTEXTPRO_REINDEX_NO_EMBED is set. " +
|
|
708
|
+
"Use after git pull, branch switch, or external edits. Builds the import graph for related/test_map tools.", {
|
|
709
|
+
target_path: z.string().optional().describe("Scope reindex to a specific directory (relative to root)."),
|
|
710
|
+
force: z.boolean().optional().describe("Force re-process all files, ignoring cache. Default: false."),
|
|
711
|
+
no_embed: z.boolean().optional().describe("Skip corpus embedding generation. Search does not generate corpus embeddings; it uses existing vectors or lexical fallback. Configure an embedding provider and run reindex with no_embed:false to populate vectors (also unset CONTEXTPRO_REINDEX_NO_EMBED). Default: false."),
|
|
712
|
+
auto_hubs: z.boolean().optional().describe("Generate feature-hub labels (one chat-model call per hub). Off by default to keep reindex fast and free. Or set CONTEXTPRO_AUTO_HUBS=1."),
|
|
713
|
+
}, withRequestActivity(async ({ target_path, force, no_embed, auto_hubs }) => ({
|
|
714
|
+
content: [{
|
|
715
|
+
type: "text",
|
|
716
|
+
text: await reindex({ rootDir: rootDir(), targetPath: target_path, force, noEmbed: no_embed, autoHubs: auto_hubs }),
|
|
717
|
+
}],
|
|
718
|
+
}), { toolName: "reindex", waitForIndexing: false }));
|
|
719
|
+
server.tool("status", "Project overview dashboard. Returns file counts, index state, memory graph stats, git branch, uncommitted changes, and recent commits. " +
|
|
720
|
+
"One call for complete situational awareness at the start of a session.", {
|
|
721
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
722
|
+
}, withRequestActivity(async ({ format }) => ({
|
|
723
|
+
content: [{
|
|
724
|
+
type: "text",
|
|
725
|
+
text: await getStatus({ rootDir: rootDir(), format }),
|
|
726
|
+
}],
|
|
727
|
+
}), { toolName: "status", waitForIndexing: false }));
|
|
728
|
+
server.tool("efficiency_report", "Report ContextPro's persistent, auditable context-compression estimates by tool. History stores numeric metrics only—never prompts, source code, response bodies, or file paths. Token savings are estimates, not billing data.", {
|
|
729
|
+
scope: z.enum(["session", "24h", "7d", "all"]).optional().describe("History window. Default: this MCP session. Call at task completion and quote finalSummary."),
|
|
730
|
+
input_cost_per_million: z.number().nonnegative().optional().describe("Optional model input price in USD per million tokens. When provided, returns a clearly-labelled hypothetical cost estimate."),
|
|
731
|
+
contextpro_cost_usd: z.number().nonnegative().optional().describe("Optional known ContextPro operating cost for this window; deducted only when supplied. Never guess."),
|
|
732
|
+
history_limit: z.number().int().min(0).max(100).optional().describe("Maximum recent saving events. Use 0 for totals only; default 10."),
|
|
733
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or JSON. Default: text."),
|
|
734
|
+
}, withRequestActivity(async ({ scope, input_cost_per_million, contextpro_cost_usd, history_limit, format }) => ({
|
|
735
|
+
content: [{
|
|
736
|
+
type: "text",
|
|
737
|
+
text: await getEfficiencyReport({
|
|
738
|
+
rootDir: rootDir(),
|
|
739
|
+
scope,
|
|
740
|
+
sessionStartedAt,
|
|
741
|
+
sessionId: runtimeSessionId,
|
|
742
|
+
inputCostPerMillion: input_cost_per_million,
|
|
743
|
+
contextproCostUsd: contextpro_cost_usd,
|
|
744
|
+
historyLimit: history_limit,
|
|
745
|
+
format,
|
|
746
|
+
}),
|
|
747
|
+
}],
|
|
748
|
+
}), { toolName: "efficiency_report", waitForIndexing: false }));
|
|
749
|
+
server.tool("diff", "Structured git diff showing changed files, lines added/removed, and affected symbols. " +
|
|
750
|
+
"Supports: working tree (default), since a commit, vs a branch, a specific file, or staged/both scopes.", {
|
|
751
|
+
since: z.string().optional().describe("Show changes since this commit (e.g. HEAD~3, abc123)."),
|
|
752
|
+
branch: z.string().optional().describe("Compare current branch against this branch (e.g. main)."),
|
|
753
|
+
file: z.string().optional().describe("Show diff for a specific file (includes raw diff output)."),
|
|
754
|
+
scope: z.enum(["unstaged", "staged", "both"]).optional().describe("Working-tree scope. 'unstaged' (default) shows the worktree vs index. 'staged' shows index vs HEAD. 'both' shows worktree vs HEAD."),
|
|
755
|
+
}, withRequestActivity(async ({ since, branch, file, scope }) => ({
|
|
756
|
+
content: [{
|
|
757
|
+
type: "text",
|
|
758
|
+
text: await getDiff({ rootDir: rootDir(), since, branch, file, scope }),
|
|
759
|
+
}],
|
|
760
|
+
})));
|
|
761
|
+
server.tool("semantic_diff", "AST-level change summary: which SYMBOLS were added/removed/modified/moved since a git ref " +
|
|
762
|
+
"(default HEAD; pass a branch like 'main'). Ignores pure reformatting and includes brand-new untracked files. " +
|
|
763
|
+
"Token-lean; pair with change_impact/blast_radius for the per-PR review loop ('what changed since main').", {
|
|
764
|
+
since: z.string().optional().describe("Git ref to compare the working tree against (e.g. main, HEAD~3). Default: HEAD."),
|
|
765
|
+
format: z.enum(["text", "json"]).optional().describe("Output format. Default: text."),
|
|
766
|
+
}, withRequestActivity(async ({ since, format }) => ({
|
|
767
|
+
content: [{
|
|
768
|
+
type: "text",
|
|
769
|
+
text: await getSemanticDiff({ rootDir: rootDir(), since, format }),
|
|
770
|
+
}],
|
|
771
|
+
})));
|
|
772
|
+
server.tool("sql_schema", "Postgres/Supabase schema + RLS navigator from migration .sql files. Answers schema/RLS questions " +
|
|
773
|
+
"without psql: table columns + types + CHECK constraints, whether RLS is enabled, RLS policies " +
|
|
774
|
+
"(command/roles/USING predicate), and SECURITY DEFINER function bodies. Filter by table/policy/function name.", {
|
|
775
|
+
name: z.string().optional().describe("Filter to a single table, policy, or function name (case-insensitive)."),
|
|
776
|
+
targetPath: z.string().optional().describe("Restrict the .sql scan to a subtree (e.g. supabase/migrations)."),
|
|
777
|
+
format: z.enum(["text", "json"]).optional().describe("Output format. Default: text."),
|
|
778
|
+
}, withRequestActivity(async ({ name, targetPath, format }) => ({
|
|
779
|
+
content: [{
|
|
780
|
+
type: "text",
|
|
781
|
+
text: await getSqlSchema({ rootDir: rootDir(), name, targetPath, format }),
|
|
782
|
+
}],
|
|
783
|
+
})));
|
|
784
|
+
server.tool("related", "Show files related to a given file: what it imports, what imports it, same-directory siblings, test file, and hub membership. " +
|
|
785
|
+
"Requires reindex to have been run for import graph data.", {
|
|
786
|
+
file: z.string().min(1).describe("File path to find related files for (relative to root)."),
|
|
787
|
+
}, withRequestActivity(async ({ file }) => ({
|
|
788
|
+
content: [{
|
|
789
|
+
type: "text",
|
|
790
|
+
text: await getRelated({ rootDir: rootDir(), file }),
|
|
791
|
+
}],
|
|
792
|
+
}), { toolName: "related", ensureFreshIndex: true }));
|
|
793
|
+
server.tool("test_map", "Map source files to their test files using import graph and naming conventions. " +
|
|
794
|
+
"Modes: specific file, specific symbol, or changed files (dirty worktree, untracked files, or last commit when clean).", {
|
|
795
|
+
file: z.string().optional().describe("Source file to find tests for."),
|
|
796
|
+
symbol: z.string().optional().describe("Symbol name — finds which file defines it, then its tests."),
|
|
797
|
+
changed: z.boolean().optional().describe("Find tests for dirty/untracked files, or for the last commit when the worktree is clean."),
|
|
798
|
+
}, withRequestActivity(async ({ file, symbol, changed }) => ({
|
|
799
|
+
content: [{
|
|
800
|
+
type: "text",
|
|
801
|
+
text: await getTestMap({ rootDir: rootDir(), file, symbol, changed }),
|
|
802
|
+
}],
|
|
803
|
+
}), { toolName: "test_map", ensureFreshIndex: true }));
|
|
804
|
+
server.tool("test_impact", "Select only the tests affected by changes since a git ref (default HEAD; pass a branch like 'main'). " +
|
|
805
|
+
"Walks reverse import dependencies from changed source files to the tests that exercise them, plus any " +
|
|
806
|
+
"directly-changed test files. Forces a run-all fallback when build config / lockfiles change. " +
|
|
807
|
+
"The 'which tests must I run' half of the per-PR loop — O(diff), not O(repo).", {
|
|
808
|
+
since: z.string().optional().describe("Git ref to compare the working tree against (e.g. main, HEAD~3). Default: HEAD."),
|
|
809
|
+
format: z.enum(["text", "json"]).optional().describe("Output format. Default: text."),
|
|
810
|
+
}, withRequestActivity(async ({ since, format }) => ({
|
|
811
|
+
content: [{
|
|
812
|
+
type: "text",
|
|
813
|
+
text: await getTestImpact({ rootDir: rootDir(), since, format }),
|
|
814
|
+
}],
|
|
815
|
+
}), { toolName: "test_impact", ensureFreshIndex: true }));
|
|
816
|
+
server.tool("changed_since", "Per-PR review pack in one call: what SYMBOLS changed (semantic_diff) + which TESTS to run " +
|
|
817
|
+
"(test_impact) + which files DEPEND ON the changed ones (file-level blast radius), since a git ref " +
|
|
818
|
+
"(default HEAD; pass a branch like 'main'). The 'what changed since main' review loop, token-lean.", {
|
|
819
|
+
since: z.string().optional().describe("Git ref to compare against (e.g. main, HEAD~3). Default: HEAD."),
|
|
820
|
+
format: z.enum(["text", "json"]).optional().describe("Output format. Default: text."),
|
|
821
|
+
}, withRequestActivity(async ({ since, format }) => ({
|
|
822
|
+
content: [{
|
|
823
|
+
type: "text",
|
|
824
|
+
text: await getChangedSince({ rootDir: rootDir(), since, format }),
|
|
825
|
+
}],
|
|
826
|
+
}), { toolName: "changed_since", ensureFreshIndex: true }));
|
|
827
|
+
server.tool("explain", "Extract a specific function/class body with its dependencies, without reading the entire file. " +
|
|
828
|
+
"Provide symbol name to get its code + called functions. Provide file path for a structural overview.", {
|
|
829
|
+
symbol: z.string().optional().describe("Function, class, or variable name to explain."),
|
|
830
|
+
symbols: z.array(z.object({ symbol: z.string().min(1).max(200), file: z.string().max(1000).optional() })).min(1).max(6).optional().describe("Batch independent symbols in one call, instead of repeated reads. Use instead of symbol/file."),
|
|
831
|
+
max_tokens: z.number().int().min(512).max(12000).optional().describe("Batch response byte/4 budget; default 4000. Whole omitted bodies are reported."),
|
|
832
|
+
file: z.string().optional().describe("File to explain (overview mode if no symbol given)."),
|
|
833
|
+
depth: z.number().optional().describe("How many levels of called functions to include. Default: 1. Use 0 for just the symbol."),
|
|
834
|
+
format: z.enum(["source", "full"]).optional().describe("source: body and location only, no callers/tests/dependencies. Default full."),
|
|
835
|
+
}, withRequestActivity(async ({ symbol, symbols, max_tokens, file, depth, format }) => ({
|
|
836
|
+
content: [{
|
|
837
|
+
type: "text",
|
|
838
|
+
text: await explain({ rootDir: rootDir(), symbol, symbols, maxTokens: max_tokens, file, depth, format }),
|
|
839
|
+
}],
|
|
840
|
+
}), { toolName: "explain", ensureFreshIndex: true }));
|
|
841
|
+
const TASK_PACK_SHAPE = {
|
|
842
|
+
task: z.string().min(1).describe("Task description or engineering objective."),
|
|
843
|
+
previous_context_id: z.string().min(1).optional().describe("Context ID from a previous packet you still hold; return a delta. Omit after context loss to recover full context."),
|
|
844
|
+
preset: z.enum(["balanced", "keyword_only"]).optional().describe("balanced (default) or keyword_only for zero-provider lexical discovery."),
|
|
845
|
+
graph_hops: z.union([z.literal(0), z.literal(1), z.literal(2)]).optional().describe("Related import context: 0, 1 or 2 hops. Default 1 with CONTEXTPRO_CONNECTED_CONTEXT=true, otherwise 0. Relationships are heuristic."),
|
|
846
|
+
graph_max_files: z.number().int().min(1).max(10).optional().describe("Maximum additional graph-related file pointers. Default: 4."),
|
|
847
|
+
max_files: z.number().optional().describe("Maximum ranked files to include. Default: 8."),
|
|
848
|
+
max_memory: z.number().optional().describe("Maximum memory hits to include. Default: 3, max: 10."),
|
|
849
|
+
max_tokens: z.number().int().min(512).max(12000).optional().describe("Final response budget estimated as UTF-8 bytes/4, not tokenizer tokens. Default: 3000."),
|
|
850
|
+
include_tests: z.boolean().optional().describe("Include nearby tests. Default: true."),
|
|
851
|
+
include_recent_diffs: z.boolean().optional().describe("Include recent file-level diffs. Default: true."),
|
|
852
|
+
include_memory: z.boolean().optional().describe("Include project memory hits. Default: true."),
|
|
853
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
854
|
+
};
|
|
855
|
+
const runTaskPack = ({ task, max_files, max_memory, max_tokens, include_tests, include_recent_diffs, include_memory, format, previous_context_id, preset, graph_hops, graph_max_files }) => buildTaskPack({
|
|
856
|
+
rootDir: rootDir(),
|
|
857
|
+
task,
|
|
858
|
+
previousContextId: previous_context_id,
|
|
859
|
+
preset,
|
|
860
|
+
graphHops: graph_hops,
|
|
861
|
+
graphMaxFiles: graph_max_files,
|
|
862
|
+
maxFiles: max_files,
|
|
863
|
+
maxMemory: max_memory,
|
|
864
|
+
maxTokens: max_tokens,
|
|
865
|
+
includeTests: include_tests,
|
|
866
|
+
includeRecentDiffs: include_recent_diffs,
|
|
867
|
+
includeMemory: include_memory,
|
|
868
|
+
format,
|
|
869
|
+
});
|
|
870
|
+
server.tool("task_pack", "Assemble the minimum useful working set for a task: ranked files, nearby tests, recent diffs, memory, and risks.", TASK_PACK_SHAPE, withRequestActivity(async (args) => ({
|
|
871
|
+
content: [{ type: "text", text: await runTaskPack(args) }],
|
|
872
|
+
}), { useEmbeddingTracker: true, toolName: "task_pack", ensureFreshIndex: true }));
|
|
873
|
+
registerDeprecatedAlias("start_task", "task_pack", "Primary workflow entry point. Returns a compact task packet with ranked files, tests, recent diffs, project memory, freshness, and risks.", TASK_PACK_SHAPE, runTaskPack, { useEmbeddingTracker: true, ensureFreshIndex: true });
|
|
874
|
+
const CHANGE_IMPACT_SHAPE = {
|
|
875
|
+
files: z.array(z.string()).optional().describe("Files to analyze for impact."),
|
|
876
|
+
symbols: z.array(z.string()).optional().describe("Symbols to analyze for impact."),
|
|
877
|
+
include_tests: z.boolean().optional().describe("Include likely impacted tests. Default: true."),
|
|
878
|
+
include_recent_churn: z.boolean().optional().describe("Include recent churn in risk scoring. Default: true."),
|
|
879
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
880
|
+
};
|
|
881
|
+
const runChangeImpact = ({ files, symbols, include_tests, include_recent_churn, format }) => buildChangeImpact({
|
|
882
|
+
rootDir: rootDir(),
|
|
883
|
+
files,
|
|
884
|
+
symbols,
|
|
885
|
+
includeTests: include_tests,
|
|
886
|
+
includeRecentChurn: include_recent_churn,
|
|
887
|
+
format,
|
|
888
|
+
});
|
|
889
|
+
server.tool("change_impact", "Estimate what could break if specific files or symbols change, including related files, tests, churn, and reasons.", CHANGE_IMPACT_SHAPE, withRequestActivity(async (args) => ({
|
|
890
|
+
content: [{ type: "text", text: await runChangeImpact(args) }],
|
|
891
|
+
}), { toolName: "change_impact", ensureFreshIndex: true }));
|
|
892
|
+
registerDeprecatedAlias("impact", "change_impact", "Primary impact workflow. Estimates what could break for changed files or symbols, including related files, tests, churn, and reasons.", CHANGE_IMPACT_SHAPE, runChangeImpact, { ensureFreshIndex: true });
|
|
893
|
+
server.tool("impact_delta", "Compare a saved graph export with the current indexed graph after edits. Use graph_export before editing, then reindex and call this to see actual structural changes.", {
|
|
894
|
+
baseline_path: z.string().optional().describe("Baseline graph path relative to root. Default: .contextpro/graph.json."),
|
|
895
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
896
|
+
}, withRequestActivity(async ({ baseline_path, format }) => ({
|
|
897
|
+
content: [{
|
|
898
|
+
type: "text",
|
|
899
|
+
text: await impactDeltaTool({ rootDir: rootDir(), baselinePath: baseline_path, format }),
|
|
900
|
+
}],
|
|
901
|
+
}), { toolName: "impact_delta", ensureFreshIndex: true }));
|
|
902
|
+
const EDIT_SCOPE_SHAPE = {
|
|
903
|
+
task: z.string().min(1).describe("Task description or intended change."),
|
|
904
|
+
max_files: z.number().optional().describe("Maximum files to recommend. Default: 5."),
|
|
905
|
+
prefer_smallest_scope: z.boolean().optional().describe("Bias toward the smallest safe scope. Default: true."),
|
|
906
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
907
|
+
};
|
|
908
|
+
const runEditScope = ({ task, max_files, prefer_smallest_scope, format }) => buildEditScope({
|
|
909
|
+
rootDir: rootDir(),
|
|
910
|
+
task,
|
|
911
|
+
maxFiles: max_files,
|
|
912
|
+
preferSmallestScope: prefer_smallest_scope,
|
|
913
|
+
format,
|
|
914
|
+
});
|
|
915
|
+
server.tool("edit_scope", "Identify the smallest likely safe edit set for a task, including likely source files and nearby tests.", EDIT_SCOPE_SHAPE, withRequestActivity(async (args) => ({
|
|
916
|
+
content: [{ type: "text", text: await runEditScope(args) }],
|
|
917
|
+
}), { useEmbeddingTracker: true, toolName: "edit_scope", ensureFreshIndex: true }));
|
|
918
|
+
registerDeprecatedAlias("plan_edit", "edit_scope", "Primary edit planning workflow. Finds the smallest likely safe edit set for a task before reading or changing files.", EDIT_SCOPE_SHAPE, runEditScope, { useEmbeddingTracker: true, ensureFreshIndex: true });
|
|
919
|
+
const VALIDATE_SCOPE_SHAPE = {
|
|
920
|
+
task: z.string().min(1).describe("Task description or intended change."),
|
|
921
|
+
files: z.array(z.string()).optional().describe("Files already changed or known to be in scope. These take precedence over task inference."),
|
|
922
|
+
max_files: z.number().optional().describe("Maximum files to inspect when building validation guidance. Default: 5."),
|
|
923
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
924
|
+
};
|
|
925
|
+
const runValidateScope = ({ task, files, max_files, format }) => buildValidateScope({ rootDir: rootDir(), task, files, maxFiles: max_files, format });
|
|
926
|
+
server.tool("validate_scope", "After edit_scope, recommend the most relevant validation checks and commands. Pass files when you already know what changed; they take precedence over task inference.", VALIDATE_SCOPE_SHAPE, withRequestActivity(async (args) => ({
|
|
927
|
+
content: [{ type: "text", text: await runValidateScope(args) }],
|
|
928
|
+
}), { useEmbeddingTracker: true, toolName: "validate_scope", ensureFreshIndex: true }));
|
|
929
|
+
registerDeprecatedAlias("validate", "validate_scope", "Primary validation workflow. Recommends the smallest relevant checks and commands for a task's likely edit scope.", VALIDATE_SCOPE_SHAPE, runValidateScope, { useEmbeddingTracker: true, ensureFreshIndex: true });
|
|
930
|
+
server.tool("decision_brief", "Compress a subsystem or task into a short decision-oriented brief with ranked files, symbols, tests, hubs, and memory.", {
|
|
931
|
+
query: z.string().min(1).max(8000).describe("Subsystem, area, or task query."),
|
|
932
|
+
include_hubs: z.boolean().optional().describe("Include matching hubs. Default: true."),
|
|
933
|
+
include_memory: z.boolean().optional().describe("Include project memory. Default: true."),
|
|
934
|
+
include_tests: z.boolean().optional().describe("Include nearby tests. Default: true."),
|
|
935
|
+
max_memory: z.number().optional().describe("Maximum memory hits to include. Default: 3, max: 10."),
|
|
936
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
937
|
+
}, withRequestActivity(async ({ query, include_hubs, include_memory, include_tests, max_memory, format }) => ({
|
|
938
|
+
content: [{
|
|
939
|
+
type: "text",
|
|
940
|
+
text: await buildDecisionBrief({
|
|
941
|
+
rootDir: rootDir(),
|
|
942
|
+
query,
|
|
943
|
+
includeHubs: include_hubs,
|
|
944
|
+
includeMemory: include_memory,
|
|
945
|
+
includeTests: include_tests,
|
|
946
|
+
maxMemory: max_memory,
|
|
947
|
+
format,
|
|
948
|
+
}),
|
|
949
|
+
}],
|
|
950
|
+
}), { useEmbeddingTracker: true, toolName: "decision_brief", ensureFreshIndex: true }));
|
|
951
|
+
const MEMORY_RECORD_TASK_SHAPE = {
|
|
952
|
+
task: z.string().min(1).max(300).describe("Task or issue name."),
|
|
953
|
+
summary: z.string().min(1).max(8000).describe("What changed or was learned."),
|
|
954
|
+
source_evidence: z.array(z.object({ file: z.string().max(240), hash: z.string().regex(/^[a-f0-9]{64}$/).optional(), start: z.number().int().positive().optional(), end: z.number().int().positive().optional() }).strict()).max(256).optional().describe("Source supporting this memory. Whole-file hashes are captured and rechecked on retrieval."),
|
|
955
|
+
supersedes: z.array(z.string()).max(50).optional().describe("Previous memory IDs explicitly superseded; history is retained."),
|
|
956
|
+
validation_refs: z.array(z.string()).max(20).optional().describe("Validation result artifact IDs; host-reported status is checked for current applicability."),
|
|
957
|
+
files: z.array(z.string().max(240)).max(256).optional().describe("Files touched or relevant to the task."),
|
|
958
|
+
assumptions: z.array(z.string().max(1000)).max(20).optional().describe("Assumptions made during the task."),
|
|
959
|
+
evidence: z.array(z.string().max(1000)).max(20).optional().describe("Evidence supporting the outcome."),
|
|
960
|
+
open_risks: z.array(z.string().max(1000)).max(20).optional().describe("Risks that remain unresolved."),
|
|
961
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
962
|
+
};
|
|
963
|
+
const runMemoryRecordTask = ({ task, summary, files, assumptions, evidence, open_risks, format, source_evidence, supersedes, validation_refs }) => recordTaskMemory({
|
|
964
|
+
rootDir: rootDir(),
|
|
965
|
+
task,
|
|
966
|
+
summary,
|
|
967
|
+
files,
|
|
968
|
+
assumptions,
|
|
969
|
+
evidence,
|
|
970
|
+
sourceEvidence: source_evidence, supersedes, validationRefs: validation_refs,
|
|
971
|
+
openRisks: open_risks,
|
|
972
|
+
format,
|
|
973
|
+
});
|
|
974
|
+
server.tool("memory_record_task", "Persist useful task outcomes with linked files, assumptions, evidence, and open risks for future sessions.", MEMORY_RECORD_TASK_SHAPE, withRequestActivity(async (args) => ({
|
|
975
|
+
content: [{ type: "text", text: await runMemoryRecordTask(args) }],
|
|
976
|
+
}), { useEmbeddingTracker: true, toolName: "memory_record_task" }));
|
|
977
|
+
server.tool("validation_record", "Record host-executed checks against a pre-check validation snapshot. Results are host-reported and become stale when inputs change.", {
|
|
978
|
+
snapshot_id: z.string().regex(/^validation_snapshot-[a-f0-9]{14}$/),
|
|
979
|
+
command: z.string().min(1).max(2000), exit_code: z.number().int(),
|
|
980
|
+
runtime: z.string().min(1).max(1000), evidence: z.string().max(8000).optional(),
|
|
981
|
+
}, withRequestActivity(async ({ snapshot_id, command, exit_code, runtime, evidence }) => ({ content: [{ type: "text",
|
|
982
|
+
text: JSON.stringify(await recordValidation({ rootDir: rootDir(), snapshotId: snapshot_id, command, exitCode: exit_code, runtime, evidence })) }],
|
|
983
|
+
}), { toolName: "validation_record", waitForIndexing: false }));
|
|
984
|
+
server.tool("team_status", "Inspect local shared task ownership, dependencies, readiness, file leases and retained handoff history. Advisory coordination only.", {
|
|
985
|
+
task_id: z.string().min(1).max(120).optional().describe("Inspect one task and its history."),
|
|
986
|
+
before_event_id: z.number().int().positive().optional().describe("Continue older history using olderEventsBefore from the last response."),
|
|
987
|
+
event_limit: z.number().int().min(1).max(50).optional().describe("History records to return; default 20."),
|
|
988
|
+
}, withRequestActivity(async ({ task_id, before_event_id, event_limit }) => ({ content: [{ type: "text",
|
|
989
|
+
text: JSON.stringify(await teamStatus(rootDir(), Date.now(), { taskId: task_id, beforeEventId: before_event_id, eventLimit: event_limit })) }] }), { toolName: "team_status", waitForIndexing: false }));
|
|
990
|
+
server.tool("team_task", "Explicit local task coordination. Claims and individual-file leases are atomic and expire after five minutes; renew while working. No agents are spawned or messaged.", {
|
|
991
|
+
action: z.enum(["create", "update", "claim", "renew", "release", "block", "complete", "handoff"]),
|
|
992
|
+
task_id: z.string().min(1).max(128).optional(), title: z.string().min(1).max(500).optional(),
|
|
993
|
+
dependencies: z.array(z.string().min(1).max(128)).max(50).optional(), owner: z.string().min(1).max(128).optional(),
|
|
994
|
+
files: z.array(z.string().min(1).max(240)).max(100).optional(), note: z.string().max(8000).optional(),
|
|
995
|
+
handoff_id: z.string().regex(/^handoff-[a-f0-9]{14}$/).optional(),
|
|
996
|
+
}, withRequestActivity(async ({ action, task_id, title, dependencies, owner, files, note, handoff_id }) => ({ content: [{ type: "text",
|
|
997
|
+
text: JSON.stringify(await teamTask({ rootDir: rootDir(), action, taskId: task_id, title, dependencies, owner, files, note, handoffId: handoff_id })) }],
|
|
998
|
+
}), { toolName: "team_task", waitForIndexing: false }));
|
|
999
|
+
server.tool("compiler_diagnostics", "Read current TS/JS or configured Pyright diagnostics for explicit files. Does not run tests. Missing language servers fail explicitly.", {
|
|
1000
|
+
files: z.array(z.string().min(1).max(240)).min(1).max(100),
|
|
1001
|
+
}, withRequestActivity(async ({ files }) => ({ content: [{ type: "text", text: JSON.stringify(await compilerDiagnostics({ rootDir: rootDir(), files })) }] }), { toolName: "compiler_diagnostics", ensureFreshIndex: true }));
|
|
1002
|
+
server.tool("rename_preview", "Preview compiler-backed TS/JS or Python rename edits. Inspect the immutable artifact before rename_apply; source/config changes invalidate it. No source is edited.", {
|
|
1003
|
+
file: z.string().min(1).max(240), line: z.number().int().positive(), column: z.number().int().positive(), new_name: z.string().min(1).max(200),
|
|
1004
|
+
max_tokens: z.number().int().min(512).max(12000).optional(),
|
|
1005
|
+
}, withRequestActivity(async ({ file, line, column, new_name, max_tokens }) => ({ content: [{ type: "text", text: await previewRename({ rootDir: rootDir(), file, line, column, newName: new_name, maxTokens: max_tokens }) }] }), { toolName: "rename_preview", ensureFreshIndex: true }));
|
|
1006
|
+
server.tool("rename_apply", "Apply only a previously inspected compiler rename preview, with workspace and per-file hash checks and a restore point. No textual fallback.", {
|
|
1007
|
+
preview_id: z.string().regex(/^rename_preview-[a-f0-9]{14}$/),
|
|
1008
|
+
}, withRequestActivity(async ({ preview_id }) => ({ content: [{ type: "text", text: JSON.stringify(await applyRename({ rootDir: rootDir(), previewId: preview_id })) }] }), { toolName: "rename_apply", waitForIndexing: false }));
|
|
1009
|
+
registerDeprecatedAlias("remember", "memory_record_task", "Primary memory workflow. Records a task outcome with linked files, assumptions, evidence, and open risks for future sessions.", MEMORY_RECORD_TASK_SHAPE, runMemoryRecordTask, { useEmbeddingTracker: true });
|
|
1010
|
+
server.tool("workspace_context", "List active registered projects and recent sessions without merging project-internal code context.", {
|
|
1011
|
+
client_id: z.string().optional().describe("Filter to a specific client or engagement."),
|
|
1012
|
+
include_projects: z.boolean().optional().describe("Include project entries. Default: true."),
|
|
1013
|
+
include_recent_sessions: z.boolean().optional().describe("Include recent session summaries. Default: true."),
|
|
1014
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
1015
|
+
}, withRequestActivity(async ({ client_id, include_projects, include_recent_sessions, format }) => ({
|
|
1016
|
+
content: [{
|
|
1017
|
+
type: "text",
|
|
1018
|
+
text: await getWorkspaceContext({
|
|
1019
|
+
clientId: client_id,
|
|
1020
|
+
includeProjects: include_projects,
|
|
1021
|
+
includeRecentSessions: include_recent_sessions,
|
|
1022
|
+
format,
|
|
1023
|
+
}),
|
|
1024
|
+
}],
|
|
1025
|
+
}), { toolName: "workspace_context" }));
|
|
1026
|
+
server.tool("promote_pattern", "Promote a project memory item into a client-level or global reusable pattern record.", {
|
|
1027
|
+
source_memory_id: z.string().min(1).describe("Memory node ID to promote."),
|
|
1028
|
+
target_scope: z.enum(["client", "global"]).describe("Where to promote the pattern."),
|
|
1029
|
+
sanitize: z.boolean().optional().describe("Mark the promoted pattern as sanitized. Default: true."),
|
|
1030
|
+
summary: z.string().min(1).describe("Reusable pattern summary."),
|
|
1031
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
1032
|
+
}, withRequestActivity(async ({ source_memory_id, target_scope, sanitize, summary, format }) => ({
|
|
1033
|
+
content: [{
|
|
1034
|
+
type: "text",
|
|
1035
|
+
text: await promotePattern({
|
|
1036
|
+
rootDir: rootDir(),
|
|
1037
|
+
sourceMemoryId: source_memory_id,
|
|
1038
|
+
targetScope: target_scope,
|
|
1039
|
+
sanitize,
|
|
1040
|
+
summary,
|
|
1041
|
+
format,
|
|
1042
|
+
}),
|
|
1043
|
+
}],
|
|
1044
|
+
}), { toolName: "promote_pattern" }));
|
|
1045
|
+
server.tool("find_symbol", "Find symbols (functions, classes, methods, types) by name path pattern. " +
|
|
1046
|
+
"A name path is a Serena-style path through the symbol tree within a file, e.g., \"ClassName/methodName\" or \"/AbsoluteRoot/Nested\". " +
|
|
1047
|
+
"Returns location, signature, and optionally body and children. Use this before symbolic edits or to navigate a file's API surface. " +
|
|
1048
|
+
"Symbol extraction here is tree-sitter/regex-based, not a real language server — if an LSP-backed tool (e.g. Serena) is available for this language, prefer it when you need compiler-exact symbol resolution.", {
|
|
1049
|
+
name_path_pattern: z.string().min(1).describe("Name path pattern. Use \"name\" to match anywhere, \"Class/method\" for relative, \"/Class/method\" for absolute."),
|
|
1050
|
+
relative_path: z.string().optional().describe("Restrict search to a specific file. Omit to scan the whole project."),
|
|
1051
|
+
include_body: z.boolean().optional().describe("Include the symbol's source body in results. Use judiciously."),
|
|
1052
|
+
depth: z.number().optional().describe("Depth of descendants to retrieve (e.g., 1 returns class methods). Default: 0."),
|
|
1053
|
+
max_matches: z.number().optional().describe("Maximum matches to return. Default: 50."),
|
|
1054
|
+
substring_matching: z.boolean().optional().describe("If true, the last path element matches by substring."),
|
|
1055
|
+
include_kinds: z.array(z.string()).optional().describe("Only return symbols of these kinds (function, class, method, etc.)."),
|
|
1056
|
+
exclude_kinds: z.array(z.string()).optional().describe("Exclude symbols of these kinds."),
|
|
1057
|
+
format: z.enum(["text", "json"]).optional().describe("Return plain text or structured JSON. Default: text."),
|
|
1058
|
+
}, withRequestActivity(async ({ name_path_pattern, relative_path, include_body, depth, max_matches, substring_matching, include_kinds, exclude_kinds, format }) => ({
|
|
1059
|
+
content: [{
|
|
1060
|
+
type: "text",
|
|
1061
|
+
text: await findSymbol({
|
|
1062
|
+
rootDir: rootDir(),
|
|
1063
|
+
namePathPattern: name_path_pattern,
|
|
1064
|
+
relativePath: relative_path,
|
|
1065
|
+
includeBody: include_body,
|
|
1066
|
+
depth,
|
|
1067
|
+
maxMatches: max_matches,
|
|
1068
|
+
substringMatching: substring_matching,
|
|
1069
|
+
includeKinds: include_kinds,
|
|
1070
|
+
excludeKinds: exclude_kinds,
|
|
1071
|
+
format,
|
|
1072
|
+
}),
|
|
1073
|
+
}],
|
|
1074
|
+
}), { toolName: "find_symbol", ensureFreshIndex: true }));
|
|
1075
|
+
server.tool("replace_symbol_body", "Replace the entire body (definition through closing brace/dedent) of a symbol identified by its name path. " +
|
|
1076
|
+
"Creates a shadow restore point before writing. Use this for surgical refactors instead of file-level edits. " +
|
|
1077
|
+
"Only call after find_symbol/skeleton/explain confirms the target.", {
|
|
1078
|
+
expected_content_hash: z.string().regex(/^[a-f0-9]{64}$/).optional().describe("Expected current SHA-256 from find_symbol; stale edits fail with a conflict."),
|
|
1079
|
+
relative_path: z.string().min(1).describe("File containing the symbol (relative to project root)."),
|
|
1080
|
+
name_path: z.string().min(1).describe("Name path of the symbol to replace (e.g., \"ClassName/methodName\")."),
|
|
1081
|
+
body: z.string().min(1).describe("Full replacement text for the symbol definition, including signature line."),
|
|
1082
|
+
message: z.string().optional().describe("Optional restore-point message for traceability."),
|
|
1083
|
+
}, withRequestActivity(async ({ relative_path, name_path, body, message, expected_content_hash }) => ({
|
|
1084
|
+
content: [{
|
|
1085
|
+
type: "text",
|
|
1086
|
+
text: await replaceSymbolBody({ rootDir: rootDir(), relativePath: relative_path, namePath: name_path, body, message, expectedContentHash: expected_content_hash }),
|
|
1087
|
+
}],
|
|
1088
|
+
}), { toolName: "replace_symbol_body" }));
|
|
1089
|
+
server.tool("insert_after_symbol", "Insert new code immediately after the closing line of an existing symbol. " +
|
|
1090
|
+
"Useful for adding sibling functions, methods, or class members without re-reading the file. Creates a shadow restore point.", {
|
|
1091
|
+
expected_content_hash: z.string().regex(/^[a-f0-9]{64}$/).optional().describe("Expected current SHA-256 from find_symbol; stale edits fail with a conflict."),
|
|
1092
|
+
relative_path: z.string().min(1).describe("File to modify (relative to project root)."),
|
|
1093
|
+
name_path: z.string().min(1).describe("Name path of the anchor symbol (insertion happens after its end)."),
|
|
1094
|
+
body: z.string().min(1).describe("Code to insert after the symbol."),
|
|
1095
|
+
message: z.string().optional().describe("Optional restore-point message."),
|
|
1096
|
+
}, withRequestActivity(async ({ relative_path, name_path, body, message, expected_content_hash }) => ({
|
|
1097
|
+
content: [{
|
|
1098
|
+
type: "text",
|
|
1099
|
+
text: await insertAroundSymbol({ rootDir: rootDir(), relativePath: relative_path, namePath: name_path, body, position: "after", message, expectedContentHash: expected_content_hash }),
|
|
1100
|
+
}],
|
|
1101
|
+
}), { toolName: "insert_after_symbol" }));
|
|
1102
|
+
server.tool("insert_before_symbol", "Insert new code immediately before the first line of an existing symbol. " +
|
|
1103
|
+
"Useful for prepending imports, helpers, or decorators. Creates a shadow restore point.", {
|
|
1104
|
+
expected_content_hash: z.string().regex(/^[a-f0-9]{64}$/).optional().describe("Expected current SHA-256 from find_symbol; stale edits fail with a conflict."),
|
|
1105
|
+
relative_path: z.string().min(1).describe("File to modify (relative to project root)."),
|
|
1106
|
+
name_path: z.string().min(1).describe("Name path of the anchor symbol (insertion happens before its start)."),
|
|
1107
|
+
body: z.string().min(1).describe("Code to insert before the symbol."),
|
|
1108
|
+
message: z.string().optional().describe("Optional restore-point message."),
|
|
1109
|
+
}, withRequestActivity(async ({ relative_path, name_path, body, message, expected_content_hash }) => ({
|
|
1110
|
+
content: [{
|
|
1111
|
+
type: "text",
|
|
1112
|
+
text: await insertAroundSymbol({ rootDir: rootDir(), relativePath: relative_path, namePath: name_path, body, position: "before", message, expectedContentHash: expected_content_hash }),
|
|
1113
|
+
}],
|
|
1114
|
+
}), { toolName: "insert_before_symbol" }));
|
|
1115
|
+
server.tool("rename_symbol", "Rename a symbol in its defining file and propagate the rename to callers and importers based on the indexed graph. " +
|
|
1116
|
+
"Creates shadow restore points for the defining file and each updated cross-file site. " +
|
|
1117
|
+
"Best-effort textual rename — does not understand scopes (though it does skip string literals and comments); always inspect blast_radius first to confirm safety. " +
|
|
1118
|
+
"If an LSP-backed rename tool (e.g. Serena) is available for this language, prefer it for scope-exact renames.", {
|
|
1119
|
+
expected_content_hash: z.string().regex(/^[a-f0-9]{64}$/).optional().describe("Expected current SHA-256 from find_symbol; stale edits fail with a conflict."),
|
|
1120
|
+
relative_path: z.string().min(1).describe("File where the symbol is defined."),
|
|
1121
|
+
name_path: z.string().min(1).describe("Name path of the symbol to rename."),
|
|
1122
|
+
new_name: z.string().min(1).describe("The new identifier name."),
|
|
1123
|
+
message: z.string().optional().describe("Optional restore-point message."),
|
|
1124
|
+
}, withRequestActivity(async ({ relative_path, name_path, new_name, message, expected_content_hash }) => ({
|
|
1125
|
+
content: [{
|
|
1126
|
+
type: "text",
|
|
1127
|
+
text: await renameSymbol({ rootDir: rootDir(), relativePath: relative_path, namePath: name_path, newName: new_name, message, expectedContentHash: expected_content_hash }),
|
|
1128
|
+
}],
|
|
1129
|
+
}), { toolName: "rename_symbol" }));
|
|
1130
|
+
}
|
|
1131
|
+
//# sourceMappingURL=register-tools.js.map
|