@akira-tl/forgerelay 0.3.7 → 0.4.1

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 CHANGED
@@ -4,6 +4,36 @@ All notable ForgeRelay changes are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.4.1] - 2026-08-11
8
+
9
+ ### Added
10
+
11
+ - Added `code.intelligence` `hover` support through the same shared Language-service and filesystem synchronization path as definition lookup.
12
+
13
+ ### Changed
14
+
15
+ - Hover responses now normalize Markdown/plaintext `MarkupContent` and supported legacy `MarkedString` payloads into a stable Agent-facing `contents` value with optional language and normalized range metadata.
16
+ - Language Servers that do not advertise hover return `code.operation_unsupported` without invalidating the shared Language service.
17
+
18
+ ## [0.4.0] - 2026-08-11
19
+
20
+ ### Added
21
+
22
+ - Added `code.intelligence` as a Capability Gateway-only LSP code-intelligence surface. ForgeRelay 0.4.0 ships the first complete `definition` tracer bullet without changing the canonical nine Core MCP tools.
23
+ - Added Language-server definitions with project (`.forgerelay/language-servers.json`), global (`~/.forgerelay/config.json`), and built-in discovery precedence. Common built-ins cover TypeScript/JavaScript, Pyright, rust-analyzer, gopls, and clangd when those executables are already installed.
24
+ - Added a deterministic child-process fake LSP server and MCP-level regression seam covering initialize/shutdown, document synchronization, definition normalization, shared Language-service identity, capacity limits, and server-initiated edit rejection.
25
+
26
+ ### Changed
27
+
28
+ - Code-intelligence positions use ForgeRelay's 1-based line and Unicode code-point column contract and are converted internally to the position encoding negotiated with the Language Server.
29
+ - Language services are shared by canonical Language project root plus effective server-definition fingerprint rather than logical workspace ID, remain capacity/idle bounded, and use structured no-shell process launch over Microsoft's `vscode-jsonrpc` / `vscode-languageserver-protocol` substrate.
30
+ - Language-server configuration can explicitly disable built-in discovery; nested Language projects resolve by walking ancestors of the requested source path instead of recursively scanning the Workspace.
31
+
32
+ ### Fixed
33
+
34
+ - External LSP definition targets are marked as informational external locations without expanding ForgeRelay file-read authority, including symlink-escape protection and canonical Workspace-root handling.
35
+ - Language-server startup failure/timeout, unsupported operations, invalid positions, configuration ambiguity, capacity exhaustion, and other policy failures now use stable ForgeRelay `code.*` errors instead of leaking raw JSON-RPC failures.
36
+
7
37
  ## [0.3.7] - 2026-08-10
8
38
 
9
39
  ### Added
@@ -0,0 +1,11 @@
1
+ # Code Intelligence
2
+
3
+ Use the `code.intelligence` Capability for read-only semantic code navigation backed by Language servers that are already installed on the user's machine or explicitly configured for the project.
4
+
5
+ ForgeRelay does not install Language servers. It discovers supported executables when available and accepts explicit definitions from the global ForgeRelay config or `<workspace>/.forgerelay/language-servers.json`. Project definitions override global definitions, and global definitions override built-in discovery. An explicit definition may disable discovery with `enabled: false`.
6
+
7
+ ForgeRelay 0.4.1 supports `definition` and `hover`. Both position-based operations accept a workspace-relative source `path` plus 1-based `line` and `column` values. Columns are Unicode code-point positions; ForgeRelay converts them to the position encoding negotiated with the Language server.
8
+
9
+ Code-intelligence results use ForgeRelay-owned shapes rather than raw LSP wire types. Definition returns normalized locations. Hover returns one `contents` string, an optional legacy `language`, and an optional ForgeRelay-normalized `range`; plaintext, Markdown `MarkupContent`, and supported legacy `MarkedString` payloads are normalized before reaching the Agent. A definition may identify an External code location outside the Workspace, but that does not expand ForgeRelay's allowed roots or grant the file tools permission to read that path.
10
+
11
+ Language-server definitions use structured process configuration rather than shell command strings. A project configuration entry may contain `command`, `args`, `env`, `languages`, `extensions`, `languageIdByExtension`, `projectMarkers`, and `enabled` fields. Use `languageIdByExtension` when one server definition covers multiple language IDs whose extensions do not map one-to-one by array position. The server command is launched directly without a shell.
@@ -34,6 +34,11 @@ const CAPABILITY_GUIDE_DEFINITIONS = [
34
34
  description: "Long-running bash processes, processId interaction, PTY, and platform edges.",
35
35
  whenToRead: "Read for running or interactive command issues.",
36
36
  },
