glm-mcp-copilot 1.1.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
@@ -12,7 +12,13 @@ calls `glm_agent` / `glm_delegate` / `glm_recommend` / `glm_status` to offload w
12
12
  - **`glm_delegate`** — GLM drafts text you place.
13
13
  - **`glm_recommend`** — free advisory: GLM vs the default model.
14
14
  - **`glm_status`** — usage ledger (proof of GLM tokens spent) + config.
15
- - A **`.github/copilot-instructions.md`** delegation policy so Copilot offloads to GLM automatically.
15
+ - A **`GLM` custom agent (subagent)** — restricted to the `glm` tools, so it *must* delegate to GLM
16
+ (the Copilot analog of the Claude `glm` subagent). Pick it from the chat mode dropdown or hand off to it.
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**.
16
22
 
17
23
  ## Prerequisites
18
24
  - **VS Code** with **GitHub Copilot + Copilot Chat**, and **Agent mode** available (MCP support).
@@ -32,7 +38,9 @@ Run it **from your project folder** (it sets up that workspace). It:
32
38
  1. installs the GLM MCP server to `~/.glm-mcp/glm-mcp/` and runs `npm install`,
33
39
  2. writes your key to that server's `.env`,
34
40
  3. registers the server in `.vscode/mcp.json` (VS Code's `servers` format),
35
- 4. writes `.github/copilot-instructions.md` (the delegation policy).
41
+ 4. installs the **`GLM` custom agent** → `.github/agents/glm.agent.md`,
42
+ 5. writes `.github/copilot-instructions.md` (the delegation policy),
43
+ 6. installs the **PreToolUse auto-routing hook** → `.github/hooks/glm.hooks.json`.
36
44
 
37
45
  ### Global (all projects)
38
46
  Set it up once for **every** workspace with `--global`:
@@ -40,22 +48,35 @@ Set it up once for **every** workspace with `--global`:
40
48
  npx glm-mcp-copilot --global --key YOUR_ZAI_API_KEY
41
49
  ```
42
50
  Global mode writes to VS Code's **user config** instead of one workspace:
43
- - the `glm` server → the **user `mcp.json`** (available in all workspaces), and
44
- - the delegation policy **user `settings.json`** (`github.copilot.chat.codeGeneration.instructions`).
51
+ - the `glm` server → the **user `mcp.json`** (all workspaces),
52
+ - the **`GLM` custom agent** `~/.copilot/agents/glm.agent.md`,
53
+ - the delegation policy → `~/.copilot/instructions/glm.instructions.md` (with `applyTo: '**'`),
54
+ - the **PreToolUse auto-routing hook** → `~/.copilot/hooks/glm.hooks.json`,
55
+ - and it registers those locations + enables agent mode in **user `settings.json`**
56
+ (`chat.agentFilesLocations`, `chat.instructionsFilesLocations`, `chat.agent.enabled`).
45
57
 
46
- > The global **server** is reliable across VS Code versions. The global **instructions** setting is
47
- > VS-Code-version-dependent (its exact key is evolving) if Copilot ignores it in your version, the
48
- > tools are still there; just nudge it ("use glm_agent to…"), or add a repo `.github/copilot-instructions.md`.
58
+ > Uses the current (non-deprecated) instructions mechanism `.instructions.md` files, **not** the old
59
+ > `codeGeneration.instructions` settings array (deprecated in VS Code 1.102; the installer migrates off it).
49
60
  > Use `--vscode-user-dir PATH` if your VS Code User folder isn't auto-detected (Insiders/VSCodium/portable).
50
61
 
51
62
  Then in VS Code: **Reload Window → open Copilot Chat → Agent mode → start the `glm` server** (`MCP: List
52
63
  Servers`). Ask Copilot to do a coding task; it will call `glm_agent`.
53
64
 
54
65
  ## How it differs from the Claude Code version
55
- Copilot doesn't have Claude Code's *subagents* or *PreToolUse hooks*, so there's no auto-routing hook or
56
- `glm` subagent. Instead:
57
- - **MCP tools** (`glm_*`) are available in **agent mode** and Copilot calls them.
58
- - **`.github/copilot-instructions.md`** steers Copilot to delegate to GLM (the CLAUDE.md equivalent).
66
+ **Essentially full parity** Copilot now has all three primitives:
67
+ - **`glm_*` MCP tools** in **agent mode** (same server as the Claude edition).
68
+ - A **`GLM` custom agent (subagent)** restricted to the `glm` tools the analog of the Claude `glm`
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).
73
+ - **Instructions files** steer delegation (the CLAUDE.md equivalent).
74
+
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).
59
80
 
60
81
  Everything else — the GLM agent loop, peak-aware model pick, cost bias, token cap, usage ledger,
61
82
  `dry_run` oversight — is the **same server**, so it behaves identically once a tool is called.
package/glm.agent.md ADDED
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: GLM
3
+ description: Cheap GLM delegate — offloads coding/edit/refactor/test/docs work to the GLM model (~10x cheaper than the default) via the glm MCP server. Use for well-specified, self-contained tasks; keep sensitive/vision/parallel/huge-context/heavy-tool-loop work on the default model.
4
+ tools: ['glm/*']
5
+ user-invocable: true
6
+ ---
7
+
8
+ You are the **GLM delegate**. You have ONLY the `glm` MCP server's tools — **you cannot edit files or
9
+ run commands yourself**, so you must route all real work through GLM (this keeps the work, and the
10
+ tokens, on GLM — ~10× cheaper than the default model).
11
+
12
+ ## How you work
13
+ 1. **Do the work via GLM.** For any coding / edit / refactor / test / docs task, call **`glm/glm_agent`**
14
+ with the goal + the **absolute repo path** (`workdir`). GLM reads, writes, edits, and runs the files
15
+ itself, end-to-end. For pure text, use **`glm/glm_delegate`**.
16
+ 2. **Check fit when unsure.** Call **`glm/glm_recommend`**; if it says the task should stay on the
17
+ default model (sensitive, vision, parallel, >128K context, heavy dependent tool-loops), say so and stop.
18
+ 3. **Report the GLM stats.** Surface the `=== GLM STATS ===` block `glm_agent` returns (model + tokens
19
+ delegated + cost) so it's clear GLM did the work and how much it spent. Run **`glm/glm_status`** for
20
+ the cumulative usage ledger.
21
+
22
+ Never claim to have done work you didn't route through GLM — you have no other tools.
@@ -0,0 +1,18 @@
1
+ ---
2
+ applyTo: '**'
3
+ ---
4
+
5
+ # GLM delegation
6
+
7
+ The **glm** MCP server is available in agent mode (`glm_agent`, `glm_delegate`, `glm_recommend`,
8
+ `glm_status`). GLM (Zhipu/Z.ai) is **~10× cheaper** than the default model — offload work to it.
9
+
10
+ - For any implementation / codegen / edit / refactor / test / docs task, call **`glm_agent`** with the
11
+ goal + the absolute repo path (`workdir`) so GLM does it end-to-end on **GLM tokens** — or hand it to
12
+ the **GLM** custom agent (subagent). Prefer this over doing the file work yourself.
13
+ - Use **`glm_delegate`** for pure text.
14
+ - **To actually spend GLM tokens (not the default model's), call the glm tools — don't do the work
15
+ inline yourself.**
16
+ - Keep on the default model: sensitive/secret code, vision/images, parallel work, very large context
17
+ (>128K), and heavy dependent tool-loops. Unsure? call **`glm_recommend`**.
18
+ - `glm_status` shows the GLM usage ledger — proof of GLM tokens spent.
@@ -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");
@@ -82,8 +90,12 @@ if (!SKIP_NPM) {
82
90
  execSync("npm install --no-audit --no-fund", { cwd: join(SERVER_HOME, "glm-mcp"), stdio: "inherit" });
83
91
  }
84
92
 
85
- // 4 & 5. Register the server + delegation policy globally (all workspaces) or in this workspace.
93
+ // 4-6. Register the server, the `glm` custom agent (subagent), and the delegation policy —
94
+ // globally (all workspaces) or in this workspace.
86
95
  const idx = join(SERVER_HOME, "glm-mcp", "src", "index.js").replace(/\\/g, "/");
96
+ const AGENTS_HOME = join(homedir(), ".copilot", "agents"); // global custom-agent location
97
+ const INSTR_HOME = join(homedir(), ".copilot", "instructions"); // global instructions location
98
+ const HOOKS_HOME = join(homedir(), ".copilot", "hooks"); // global hooks location
87
99
 
88
100
  function mergeMcp(mcpPath) {
89
101
  mkdirSync(dirname(mcpPath), { recursive: true });
@@ -97,61 +109,85 @@ function mergeMcp(mcpPath) {
97
109
  writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + "\n");
98
110
  return mcpPath;
99
111
  }
100
-
101
- const CONCISE_POLICY =
102
- "GLM delegation: prefer the glm MCP tools. For any coding / edit / refactor / test / docs task, call " +
103
- "glm_agent with the goal + the absolute repo path (workdir) so GLM does it end-to-end on GLM tokens " +
104
- "(~10x cheaper) — don't do the file work yourself when GLM can. Use glm_delegate for pure text. Keep " +
105
- "sensitive/secret code, vision, parallel work, >128K context, and heavy dependent tool-loops on the " +
106
- "default model. Run glm_status for the GLM usage ledger.";
112
+ function copyInto(dir, srcFile) {
113
+ mkdirSync(dir, { recursive: true });
114
+ const dest = join(dir, srcFile);
115
+ copyFileSync(join(SELF, srcFile), dest);
116
+ return dest;
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
+ }
107
124
 
108
125
  if (GLOBAL) {
109
126
  const userDir = vscodeUserDir();
110
127
  step("Registering glm GLOBALLY (VS Code user mcp.json) -> " + userDir);
111
128
  log(" " + mergeMcp(join(userDir, "mcp.json")));
112
129
 
113
- step("Adding GLOBAL Copilot instructions (VS Code user settings.json)");
130
+ step("Installing the GLM custom agent (subagent) -> " + AGENTS_HOME);
131
+ log(" " + copyInto(AGENTS_HOME, "glm.agent.md"));
132
+
133
+ step("Installing GLOBAL Copilot instructions -> " + INSTR_HOME);
134
+ log(" " + copyInto(INSTR_HOME, "glm.instructions.md"));
135
+
136
+ step("Installing the GLM PreToolUse hook (auto-routing) -> " + HOOKS_HOME);
137
+ log(" " + writeHookFile(HOOKS_HOME));
138
+
139
+ step("Updating VS Code user settings.json (locations + toggles)");
114
140
  const setPath = join(userDir, "settings.json");
115
141
  let settings = {};
116
142
  if (existsSync(setPath)) {
117
143
  try { settings = JSON.parse(readFileSync(setPath, "utf8")); } catch { settings = {}; }
118
144
  writeFileSync(setPath + ".bak-" + Date.now(), readFileSync(setPath));
119
145
  }
120
- const K = "github.copilot.chat.codeGeneration.instructions";
121
- const arr = Array.isArray(settings[K]) ? settings[K] : [];
122
- if (!arr.some((e) => e && typeof e.text === "string" && e.text.includes("glm_agent"))) {
123
- arr.push({ text: CONCISE_POLICY });
124
- settings[K] = arr;
125
- writeFileSync(setPath, JSON.stringify(settings, null, 2) + "\n");
126
- log(" added to " + setPath);
127
- } else {
128
- log(" policy already present");
146
+ const agentsGlob = AGENTS_HOME.replace(/\\/g, "/");
147
+ const instrGlob = INSTR_HOME.replace(/\\/g, "/");
148
+ settings["chat.agentFilesLocations"] = { ...(settings["chat.agentFilesLocations"] || {}), [agentsGlob]: true };
149
+ settings["chat.instructionsFilesLocations"] = { ...(settings["chat.instructionsFilesLocations"] || {}), [instrGlob]: true };
150
+ settings["github.copilot.chat.codeGeneration.useInstructionFiles"] = true;
151
+ settings["chat.agent.enabled"] = true;
152
+ // Migrate off the deprecated inline-instructions setting (remove our old entry if present).
153
+ const DEP = "github.copilot.chat.codeGeneration.instructions";
154
+ if (Array.isArray(settings[DEP])) {
155
+ settings[DEP] = settings[DEP].filter((e) => !(e && typeof e.text === "string" && e.text.includes("glm_agent")));
156
+ if (settings[DEP].length === 0) delete settings[DEP];
129
157
  }
158
+ writeFileSync(setPath, JSON.stringify(settings, null, 2) + "\n");
159
+ log(" " + setPath);
130
160
  } else {
131
161
  step("Registering the glm server in VS Code (workspace .vscode/mcp.json)");
132
162
  log(" " + mergeMcp(join(WORKSPACE, ".vscode", "mcp.json")));
133
163
 
164
+ step("Installing the GLM custom agent (subagent) -> .github/agents/");
165
+ log(" " + copyInto(join(WORKSPACE, ".github", "agents"), "glm.agent.md"));
166
+
134
167
  step("Adding delegation policy (workspace .github/copilot-instructions.md)");
135
- const ghDir = join(WORKSPACE, ".github");
136
- mkdirSync(ghDir, { recursive: true });
137
- const ciPath = join(ghDir, "copilot-instructions.md");
168
+ const ciPath = join(WORKSPACE, ".github", "copilot-instructions.md");
169
+ mkdirSync(dirname(ciPath), { recursive: true });
138
170
  const policy = readFileSync(join(SELF, "copilot-instructions.md"), "utf8");
139
171
  const existing = existsSync(ciPath) ? readFileSync(ciPath, "utf8") : "";
140
172
  if (!existing.includes("glm_agent")) {
141
173
  writeFileSync(ciPath, existing + (existing ? "\n\n" : "") + policy);
142
174
  log(" " + ciPath);
143
175
  } else {
144
- log(" policy already present (left as-is)");
176
+ log(" copilot-instructions.md already has the policy");
145
177
  }
178
+
179
+ step("Installing the GLM PreToolUse hook (auto-routing) -> .github/hooks/");
180
+ log(" " + writeHookFile(join(WORKSPACE, ".github", "hooks")));
146
181
  }
147
182
 
148
183
  log("\n✅ Done. Next steps:");
149
184
  log(" 1. Ensure GLM_API_KEY is set in " + envPath);
150
185
  log(" 2. In VS Code: Reload Window, open Copilot Chat, switch to Agent mode.");
151
186
  log(" 3. Start the 'glm' server: run 'MCP: List Servers' (or VS Code will offer to start it).");
152
- log(" 4. Ask Copilot to do a coding task it will call glm_agent. Run glm_status for the GLM usage ledger.");
187
+ log(" 4. Use it: pick the 'GLM' agent in the chat mode dropdown, or ask the main agent to");
188
+ log(" 'use glm_agent to …'. Run glm_status for the GLM usage ledger.");
153
189
  log(
154
190
  GLOBAL
155
- ? "\nGLOBAL mode: the glm server + 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."
156
192
  : "\nWorkspace mode: current project only. Re-run with --global to apply to every project."
157
193
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "glm-mcp-copilot",
3
- "version": "1.1.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) wired into VS Code agent mode.",
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,9 @@
9
9
  "files": [
10
10
  "glm-mcp/",
11
11
  "install-copilot.mjs",
12
+ "glm_router_hook.mjs",
13
+ "glm.agent.md",
14
+ "glm.instructions.md",
12
15
  "copilot-instructions.md",
13
16
  "mcp.json.example",
14
17
  "README.md",