code-ai-installer 4.3.0 โ†’ 4.3.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/README.md CHANGED
@@ -157,8 +157,9 @@ Depending on `--target`, `code-ai` restructures your project:
157
157
 
158
158
  ## ๐Ÿงฌ Versions & migration
159
159
 
160
- `code-ai-installer` is on **v4.3.0**.
160
+ `code-ai-installer` is on **v4.3.1**.
161
161
 
162
+ - **v4.3.1** โ€” the `code-ai-mcp` registration is now **pinned** to the installed version (`npx -p code-ai-installer@<version>`) and re-pinned on every reinstall, so an updated server actually takes effect instead of an unpinned `npx` silently reusing a stale global/cache copy. The server also logs `code-ai-mcp v<version> ยท domain=<domain>` to stderr at startup, so the live build is visible in Claude's MCP logs.
162
163
  - **v4.3.0** โ€” `render_diff` MCP tool (unified diff โ†’ a standalone HTML review page); MCP gate-flow + stop-at-user-gate sections added to the content / analytics / product conductors; Auditor trigger โ€” a `/audit` command plus a Release-Gate nudge that surfaces after every 3rd completed run (development pilot).
163
164
  - **v4.1.0** โ€” MCP servers now register in your **global (user-scope)** config via a direct, idempotent `~/.claude.json` merge (no dependency on the `claude` CLI); the conductor halts at each user gate โ€” one at a time, no batching, no auto-pass on green.
164
165
  - **v4.0.0** โ€” consolidated the previously separate `code-ai-mcp` and types packages into this single package with **two bins** (`code-ai` + `code-ai-mcp`). Installing the CLI now also delivers the MCP server; for Claude it is auto-registered in your global (user-scope) MCP config. Existing 3.x CLI behavior is unchanged.
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
2
  import path from "node:path";
4
3
  import { fileURLToPath } from "node:url";
5
4
  import prompts from "prompts";
@@ -13,10 +12,9 @@ import { setupMcp, teardownMcp } from "./mcp_setup.js";
13
12
  import { getPlatformAdapters } from "./platforms/adapters.js";
14
13
  import { resolveSourceRoot } from "./sourceResolver.js";
15
14
  import { printBanner } from "./banner.js";
15
+ import { readInstallerVersion } from "./version.js";
16
16
  const program = new Command();
17
17
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
18
- const requireJson = createRequire(import.meta.url);
19
- const pkg = requireJson("../package.json");
20
18
  const WIZARD_TEXT = {
21
19
  en: {
22
20
  cancelled: "Installation cancelled.",
@@ -76,7 +74,7 @@ const WIZARD_TEXT = {
76
74
  program
77
75
  .name("code-ai")
78
76
  .description("Install code-ai agents and skills for AI coding assistants")
79
- .version(pkg.version);
77
+ .version(readInstallerVersion());
80
78
  program
81
79
  .command("targets")
82
80
  .description("List supported AI targets")
@@ -228,7 +226,7 @@ program
228
226
  });
229
227
  for (const notice of report.notices)
230
228
  info(` ${notice}`);
231
- info(` MCP (${report.registration}): registered [${report.serversRegistered.join(", ") || "โ€”"}], already present [${report.serversAlreadyPresent.join(", ") || "โ€”"}]`);
229
+ info(` MCP (${report.registration}): registered [${report.serversRegistered.join(", ") || "โ€”"}], re-pinned [${report.serversUpdated.join(", ") || "โ€”"}], already present [${report.serversAlreadyPresent.join(", ") || "โ€”"}]`);
232
230
  info(` .code-ai/config.json: written at ${report.configPath} (decision_store=${report.mempalaceUsed ? "mempalace" : "jsonl"})`);
233
231
  }
234
232
  if (dryRun) {
@@ -494,7 +492,7 @@ async function runInteractiveWizard() {
494
492
  });
495
493
  for (const notice of report.notices)
496
494
  info(` ${notice}`);
497
- info(` MCP (${report.registration}): registered [${report.serversRegistered.join(", ") || "โ€”"}], already present [${report.serversAlreadyPresent.join(", ") || "โ€”"}]`);
495
+ info(` MCP (${report.registration}): registered [${report.serversRegistered.join(", ") || "โ€”"}], re-pinned [${report.serversUpdated.join(", ") || "โ€”"}], already present [${report.serversAlreadyPresent.join(", ") || "โ€”"}]`);
498
496
  info(` .code-ai/config.json: written at ${report.configPath} (decision_store=${report.mempalaceUsed ? "mempalace" : "jsonl"})`);