37
+ {
38
+ name: "code-intelligence",
39
+ description: "Read-only semantic code navigation backed by external Language servers.",
40
+ whenToRead: "Read before using code.intelligence or configuring Language servers.",
41
+ },
37
42
  ];
38
43
  function capabilityGuidesDir() {
39
44
  return fileURLToPath(new URL("../capabilities", import.meta.url));
@@ -75,6 +80,7 @@ export function buildCapabilityFingerprint(config, version, context = {}) {
75
80
  "process.lifecycle",
76
81
  "hooks.lifecycle",
77
82
  "capability-guides.read",
83
+ "code.intelligence",
78
84
  ];
79
85
  if (config.subagents) {
80
86
  capabilities.push("subagent.profiles");
@@ -115,6 +115,12 @@ export class CapabilityRegistry {
115
115
  }
116
116
  export function createCapabilityRegistry(dependencies) {
117
117
  const hooksCheckInput = z.object({}).strict();
118
+ const codeIntelligenceInput = z.object({
119
+ operation: z.enum(["definition", "hover"]),
120
+ path: z.string().min(1),
121
+ line: z.number().int(),
122
+ column: z.number().int(),
123
+ }).strict();
118
124
  return new CapabilityRegistry([
119
125
  {
120
126
  name: "hooks.check",
@@ -144,6 +150,20 @@ export function createCapabilityRegistry(dependencies) {
144
150
  run: async (_input, context) => dependencies.reviewChanges.run(context),
145
151
  }]
146
152
  : []),
153
+ ...(dependencies.codeIntelligence
154
+ ? [{
155
+ name: "code.intelligence",
156
+ description: "Read semantic code information through an available Language server without changing the Workspace.",
157
+ guideName: "code-intelligence",
158
+ readGuideBeforeFirstUse: true,
159
+ inputSchema: codeIntelligenceInput,
160
+ availability: () => ({
161
+ available: dependencies.codeIntelligence?.available ?? false,
162
+ reason: dependencies.codeIntelligence?.unavailableReason,
163
+ }),
164
+ run: async (input, context) => dependencies.codeIntelligence.run(input, context),
165
+ }]
166
+ : []),
147
167
  ...(dependencies.downloadArtifact
148
168
  ? [{
149
169
  name: "artifact.download",
package/dist/config.js CHANGED
@@ -223,6 +223,7 @@ export function loadConfig(env = process.env) {
223
223
  subagents: productEnv(env, "SUBAGENTS") === undefined
224
224
  ? files.config.subagents === true
225
225
  : parseBoolean(productEnv(env, "SUBAGENTS")),
226
+ languageServers: files.config.languageServers ?? {},
226
227
  agentDir: resolve(expandHomePath(productEnv(env, "AGENT_DIR") ?? files.config.agentDir ?? defaultAgentDir())),
227
228
  systemInstructionsPath: parseSystemInstructionsPath(productEnv(env, "SYSTEM_INSTRUCTIONS_PATH") ?? files.config.systemInstructionsPath),
228
229
  hooks: mergeHookConfigs(parseHookConfig(files.config.hooks), parseHookConfig(files.hooks), files.hookFiles),
@@ -0,0 +1,8 @@
1
+ export class CodeIntelligenceError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = "CodeIntelligenceError";
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ export {};