glm-mcp-copilot 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,10 @@ calls `glm_agent` / `glm_delegate` / `glm_recommend` / `glm_status` to offload w
15
15
  - A **`GLM` custom agent (subagent)** — restricted to the `glm` tools, so it *must* delegate to GLM
16
16
  (the Copilot analog of the Claude `glm` subagent). Pick it from the chat mode dropdown or hand off to it.
17
17
  - A **delegation-policy instructions file** so Copilot offloads to GLM automatically.
18
+ - A **PreToolUse auto-routing hook** (`glm_router_hook.mjs`) — fires before the default model does work
19
+ itself and nudges delegating to GLM (the Copilot analog of the Claude `glm_subagent_router` hook).
20
+ Installed to `.github/hooks/glm.hooks.json` per-project, or `~/.copilot/hooks/glm.hooks.json` globally.
21
+ Non-blocking (always allows the tool call). VS Code **preview feature**.
18
22
 
19
23
  ## Prerequisites
20
24
  - **VS Code** with **GitHub Copilot + Copilot Chat**, and **Agent mode** available (MCP support).
@@ -35,7 +39,8 @@ Run it **from your project folder** (it sets up that workspace). It:
35
39
  2. writes your key to that server's `.env`,
36
40
  3. registers the server in `.vscode/mcp.json` (VS Code's `servers` format),
37
41
  4. installs the **`GLM` custom agent** → `.github/agents/glm.agent.md`,
38
- 5. writes `.github/copilot-instructions.md` (the delegation policy).
42
+ 5. writes `.github/copilot-instructions.md` (the delegation policy),
43
+ 6. installs the **PreToolUse auto-routing hook** → `.github/hooks/glm.hooks.json`.
39
44
 
40
45
  ### Global (all projects)
41
46
  Set it up once for **every** workspace with `--global`:
@@ -46,6 +51,7 @@ Global mode writes to VS Code's **user config** instead of one workspace:
46
51
  - the `glm` server → the **user `mcp.json`** (all workspaces),
47
52
  - the **`GLM` custom agent** → `~/.copilot/agents/glm.agent.md`,
48
53
  - the delegation policy → `~/.copilot/instructions/glm.instructions.md` (with `applyTo: '**'`),
54
+ - the **PreToolUse auto-routing hook** → `~/.copilot/hooks/glm.hooks.json`,
49
55
  - and it registers those locations + enables agent mode in **user `settings.json`**
50
56
  (`chat.agentFilesLocations`, `chat.instructionsFilesLocations`, `chat.agent.enabled`).
51
57
 
@@ -57,14 +63,20 @@ Then in VS Code: **Reload Window → open Copilot Chat → Agent mode → start
57
63
  Servers`). Ask Copilot to do a coding task; it will call `glm_agent`.
58
64
 
59
65
  ## How it differs from the Claude Code version
60
- Near-parity — Copilot **does** have subagents (custom agents):
66
+ **Essentially full parity** — Copilot now has all three primitives:
61
67
  - **`glm_*` MCP tools** in **agent mode** (same server as the Claude edition).
62
68
  - A **`GLM` custom agent (subagent)** restricted to the `glm` tools — the analog of the Claude `glm`
63
69
  subagent (forced to delegate to GLM). Invoke it from the mode dropdown or via an agent handoff.
70
+ - A **PreToolUse agent hook** (`glm_router_hook.mjs`) that auto-nudges delegation — the analog of the
71
+ Claude `glm_subagent_router` hook: before the default model does work itself, it suggests delegating
72
+ to `glm_agent` (non-blocking; it only injects advisory context, never denies a tool call).
64
73
  - **Instructions files** steer delegation (the CLAUDE.md equivalent).
65
74
 
66
- The only thing Copilot lacks is Claude Code's **PreToolUse hook** (auto-routing on every subagent
67
- spawn) so in Copilot, delegation is driven by the subagent + instructions rather than a hook.
75
+ Small differences that remain:
76
+ - VS Code hooks **ignore the matcher**, so the hook fires on *every* tool call and filters by
77
+ `tool_name` internally (and advises at most once per session to stay quiet).
78
+ - VS Code **hooks are a preview feature**; flip them on in Copilot settings if your build hides them.
79
+ - There is **no separate `glm-code` full-GLM launcher** (Claude's standalone all-GLM entry point).
68
80
 
69
81
  Everything else — the GLM agent loop, peak-aware model pick, cost bias, token cap, usage ledger,
70
82
  `dry_run` oversight — is the **same server**, so it behaves identically once a tool is called.
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ // glm_router_hook.mjs — PreToolUse hook for GitHub Copilot (VS Code agent hooks).
3
+ // The Copilot analog of the Claude Code glm_subagent_router hook: before the default
4
+ // model does work itself, it nudges delegating to GLM (glm_agent, ~10x cheaper).
5
+ // Non-blocking — it only injects advisory context, never denies a tool call.
6
+ import { readFileSync, existsSync, writeFileSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { tmpdir } from "node:os";
9
+
10
+ let raw = "";
11
+ try { raw = readFileSync(0, "utf8"); } catch {}
12
+ let data = {};
13
+ try { data = JSON.parse(raw || "{}"); } catch {}
14
+
15
+ const tool = String(data.tool_name || "");
16
+ const sessionId = String(data.session_id || "");
17
+
18
+ // "Copilot is about to do real work itself" (not via GLM).
19
+ const WORK_TOOLS = new Set([
20
+ "editFiles", "createFile", "deleteFile", "applyPatch",
21
+ "runTerminalCommand", "runCommands", "runInTerminal",
22
+ ]);
23
+
24
+ const allow = (extra) =>
25
+ process.stdout.write(JSON.stringify({ continue: true, hookSpecificOutput: { permissionDecision: "allow", ...(extra || {}) }, ...(extra && extra.systemMessage ? { systemMessage: extra.systemMessage } : {}) }));
26
+
27
+ // Never interfere with GLM's own tools, reads, or anything that isn't direct work.
28
+ if (!WORK_TOOLS.has(tool) || /glm/i.test(tool)) { allow(); process.exit(0); }
29
+
30
+ // Advise only ONCE per session to avoid noise on every edit.
31
+ const marker = join(tmpdir(), "glm-router-" + (sessionId || "nosess") + ".flag");
32
+ if (sessionId && existsSync(marker)) { allow(); process.exit(0); }
33
+ try { if (sessionId) writeFileSync(marker, "1"); } catch {}
34
+
35
+ const context =
36
+ "GLM router: the default model is about to do this work itself. If this is a well-specified, " +
37
+ "self-contained coding / edit / refactor / test / docs task, delegate it to GLM instead — call " +
38
+ "glm_agent with the goal + the absolute repo path (workdir) so it runs on GLM tokens (~10x cheaper), " +
39
+ "then review the result. Keep sensitive/secret, vision, parallel, very-large-context (>128K), and " +
40
+ "heavy dependent tool-loop work on the default model. Run glm_recommend if unsure.";
41
+
42
+ process.stdout.write(JSON.stringify({
43
+ continue: true,
44
+ systemMessage: "GLM router: consider delegating this to glm_agent (~10x cheaper).",
45
+ hookSpecificOutput: { permissionDecision: "allow", additionalContext: context },
46
+ }));
47
+ process.exit(0);
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // install-copilot.mjs — set up GLM as a delegate for GitHub Copilot / Copilot Chat (VS Code agent mode).
3
3
  // Installs the shared GLM MCP server, registers it in the workspace .vscode/mcp.json (VS Code's
4
- // "servers" format), and writes .github/copilot-instructions.md so Copilot delegates to GLM.
5
- // It does NOT touch any Claude Code setup.
4
+ // "servers" format), writes .github/copilot-instructions.md so Copilot delegates to GLM, and installs a
5
+ // PreToolUse agent hook (.github/hooks/glm.hooks.json or ~/.copilot/hooks/glm.hooks.json) that nudges
6
+ // delegation to GLM. It does NOT touch any Claude Code setup.
6
7
  //
7
8
  // Usage:
8
9
  // node install-copilot.mjs --key YOUR_ZAI_KEY # set up in the current workspace
@@ -63,6 +64,13 @@ cpSync(join(SELF, "glm-mcp"), join(SERVER_HOME, "glm-mcp"), {
63
64
  },
64
65
  });
65
66
 
67
+ // Also stage the PreToolUse hook script inside the server home so the hook has a stable absolute path.
68
+ step("Staging the GLM PreToolUse hook script");
69
+ copyFileSync(join(SELF, "glm_router_hook.mjs"), join(SERVER_HOME, "glm_router_hook.mjs"));
70
+ const hookScript = join(SERVER_HOME, "glm_router_hook.mjs").replace(/\\/g, "/");
71
+ const hookCmd = 'node "' + hookScript + '"';
72
+ log(" " + join(SERVER_HOME, "glm_router_hook.mjs"));
73
+
66
74
  // 2. .env (API key)
67
75
  step("Setting up .env");
68
76
  const envPath = join(SERVER_HOME, "glm-mcp", ".env");
@@ -87,6 +95,7 @@ if (!SKIP_NPM) {
87
95
  const idx = join(SERVER_HOME, "glm-mcp", "src", "index.js").replace(/\\/g, "/");
88
96
  const AGENTS_HOME = join(homedir(), ".copilot", "agents"); // global custom-agent location
89
97
  const INSTR_HOME = join(homedir(), ".copilot", "instructions"); // global instructions location
98
+ const HOOKS_HOME = join(homedir(), ".copilot", "hooks"); // global hooks location
90
99
 
91
100
  function mergeMcp(mcpPath) {
92
101
  mkdirSync(dirname(mcpPath), { recursive: true });
@@ -106,6 +115,12 @@ function copyInto(dir, srcFile) {
106
115
  copyFileSync(join(SELF, srcFile), dest);
107
116
  return dest;
108
117
  }
118
+ function writeHookFile(dir) {
119
+ mkdirSync(dir, { recursive: true });
120
+ const p = join(dir, "glm.hooks.json");
121
+ writeFileSync(p, JSON.stringify({ hooks: { PreToolUse: [ { type: "command", command: hookCmd, timeout: 10 } ] } }, null, 2) + "\n");
122
+ return p;
123
+ }
109
124
 
110
125
  if (GLOBAL) {
111
126
  const userDir = vscodeUserDir();
@@ -118,6 +133,9 @@ if (GLOBAL) {
118
133
  step("Installing GLOBAL Copilot instructions -> " + INSTR_HOME);
119
134
  log(" " + copyInto(INSTR_HOME, "glm.instructions.md"));
120
135
 
136
+ step("Installing the GLM PreToolUse hook (auto-routing) -> " + HOOKS_HOME);
137
+ log(" " + writeHookFile(HOOKS_HOME));
138
+
121
139
  step("Updating VS Code user settings.json (locations + toggles)");
122
140
  const setPath = join(userDir, "settings.json");
123
141
  let settings = {};
@@ -157,6 +175,9 @@ if (GLOBAL) {
157
175
  } else {
158
176
  log(" copilot-instructions.md already has the policy");
159
177
  }
178
+
179
+ step("Installing the GLM PreToolUse hook (auto-routing) -> .github/hooks/");
180
+ log(" " + writeHookFile(join(WORKSPACE, ".github", "hooks")));
160
181
  }
161
182
 
162
183
  log("\n✅ Done. Next steps:");
@@ -167,6 +188,6 @@ log(" 4. Use it: pick the 'GLM' agent in the chat mode dropdown, or ask the mai
167
188
  log(" 'use glm_agent to …'. Run glm_status for the GLM usage ledger.");
168
189
  log(
169
190
  GLOBAL
170
- ? "\nGLOBAL mode: the glm server, GLM subagent, and delegation policy now apply to ALL your VS Code workspaces."
191
+ ? "\nGLOBAL mode: the glm server, GLM subagent, delegation policy, and PreToolUse auto-routing hook now apply to ALL your VS Code workspaces."
171
192
  : "\nWorkspace mode: current project only. Re-run with --global to apply to every project."
172
193
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "glm-mcp-copilot",
3
- "version": "1.2.0",
4
- "description": "GLM (Zhipu/Z.ai) as a cheap delegate for GitHub Copilot / Copilot Chat in VS Code — the same GLM MCP tools (glm_agent/glm_delegate/glm_recommend/glm_status) plus a GLM custom agent (subagent), wired into VS Code agent mode, installable globally.",
3
+ "version": "1.3.0",
4
+ "description": "GLM (Zhipu/Z.ai) as a cheap delegate for GitHub Copilot / Copilot Chat in VS Code — the same GLM MCP tools (glm_agent/glm_delegate/glm_recommend/glm_status), a GLM custom agent (subagent), and a PreToolUse auto-routing hook, wired into VS Code agent mode, installable globally.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "glm-mcp-copilot": "install-copilot.mjs"
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "glm-mcp/",
11
11
  "install-copilot.mjs",
12
+ "glm_router_hook.mjs",
12
13
  "glm.agent.md",
13
14
  "glm.instructions.md",
14
15
  "copilot-instructions.md",