499
497
  }
500
498
  success("Install completed.");
package/dist/mcp/cli.d.ts CHANGED
@@ -11,6 +11,10 @@
11
11
  */
12
12
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
13
  import { type ToolName } from "../shared/index.js";
14
+ declare const SERVER_INFO: {
15
+ name: string;
16
+ version: string;
17
+ };
14
18
  /**
15
19
  * One-line description per tool. Not stored in the shared types module (src/shared) because the
16
20
  * registry there is pure I/O contracts; descriptions live with the transport
@@ -18,4 +22,4 @@ import { type ToolName } from "../shared/index.js";
18
22
  */
19
23
  declare const TOOL_DESCRIPTIONS: Record<ToolName, string>;
20
24
  declare function buildServer(): McpServer;
21
- export { buildServer, TOOL_DESCRIPTIONS };
25
+ export { buildServer, TOOL_DESCRIPTIONS, SERVER_INFO };
package/dist/mcp/cli.js CHANGED
@@ -14,9 +14,11 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
14
14
  import { TOOL_REGISTRY } from "../shared/index.js";
15
15
  import { CodeAiMcpServer } from "./server.js";
16
16
  import { NotImplementedError } from "./tools/stubs.js";
17
+ import { readInstallerVersion } from "../version.js";
18
+ import { resolveActiveDomain } from "./config.js";
17
19
  const SERVER_INFO = {
18
20
  name: "code-ai-mcp",
19
- version: "0.0.0",
21
+ version: readInstallerVersion(),
20
22
  };
21
23
  /**
22
24
  * One-line description per tool. Not stored in the shared types module (src/shared) because the
@@ -108,6 +110,11 @@ async function main() {
108
110
  const mcp = buildServer();
109
111
  const transport = new StdioServerTransport();
110
112
  await mcp.connect(transport);
113
+ // Startup banner to STDERR โ€” never stdout, which carries the JSON-RPC stream.
114
+ // Makes the live build + active domain visible in Claude's MCP logs, so a
115
+ // stale-server mismatch is obvious instead of silent.
116
+ const domain = await resolveActiveDomain();
117
+ console.error(`code-ai-mcp v${SERVER_INFO.version} ยท domain=${domain}`);
111
118
  }
112
119
  // Allow importing buildServer in tests without starting the stdio loop.
113
120
  const isDirectRun = (() => {
@@ -125,4 +132,4 @@ if (isDirectRun) {
125
132
  process.exit(1);
126
133
  });
127
134
  }
128
- export { buildServer, TOOL_DESCRIPTIONS };
135
+ export { buildServer, TOOL_DESCRIPTIONS, SERVER_INFO };
@@ -69,6 +69,8 @@ export interface McpSetupReport {
69
69
  registration: "user-scope" | "manual-fallback";
70
70
  /** Server names freshly added to the user config this run. */
71
71
  serversRegistered: string[];
72
+ /** Installer-owned server names whose entry was refreshed (e.g. re-pinned to a new version). */
73
+ serversUpdated: string[];
72
74
  /** Server names already present in the user config (left untouched). */
73
75
  serversAlreadyPresent: string[];
74
76
  /** Server names whose registration failed (config unwritable). */
@@ -128,13 +130,16 @@ export declare function userConfigPath(): string;
128
130
  export declare function createUserConfigIO(configPath: string): UserConfigIO;
129
131
  /**
130
132
  * Idempotently merge `servers` into the config's top-level `mcpServers`. Pure โ€”
131
- * returns a new config object plus which names were freshly added vs already
132
- * present. An existing key is NEVER overwritten (so a pre-existing global
133
- * `mempalace` is preserved untouched). Exported for unit testing.
133
+ * returns a new config object plus which names were added / updated / already
134
+ * present. A FOREIGN existing key is never overwritten (so a pre-existing global
135
+ * `mempalace` is preserved). An OWNED server (named in `ownedServers`) whose
136
+ * entry differs is refreshed in place โ€” that is how a reinstall re-pins
137
+ * `code-ai-mcp` to the new version. Exported for unit testing.
134
138
  */
