Compare commits
1 Commits
v1.15.0-mc
...
v1.16.0-co
| Author | SHA1 | Date | |
|---|---|---|---|
| 29c7d051b6 |
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
All notable changes per release tag. Most recent on top, ordered by tag creation date (which matches the git history). Tag names follow `vMAJOR.MINOR.PATCH-slug` — the slug describes what shipped, so the tag name alone is enough to recall the batch.
|
All notable changes per release tag. Most recent on top, ordered by tag creation date (which matches the git history). Tag names follow `vMAJOR.MINOR.PATCH-slug` — the slug describes what shipped, so the tag name alone is enough to recall the batch.
|
||||||
|
|
||||||
|
## v1.16.0-codesight-merge — 2026-05-24
|
||||||
|
|
||||||
|
Ports codesight's highest-value analysis capabilities into the codecontext sidecar as 4 new MCP tools. Tier 1 (graph queries on existing edges, no re-parsing): `get_blast_radius` (BFS reverse-edge traversal — "what breaks if I change this file?", with depth tracking) and `get_hot_files` (most-imported files ranked by incoming edge count — change-risk indicators). Tier 2 (tree-sitter AST re-parsing on demand): `get_routes` (Fastify/Express HTTP route extraction with method, path, file, line, inferred tags for db/auth/cache) and `get_middleware` (middleware registration detection via import-name heuristics and app.register/addHook/setErrorHandler patterns, classifying as auth/cors/rate-limit/security/error-handler/logging/validation). All 4 tools use `defer s.graphMu.RUnlock()` for consistent mutex discipline (reviewer caught that the initial implementation released the lock early on the Tier 2 tools). Route object-property extraction delegates to `extractStringValue` for template-literal handling (reviewer catch). codecontext sidecar rebuilt from `/opt/forks/codecontext` commit `b19e646`, tagged `v1.16.0-codesight-merge`. BooCode wrapper tools follow the existing codecontext pattern — 4 new files in `apps/server/src/services/tools/codecontext/`, registered in ALL_TOOLS. 29 new Go tests + 363/363 BooCode server tests passing. No schema changes, no frontend changes.
|
||||||
|
|
||||||
## v1.15.0-mcp-multi — 2026-05-24
|
## v1.15.0-mcp-multi — 2026-05-24
|
||||||
|
|
||||||
Multi-server MCP client with stdio + Streamable HTTP transports, JSON config file, and per-agent tool glob patterns. Generalizes the v1.14.1 single-server Context7 PoC into a registry of named MCP servers with per-server graceful degradation. JSON config at `/data/mcp.json` (bind-mounted alongside `AGENTS.md`) matches opencode's `mcpServers` schema shape so server entries are copy-pasteable. Config file missing = no MCP (opt-in by file presence). Stdio transport spawns a persistent subprocess via the SDK's `StdioClientTransport` with NDJSON framing; Streamable HTTP reuses the v1.14.1 pattern via `StreamableHTTPClientTransport`. Tool prefix generalized from `context7_<name>` to `<serverName>_<toolName>` with a reverse `toolToServer` map for dispatch routing. Per-agent AGENTS.md `tools:` field now supports glob patterns (`context7_*`, `!web_*`) via `matchToolGlob` (last-match-wins, `!` prefix denies); replaces the exact-match `.includes()` in `stream-phase.ts`. Glob patterns bypass `ALL_TOOL_NAMES` validation in the parser since MCP tool names aren't known at parse time. `refreshToolNames()` in `agents.ts` rebuilds the `DEFAULT_TOOLS` snapshot after `appendMcpTools` so agents without explicit `tools:` lists see MCP tools — reviewer caught that the module-load-time snapshot would permanently exclude late-registered tools. Read-only invariant preserved: all MCP tools with `readOnlyHint: false` rejected at discovery. Result size capped at 5MB. Shutdown hook closes all transports. v1.14.1 env vars (`MCP_CONTEXT7_URL`, `MCP_CONTEXT7_API_KEY`) removed — superseded by the config file. Default `data/mcp.json` ships with Context7 disabled; flip `"enabled": true` to activate. 363/363 server tests passing (27 new: multi-server wrapping, glob matching, routing, degradation). No schema changes, no frontend changes.
|
Multi-server MCP client with stdio + Streamable HTTP transports, JSON config file, and per-agent tool glob patterns. Generalizes the v1.14.1 single-server Context7 PoC into a registry of named MCP servers with per-server graceful degradation. JSON config at `/data/mcp.json` (bind-mounted alongside `AGENTS.md`) matches opencode's `mcpServers` schema shape so server entries are copy-pasteable. Config file missing = no MCP (opt-in by file presence). Stdio transport spawns a persistent subprocess via the SDK's `StdioClientTransport` with NDJSON framing; Streamable HTTP reuses the v1.14.1 pattern via `StreamableHTTPClientTransport`. Tool prefix generalized from `context7_<name>` to `<serverName>_<toolName>` with a reverse `toolToServer` map for dispatch routing. Per-agent AGENTS.md `tools:` field now supports glob patterns (`context7_*`, `!web_*`) via `matchToolGlob` (last-match-wins, `!` prefix denies); replaces the exact-match `.includes()` in `stream-phase.ts`. Glob patterns bypass `ALL_TOOL_NAMES` validation in the parser since MCP tool names aren't known at parse time. `refreshToolNames()` in `agents.ts` rebuilds the `DEFAULT_TOOLS` snapshot after `appendMcpTools` so agents without explicit `tools:` lists see MCP tools — reviewer caught that the module-load-time snapshot would permanently exclude late-registered tools. Read-only invariant preserved: all MCP tools with `readOnlyHint: false` rejected at discovery. Result size capped at 5MB. Shutdown hook closes all transports. v1.14.1 env vars (`MCP_CONTEXT7_URL`, `MCP_CONTEXT7_API_KEY`) removed — superseded by the config file. Default `data/mcp.json` ships with Context7 disabled; flip `"enabled": true` to activate. 363/363 server tests passing (27 new: multi-server wrapping, glob matching, routing, degradation). No schema changes, no frontend changes.
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import {
|
|||||||
watchChanges,
|
watchChanges,
|
||||||
getSemanticNeighborhoods,
|
getSemanticNeighborhoods,
|
||||||
getFrameworkAnalysis,
|
getFrameworkAnalysis,
|
||||||
|
getBlastRadius,
|
||||||
|
getHotFiles,
|
||||||
|
getRoutes,
|
||||||
|
getMiddleware,
|
||||||
} from './tools/codecontext/index.js';
|
} from './tools/codecontext/index.js';
|
||||||
// v1.13.17-cross-repo-reads: cross-repo read grant request tool. Paired
|
// v1.13.17-cross-repo-reads: cross-repo read grant request tool. Paired
|
||||||
// with the pause-on-pending-grant branch in inference/tool-phase.ts and the
|
// with the pause-on-pending-grant branch in inference/tool-phase.ts and the
|
||||||
@@ -680,6 +684,11 @@ export let ALL_TOOLS: ToolDef<unknown>[] = [
|
|||||||
watchChanges as ToolDef<unknown>,
|
watchChanges as ToolDef<unknown>,
|
||||||
getSemanticNeighborhoods as ToolDef<unknown>,
|
getSemanticNeighborhoods as ToolDef<unknown>,
|
||||||
getFrameworkAnalysis as ToolDef<unknown>,
|
getFrameworkAnalysis as ToolDef<unknown>,
|
||||||
|
// v1.16: codesight-merge tools. Backed by the same codecontext sidecar.
|
||||||
|
getBlastRadius as ToolDef<unknown>,
|
||||||
|
getHotFiles as ToolDef<unknown>,
|
||||||
|
getRoutes as ToolDef<unknown>,
|
||||||
|
getMiddleware as ToolDef<unknown>,
|
||||||
// v1.13.17-cross-repo-reads: paired with the pause-on-pending-grant
|
// v1.13.17-cross-repo-reads: paired with the pause-on-pending-grant
|
||||||
// branch in tool-phase.ts. Read-only — only ever READS files; the only
|
// branch in tool-phase.ts. Read-only — only ever READS files; the only
|
||||||
// state change is appending to sessions.allowed_read_paths via the
|
// state change is appending to sessions.allowed_read_paths via the
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import type { ToolDef } from '../../tools.js';
|
||||||
|
import { callCodecontext, type CodecontextResponse } from '../../codecontext_client.js';
|
||||||
|
|
||||||
|
export const GetBlastRadiusInput = z.object({
|
||||||
|
file_path: z.string().trim().min(1),
|
||||||
|
});
|
||||||
|
export type GetBlastRadiusInputT = z.infer<typeof GetBlastRadiusInput>;
|
||||||
|
|
||||||
|
const DESCRIPTION =
|
||||||
|
'Returns all files that depend (transitively) on the given file, with depth tracking. ' +
|
||||||
|
'Use to assess the impact of changing a file — "what breaks if I modify this?" ' +
|
||||||
|
'Traverses the import graph in reverse via BFS. Results sorted by distance (closest dependents first).';
|
||||||
|
|
||||||
|
export async function executeGetBlastRadius(
|
||||||
|
input: GetBlastRadiusInputT,
|
||||||
|
projectPath: string,
|
||||||
|
fetcher: typeof fetch = fetch,
|
||||||
|
): Promise<CodecontextResponse> {
|
||||||
|
return callCodecontext(
|
||||||
|
{ toolName: 'get_blast_radius', args: { file_path: input.file_path }, projectPath },
|
||||||
|
fetcher,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getBlastRadius: ToolDef<GetBlastRadiusInputT> = {
|
||||||
|
name: 'get_blast_radius',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
inputSchema: GetBlastRadiusInput,
|
||||||
|
jsonSchema: {
|
||||||
|
type: 'function',
|
||||||
|
function: {
|
||||||
|
name: 'get_blast_radius',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
file_path: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Absolute or project-relative path to the file to analyze.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['file_path'],
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async execute(input, projectRoot) {
|
||||||
|
return await executeGetBlastRadius(input, projectRoot);
|
||||||
|
},
|
||||||
|
};
|
||||||
50
apps/server/src/services/tools/codecontext/get_hot_files.ts
Normal file
50
apps/server/src/services/tools/codecontext/get_hot_files.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import type { ToolDef } from '../../tools.js';
|
||||||
|
import { callCodecontext, type CodecontextResponse } from '../../codecontext_client.js';
|
||||||
|
|
||||||
|
export const GetHotFilesInput = z.object({
|
||||||
|
limit: z.number().int().min(1).max(100).optional(),
|
||||||
|
});
|
||||||
|
export type GetHotFilesInputT = z.infer<typeof GetHotFilesInput>;
|
||||||
|
|
||||||
|
const DESCRIPTION =
|
||||||
|
'Returns the most-imported files in the project, ranked by incoming import count. ' +
|
||||||
|
'Hot files are high-risk change targets — many other files depend on them. ' +
|
||||||
|
'Use to identify core modules and assess refactoring risk.';
|
||||||
|
|
||||||
|
export async function executeGetHotFiles(
|
||||||
|
input: GetHotFilesInputT,
|
||||||
|
projectPath: string,
|
||||||
|
fetcher: typeof fetch = fetch,
|
||||||
|
): Promise<CodecontextResponse> {
|
||||||
|
return callCodecontext(
|
||||||
|
{ toolName: 'get_hot_files', args: input.limit != null ? { limit: input.limit } : {}, projectPath },
|
||||||
|
fetcher,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHotFiles: ToolDef<GetHotFilesInputT> = {
|
||||||
|
name: 'get_hot_files',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
inputSchema: GetHotFilesInput,
|
||||||
|
jsonSchema: {
|
||||||
|
type: 'function',
|
||||||
|
function: {
|
||||||
|
name: 'get_hot_files',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
limit: {
|
||||||
|
type: 'number',
|
||||||
|
description: 'Maximum number of files to return (default 20, max 100).',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async execute(input, projectRoot) {
|
||||||
|
return await executeGetHotFiles(input, projectRoot);
|
||||||
|
},
|
||||||
|
};
|
||||||
41
apps/server/src/services/tools/codecontext/get_middleware.ts
Normal file
41
apps/server/src/services/tools/codecontext/get_middleware.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import type { ToolDef } from '../../tools.js';
|
||||||
|
import { callCodecontext, type CodecontextResponse } from '../../codecontext_client.js';
|
||||||
|
|
||||||
|
export const GetMiddlewareInput = z.object({});
|
||||||
|
export type GetMiddlewareInputT = z.infer<typeof GetMiddlewareInput>;
|
||||||
|
|
||||||
|
const DESCRIPTION =
|
||||||
|
'Detects middleware registrations in the project. Identifies auth, CORS, rate-limit, ' +
|
||||||
|
'security-headers, error-handler, logging, and validation middleware by analyzing ' +
|
||||||
|
'import names (@fastify/cors, helmet, etc.) and registration patterns ' +
|
||||||
|
'(app.register, app.addHook, app.setErrorHandler).';
|
||||||
|
|
||||||
|
export async function executeGetMiddleware(
|
||||||
|
_input: GetMiddlewareInputT,
|
||||||
|
projectPath: string,
|
||||||
|
fetcher: typeof fetch = fetch,
|
||||||
|
): Promise<CodecontextResponse> {
|
||||||
|
return callCodecontext({ toolName: 'get_middleware', args: {}, projectPath }, fetcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMiddleware: ToolDef<GetMiddlewareInputT> = {
|
||||||
|
name: 'get_middleware',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
inputSchema: GetMiddlewareInput,
|
||||||
|
jsonSchema: {
|
||||||
|
type: 'function',
|
||||||
|
function: {
|
||||||
|
name: 'get_middleware',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {},
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async execute(input, projectRoot) {
|
||||||
|
return await executeGetMiddleware(input, projectRoot);
|
||||||
|
},
|
||||||
|
};
|
||||||
50
apps/server/src/services/tools/codecontext/get_routes.ts
Normal file
50
apps/server/src/services/tools/codecontext/get_routes.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import type { ToolDef } from '../../tools.js';
|
||||||
|
import { callCodecontext, type CodecontextResponse } from '../../codecontext_client.js';
|
||||||
|
|
||||||
|
export const GetRoutesInput = z.object({
|
||||||
|
framework: z.string().trim().optional(),
|
||||||
|
});
|
||||||
|
export type GetRoutesInputT = z.infer<typeof GetRoutesInput>;
|
||||||
|
|
||||||
|
const DESCRIPTION =
|
||||||
|
'Extracts HTTP routes from the project via tree-sitter AST analysis. ' +
|
||||||
|
'Detects Fastify and Express route registrations (app.get, app.post, app.route, router.use, etc.) ' +
|
||||||
|
'with method, path, file, line number, and inferred tags (db, auth, cache). ' +
|
||||||
|
'Optional framework filter narrows to "fastify" or "express".';
|
||||||
|
|
||||||
|
export async function executeGetRoutes(
|
||||||
|
input: GetRoutesInputT,
|
||||||
|
projectPath: string,
|
||||||
|
fetcher: typeof fetch = fetch,
|
||||||
|
): Promise<CodecontextResponse> {
|
||||||
|
const args: Record<string, unknown> = {};
|
||||||
|
if (input.framework) args.framework = input.framework;
|
||||||
|
return callCodecontext({ toolName: 'get_routes', args, projectPath }, fetcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRoutes: ToolDef<GetRoutesInputT> = {
|
||||||
|
name: 'get_routes',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
inputSchema: GetRoutesInput,
|
||||||
|
jsonSchema: {
|
||||||
|
type: 'function',
|
||||||
|
function: {
|
||||||
|
name: 'get_routes',
|
||||||
|
description: DESCRIPTION,
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
framework: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Filter to a specific framework: "fastify" or "express". Omit for all.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async execute(input, projectRoot) {
|
||||||
|
return await executeGetRoutes(input, projectRoot);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// v1.12 Track B.2: codecontext tool registry. Re-exports the 8 ToolDefs so
|
// codecontext tool registry. Re-exports ToolDefs so tools.ts can pull them
|
||||||
// tools.ts can pull them in one line.
|
// in one line. v1.12: 8 original tools. v1.16: +4 codesight-merge tools.
|
||||||
|
|
||||||
export { getCodebaseOverview } from './get_codebase_overview.js';
|
export { getCodebaseOverview } from './get_codebase_overview.js';
|
||||||
export { getFileAnalysis } from './get_file_analysis.js';
|
export { getFileAnalysis } from './get_file_analysis.js';
|
||||||
@@ -9,3 +9,7 @@ export { getDependencies } from './get_dependencies.js';
|
|||||||
export { watchChanges } from './watch_changes.js';
|
export { watchChanges } from './watch_changes.js';
|
||||||
export { getSemanticNeighborhoods } from './get_semantic_neighborhoods.js';
|
export { getSemanticNeighborhoods } from './get_semantic_neighborhoods.js';
|
||||||
export { getFrameworkAnalysis } from './get_framework_analysis.js';
|
export { getFrameworkAnalysis } from './get_framework_analysis.js';
|
||||||
|
export { getBlastRadius } from './get_blast_radius.js';
|
||||||
|
export { getHotFiles } from './get_hot_files.js';
|
||||||
|
export { getRoutes } from './get_routes.js';
|
||||||
|
export { getMiddleware } from './get_middleware.js';
|
||||||
|
|||||||
Reference in New Issue
Block a user