135
- export declare function mergeUserScopeServers(config: Record<string, unknown>, servers: Record<string, McpServerEntry>): {
139
+ export declare function mergeUserScopeServers(config: Record<string, unknown>, servers: Record<string, McpServerEntry>, ownedServers?: readonly string[]): {
136
140
  config: Record<string, unknown>;
137
141
  registered: string[];
142
+ updated: string[];
138
143
  alreadyPresent: string[];
139
144
  };
140
145
  /**
package/dist/mcp_setup.js CHANGED
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { copyFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { readInstallerVersion } from "./version.js";
5
6
  /**
6
7
  * Try `mempalace-mcp --help` โ€” the dedicated MCP-server bin, which is exactly
7
8
  * what we register. Resolves true on exit code 0, false otherwise (including
@@ -120,25 +121,38 @@ function readMcpServers(config) {
120
121
  }
121
122
  /**
122
123
  * Idempotently merge `servers` into the config's top-level `mcpServers`. Pure โ€”
123
- * returns a new config object plus which names were freshly added vs already
124
- * present. An existing key is NEVER overwritten (so a pre-existing global
125
- * `mempalace` is preserved untouched). Exported for unit testing.
124
+ * returns a new config object plus which names were added / updated / already
125
+ * present. A FOREIGN existing key is never overwritten (so a pre-existing global
126
+ * `mempalace` is preserved). An OWNED server (named in `ownedServers`) whose
127
+ * entry differs is refreshed in place โ€” that is how a reinstall re-pins
128
+ * `code-ai-mcp` to the new version. Exported for unit testing.
126
129
  */
127
- export function mergeUserScopeServers(config, servers) {
130
+ export function mergeUserScopeServers(config, servers, ownedServers = []) {
128
131
  const next = { ...config };
129
132
  const mcpServers = readMcpServers(config);
133
+ const owned = new Set(ownedServers);
130
134
  const registered = [];
135
+ const updated = [];
131
136
  const alreadyPresent = [];
132
137
  for (const [name, entry] of Object.entries(servers)) {
138
+ const desired = toUserScopeEntry(entry);
133
139
  if (Object.prototype.hasOwnProperty.call(mcpServers, name)) {
134
- alreadyPresent.push(name);
140
+ // Refresh ONLY our own servers (e.g. re-pin to a new version). A foreign
141
+ // server we also register (mempalace) is never overwritten.
142
+ if (owned.has(name) && JSON.stringify(mcpServers[name]) !== JSON.stringify(desired)) {
143
+ mcpServers[name] = desired;
144
+ updated.push(name);
145
+ }
146
+ else {
147
+ alreadyPresent.push(name);
148
+ }
135
149
  continue;
136
150
  }
137
- mcpServers[name] = toUserScopeEntry(entry);
151
+ mcpServers[name] = desired;
138
152
  registered.push(name);
139
153
  }
140
154
  next.mcpServers = mcpServers;
141
- return { config: next, registered, alreadyPresent };
155
+ return { config: next, registered, updated, alreadyPresent };
142
156
  }
143
157
  /**
144
158
  * Idempotently remove `names` from the config's `mcpServers`. Pure โ€” returns a
@@ -184,10 +198,14 @@ function buildServerEntries(mempalaceUsed) {
184
198
  // DEV-100 consolidation: the code-ai-mcp bin lives inside the
185
199
  // `code-ai-installer` npm package. `npx -p <package> <bin>` tells npm to
186
200
  // install that package and run the named bin from it.
201
+ // Pin to THIS installer's version so the spawned server is explicit and
202
+ // reproducible. An unpinned `npx` silently runs whatever global/cache copy
203
+ // exists โ€” that is how a stale, pre-fix server kept running after a reinstall.
204
+ // A reinstall re-pins this (mergeUserScopeServers refreshes owned servers).
187
205
  const servers = {
188
206
  "code-ai-mcp": {
189
207
  command: "npx",
190
- args: ["-y", "-p", "code-ai-installer", "code-ai-mcp"],
208
+ args: ["-y", "-p", `code-ai-installer@${readInstallerVersion()}`, "code-ai-mcp"],
191
209
  },
192
210
  };
193
211
  if (mempalaceUsed) {
@@ -249,6 +267,7 @@ export async function setupMcp(opts, io = defaultUserConfigIO) {
249
267
  }
250
268
  const servers = buildServerEntries(mempalaceUsed);
251
269
  const serversRegistered = [];
270
+ const serversUpdated = [];
252
271
  const serversAlreadyPresent = [];
253
272
  const serversFailed = [];
254
273
  let registration;
@@ -268,12 +287,13 @@ export async function setupMcp(opts, io = defaultUserConfigIO) {
268
287
  notices.push(manualEntryLine(name, entry));
269
288
  }
270
289
  else {
271
- const merged = mergeUserScopeServers(read.data, servers);
290
+ const merged = mergeUserScopeServers(read.data, servers, INSTALLER_OWNED_SERVERS);
272
291
  serversAlreadyPresent.push(...merged.alreadyPresent);
273
292
  for (const name of merged.alreadyPresent) {
274
293
  notices.push(`MCP server '${name}' already in user config โ€” left untouched.`);
275
294
  }
276
- if (merged.registered.length === 0) {
295
+ const changed = [...merged.registered, ...merged.updated];
296
+ if (changed.length === 0) {
277
297
  registration = "user-scope";
278
298
  }
279
299
  else {
@@ -281,16 +301,22 @@ export async function setupMcp(opts, io = defaultUserConfigIO) {
281
301
  if (written.ok) {
282
302
  registration = "user-scope";
283
303
  serversRegistered.push(...merged.registered);
284
- notices.push(`Registered MCP server(s) [${merged.registered.join(", ")}] in Claude user config ${io.configPath}` +
304
+ serversUpdated.push(...merged.updated);
305
+ const parts = [];
306
+ if (merged.registered.length)
307
+ parts.push(`registered [${merged.registered.join(", ")}]`);
308
+ if (merged.updated.length)
309
+ parts.push(`re-pinned [${merged.updated.join(", ")}]`);
310
+ notices.push(`MCP server(s) ${parts.join(", ")} in Claude user config ${io.configPath}` +
285
311
  (written.backupPath ? ` (backup: ${written.backupPath})` : "") +
286
312
  ". Restart your Claude Code session to load them.");
287
313
  }
288
314
  else {
289
315
  registration = "manual-fallback";
290
- serversFailed.push(...merged.registered);
316
+ serversFailed.push(...changed);
291
317
  notices.push(`Failed to write Claude user config at ${io.configPath} (${written.error ?? "unknown error"}). ` +
292
318
  `No changes made โ€” add these entries to its "mcpServers" object by hand:`);
293
- for (const name of merged.registered)
319
+ for (const name of changed)
294
320
  notices.push(manualEntryLine(name, servers[name]));
295
321
  }
296
322
  }
@@ -308,6 +334,7 @@ export async function setupMcp(opts, io = defaultUserConfigIO) {
308
334
  userConfigPath: io.configPath,
309
335
  registration,
310
336
  serversRegistered,
337
+ serversUpdated,
311
338
  serversAlreadyPresent,
312
339
  serversFailed,
313
340
  configPath: cfg.path,
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The installed `code-ai-installer` package version โ€” the single source for the
3
+ * CLI `--version`, the MCP server registration pin, and the server startup
4
+ * banner. Resolves `package.json` relative to THIS module (dist/version.js โ†’
5
+ * ../package.json = package root), so every caller gets the right path
6
+ * regardless of its own depth in the tree.
7
+ */
8
+ export declare function readInstallerVersion(): string;
@@ -0,0 +1,13 @@
1
+ import { createRequire } from "node:module";
2
+ /**
3
+ * The installed `code-ai-installer` package version โ€” the single source for the
4
+ * CLI `--version`, the MCP server registration pin, and the server startup
5
+ * banner. Resolves `package.json` relative to THIS module (dist/version.js โ†’
6
+ * ../package.json = package root), so every caller gets the right path
7
+ * regardless of its own depth in the tree.
8
+ */
9
+ export function readInstallerVersion() {
10
+ const requireJson = createRequire(import.meta.url);
11
+ const pkg = requireJson("../package.json");
12
+ return pkg.version;
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ai-installer",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
4
4
  "description": "Production-ready CLI to install code-ai agents and skills for multiple AI coding assistants. Bundles the code-ai-mcp MCP server for Claude Code.",
5
5
  "license": "MIT",
6
6
  "author": "Denish1209",