@maz-ui/node 5.1.0-beta.1 → 5.2.0-beta.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
@@ -18,7 +18,7 @@ npm install @maz-ui/node
18
18
  - 🔧 **Highly configurable** - custom loggers with different levels and reporters
19
19
  - 🛡️ **Error handling** - comprehensive error management
20
20
  - 🎯 **Multiple log types** - info, debug, trace, start, ready, fail, box
21
- - 🤖 **AI agents setup** - `setupAgents` configures the AI coding agents of a project for maz-ui (rules and MCP server), as `npx @maz-ui/mcp init` does: [documentation](https://maz-ui.com/ecosystem/node/setup-agents)
21
+ - 🤖 **AI agents setup** - `setupAgents` configures the AI coding agents of a project for maz-ui (rules and MCP server), as `npx @maz-ui/mcp init` does, or for another tool that ships its own MCP server and rules (`tool` option): [documentation](https://maz-ui.com/ecosystem/node/setup-agents)
22
22
 
23
23
  ## Usage
24
24
 
package/dist/index.d.mts CHANGED
@@ -171,7 +171,7 @@ declare function redactError<T>(error: T): T;
171
171
  /**
172
172
  * AI coding agents supported by `setupAgents`.
173
173
  *
174
- * - `claude`: Claude Code rule (`.claude/rules/maz-ui.md`) and MCP server (`.mcp.json`)
174
+ * - `claude`: Claude Code rule (`.claude/rules/<tool>.md`) and MCP server (`.mcp.json`)
175
175
  * - `agents-md`: section in `AGENTS.md` (Codex, Cursor, Copilot, Windsurf, Zed, Gemini...)
176
176
  * - `cursor`: MCP server in `.cursor/mcp.json` (rules come from `AGENTS.md`)
177
177
  * - `vscode`: MCP server in `.vscode/mcp.json` for GitHub Copilot (rules come from `AGENTS.md`)
@@ -183,9 +183,9 @@ interface SetupAgentsOptions {
183
183
  rootDir: string;
184
184
  /** Agents to configure. Default: the agents detected in the project */
185
185
  targets?: AgentTarget[];
186
- /** Write the maz-ui rules (Claude Code rule, `AGENTS.md` section). Default `true` */
186
+ /** Write the rules of the tool (Claude Code rule, `AGENTS.md` section). Default `true` */
187
187
  rules?: boolean;
188
- /** Register the maz-ui MCP server. Default `true` */
188
+ /** Register the MCP server of the tool. Default `true` */
189
189
  mcp?: boolean;
190
190
  /** Compute the changes without writing anything */
191
191
  dryRun?: boolean;
@@ -193,11 +193,18 @@ interface SetupAgentsOptions {
193
193
  remove?: boolean;
194
194
  /** Folder of the maz-ui agent docs. Default: `docs/` of the maz-ui package installed in `rootDir` */
195
195
  docsDir?: string;
196
- /** Command used to start the MCP server */
197
- mcpServer?: {
198
- command: string;
199
- args: string[];
200
- };
196
+ /** Command used to start the MCP server. Default: `mcpServerCommand(rootDir, mcpVersion, tool)` */
197
+ mcpServer?: McpServerCommand;
198
+ /** Version of the MCP package pinned in the `npx -y` command, when the project does not install it. Default `latest` */
199
+ mcpVersion?: string;
200
+ /** Tool whose agents are configured. Default: maz-ui (`MAZ_UI_AGENTS_TOOL`) */
201
+ tool?: AgentsTool;
202
+ /** Rules of the tool, required with another `tool` than maz-ui. Default: the maz-ui agent docs of `docsDir` */
203
+ agentDocs?: AgentDocs;
204
+ }
205
+ interface McpServerCommand {
206
+ command: string;
207
+ args: string[];
201
208
  }
202
209
  type SetupAgentsAction = 'create' | 'update' | 'remove' | 'unchanged' | 'skip';
203
210
  interface SetupAgentsChange {
@@ -210,15 +217,54 @@ interface SetupAgentsResult {
210
217
  targets: AgentTarget[];
211
218
  version: string;
212
219
  changes: SetupAgentsChange[];
220
+ /** Command registered in the MCP config files */
221
+ mcpServer: McpServerCommand;
222
+ /** `false` when the server runs through `npx -y` because the project does not install the MCP package */
223
+ localMcpServer: boolean;
213
224
  }
214
225
  declare const MANAGED_MARKER = "Managed by maz-ui";
215
226
  declare const AGENTS_MD_BEGIN = "<!-- BEGIN:maz-ui -->";
216
227
  declare const AGENTS_MD_END = "<!-- END:maz-ui -->";
217
228
  declare const MCP_SERVER_NAME = "maz-ui";
218
- declare const DEFAULT_MCP_SERVER: {
219
- command: string;
220
- args: string[];
221
- };
229
+ declare const DEFAULT_MCP_SERVER: McpServerCommand;
230
+ /**
231
+ * A tool whose AI agents are configured by `setupAgents`: maz-ui by default,
232
+ * or another package that ships its own MCP server and rules.
233
+ */
234
+ interface AgentsTool {
235
+ /** Name of the tool: key of its MCP server, `.claude/rules/<name>.md` and markers of the generated content */
236
+ name: string;
237
+ /** Package that ships the MCP server */
238
+ mcpPackage: string;
239
+ /** Bin of `mcpPackage` that starts the MCP server */
240
+ mcpBin: string;
241
+ /** Arguments of `mcpBin` that start the MCP server (e.g. `['mcp']`). Default `[]` */
242
+ mcpArgs?: string[];
243
+ /** What generates the rules, written in their header (markdown, e.g. `` `npx relizy mcp init` ``) */
244
+ generatedBy: string;
245
+ /** Line written under the title of the rules. Default: `> <name> <version> is installed: these rules match this version.` */
246
+ versionNote?: (version: string) => string;
247
+ }
248
+ declare const MAZ_UI_AGENTS_TOOL: AgentsTool;
249
+ type PackageManager = 'pnpm' | 'yarn' | 'bun' | 'npm';
250
+ /** `true` when the `package.json` of the project declares the MCP package of the tool (`@maz-ui/mcp` by default) */
251
+ declare function installsMcpPackage(rootDir: string, tool?: AgentsTool): boolean;
252
+ /** Package manager of a project: the `packageManager` field, else its lockfile */
253
+ declare function detectPackageManager(rootDir: string): PackageManager;
254
+ /**
255
+ * Command of the MCP server of a tool (maz-ui by default) for a project. When
256
+ * the project installs its package, the package manager starts the installed
257
+ * version in about a second, at the version of the lockfile. Otherwise
258
+ * `npx -y` downloads it, pinned to `version`: a dist-tag (`latest`) makes npx
259
+ * query the registry at every start, which can exceed the start timeout of the
260
+ * agents behind a slow registry.
261
+ */
262
+ declare function mcpServerCommand(rootDir: string, version?: string, tool?: AgentsTool): McpServerCommand;
263
+ /**
264
+ * `true` when generated rules were edited by hand since they were written:
265
+ * they are kept, a newer version of the rules would overwrite the edits.
266
+ */
267
+ declare function isEditedManagedContent(content: string | undefined, tool?: AgentsTool): boolean;
222
268
  /** Detects the AI agents configured in a project */
223
269
  declare function detectAgentTargets(rootDir: string): AgentTarget[];
224
270
  interface AgentDocs {
@@ -233,23 +279,41 @@ interface AgentDocs {
233
279
  * package installed for the project is found.
234
280
  */
235
281
  declare function findInstalledPackage(fromDir: string, name: string): string | undefined;
236
- /** Reads the agent files shipped in `maz-ui/docs/agents` */
237
282
  /** Version of an installed package, read from its `package.json` */
238
283
  declare function readPackageVersion(packageDir: string): string | undefined;
284
+ /**
285
+ * Workspace patterns of a monorepo: the `packages` of `pnpm-workspace.yaml`,
286
+ * else the `workspaces` of `package.json` (npm, yarn, bun), else `apps/*` and
287
+ * `packages/*`. Negated patterns are ignored.
288
+ */
289
+ declare function readWorkspacePatterns(rootDir: string): string[];
290
+ /** Folders of the workspaces of a monorepo, in the order of the patterns. Only the fixed part of each pattern is read (`apps/*` reads `apps`) */
291
+ declare function findWorkspaceDirs(rootDir: string): string[];
239
292
  /**
240
293
  * Finds a package installed in a project: in the `node_modules` of the
241
- * project root and of its workspace folders (`apps/*`, `packages/*`, for
242
- * monorepos without hoisting), then in the parent folders.
294
+ * project root and of its workspaces (monorepos without hoisting), then in
295
+ * the parent folders.
243
296
  */
244
297
  declare function findProjectPackage(rootDir: string, name: string): string | undefined;
298
+ /** Reads the agent files shipped in `maz-ui/docs/agents` */
245
299
  declare function readAgentDocs(rootDir: string, docsDir?: string): AgentDocs;
246
300
  /**
247
- * Configures the AI coding agents of a project to use maz-ui: rules (version
248
- * matched with the installed maz-ui) and the maz-ui MCP server. Idempotent and
249
- * conservative: it only writes files it owns, a delimited section of
250
- * `AGENTS.md` and its own entry of the MCP config files.
301
+ * Writes a temporary file then renames it: a concurrent reader (several Nuxt
302
+ * apps of a monorepo starting together) sees the old or the new file, never a
303
+ * truncated one.
304
+ */
305
+ declare function writeFileAtomic(path: string, content: string): void;
306
+ /** Compares two semver versions: negative when `a < b`, positive when `a > b`, `0` when equal */
307
+ declare function compareVersions(a: string, b: string): number;
308
+ /** Version of the tool (maz-ui by default) that generated a managed content, read from its header */
309
+ declare function readManagedVersion(content: string | undefined, tool?: AgentsTool): string | undefined;
310
+ /**
311
+ * Configures the AI coding agents of a project to use a tool, maz-ui by
312
+ * default: rules (version matched with the installed tool) and its MCP server.
313
+ * Idempotent and conservative: it only writes files it owns, a delimited
314
+ * section of `AGENTS.md` and its own entry of the MCP config files.
251
315
  */
252
316
  declare function setupAgents(options: SetupAgentsOptions): SetupAgentsResult;
253
317
 
254
- export { AGENTS_MD_BEGIN, AGENTS_MD_END, AGENT_TARGETS, DEFAULT_MCP_SERVER, MANAGED_MARKER, MCP_SERVER_NAME, createLogger, detectAgentTargets, execPromise, findInstalledPackage, findProjectPackage, logger, printBanner, readAgentDocs, readPackageVersion, redactError, redactSecrets, setupAgents };
255
- export type { AgentDocs, AgentTarget, ExecPromiseOptions, LogLevel, Logger, LoggerOptions, SetupAgentsAction, SetupAgentsChange, SetupAgentsOptions, SetupAgentsResult };
318
+ export { AGENTS_MD_BEGIN, AGENTS_MD_END, AGENT_TARGETS, DEFAULT_MCP_SERVER, MANAGED_MARKER, MAZ_UI_AGENTS_TOOL, MCP_SERVER_NAME, compareVersions, createLogger, detectAgentTargets, detectPackageManager, execPromise, findInstalledPackage, findProjectPackage, findWorkspaceDirs, installsMcpPackage, isEditedManagedContent, logger, mcpServerCommand, printBanner, readAgentDocs, readManagedVersion, readPackageVersion, readWorkspacePatterns, redactError, redactSecrets, setupAgents, writeFileAtomic };
319
+ export type { AgentDocs, AgentTarget, AgentsTool, ExecPromiseOptions, LogLevel, Logger, LoggerOptions, McpServerCommand, PackageManager, SetupAgentsAction, SetupAgentsChange, SetupAgentsOptions, SetupAgentsResult };
package/dist/index.d.ts CHANGED
@@ -171,7 +171,7 @@ declare function redactError<T>(error: T): T;
171
171
  /**
172
172
  * AI coding agents supported by `setupAgents`.
173
173
  *
174
- * - `claude`: Claude Code rule (`.claude/rules/maz-ui.md`) and MCP server (`.mcp.json`)
174
+ * - `claude`: Claude Code rule (`.claude/rules/<tool>.md`) and MCP server (`.mcp.json`)
175
175
  * - `agents-md`: section in `AGENTS.md` (Codex, Cursor, Copilot, Windsurf, Zed, Gemini...)
176
176
  * - `cursor`: MCP server in `.cursor/mcp.json` (rules come from `AGENTS.md`)
177
177
  * - `vscode`: MCP server in `.vscode/mcp.json` for GitHub Copilot (rules come from `AGENTS.md`)
@@ -183,9 +183,9 @@ interface SetupAgentsOptions {
183
183
  rootDir: string;
184
184
  /** Agents to configure. Default: the agents detected in the project */
185
185
  targets?: AgentTarget[];
186
- /** Write the maz-ui rules (Claude Code rule, `AGENTS.md` section). Default `true` */
186
+ /** Write the rules of the tool (Claude Code rule, `AGENTS.md` section). Default `true` */
187
187
  rules?: boolean;
188
- /** Register the maz-ui MCP server. Default `true` */
188
+ /** Register the MCP server of the tool. Default `true` */
189
189
  mcp?: boolean;
190
190
  /** Compute the changes without writing anything */
191
191
  dryRun?: boolean;
@@ -193,11 +193,18 @@ interface SetupAgentsOptions {
193
193
  remove?: boolean;
194
194
  /** Folder of the maz-ui agent docs. Default: `docs/` of the maz-ui package installed in `rootDir` */
195
195
  docsDir?: string;
196
- /** Command used to start the MCP server */
197
- mcpServer?: {
198
- command: string;
199
- args: string[];
200
- };
196
+ /** Command used to start the MCP server. Default: `mcpServerCommand(rootDir, mcpVersion, tool)` */
197
+ mcpServer?: McpServerCommand;
198
+ /** Version of the MCP package pinned in the `npx -y` command, when the project does not install it. Default `latest` */
199
+ mcpVersion?: string;
200
+ /** Tool whose agents are configured. Default: maz-ui (`MAZ_UI_AGENTS_TOOL`) */
201
+ tool?: AgentsTool;
202
+ /** Rules of the tool, required with another `tool` than maz-ui. Default: the maz-ui agent docs of `docsDir` */
203
+ agentDocs?: AgentDocs;
204
+ }
205
+ interface McpServerCommand {
206
+ command: string;
207
+ args: string[];
201
208
  }
202
209
  type SetupAgentsAction = 'create' | 'update' | 'remove' | 'unchanged' | 'skip';
203
210
  interface SetupAgentsChange {
@@ -210,15 +217,54 @@ interface SetupAgentsResult {
210
217
  targets: AgentTarget[];
211
218
  version: string;
212
219
  changes: SetupAgentsChange[];
220
+ /** Command registered in the MCP config files */
221
+ mcpServer: McpServerCommand;
222
+ /** `false` when the server runs through `npx -y` because the project does not install the MCP package */
223
+ localMcpServer: boolean;
213
224
  }
214
225
  declare const MANAGED_MARKER = "Managed by maz-ui";
215
226
  declare const AGENTS_MD_BEGIN = "<!-- BEGIN:maz-ui -->";
216
227
  declare const AGENTS_MD_END = "<!-- END:maz-ui -->";
217
228
  declare const MCP_SERVER_NAME = "maz-ui";
218
- declare const DEFAULT_MCP_SERVER: {
219
- command: string;
220
- args: string[];
221
- };
229
+ declare const DEFAULT_MCP_SERVER: McpServerCommand;
230
+ /**
231
+ * A tool whose AI agents are configured by `setupAgents`: maz-ui by default,
232
+ * or another package that ships its own MCP server and rules.
233
+ */
234
+ interface AgentsTool {
235
+ /** Name of the tool: key of its MCP server, `.claude/rules/<name>.md` and markers of the generated content */
236
+ name: string;
237
+ /** Package that ships the MCP server */
238
+ mcpPackage: string;
239
+ /** Bin of `mcpPackage` that starts the MCP server */
240
+ mcpBin: string;
241
+ /** Arguments of `mcpBin` that start the MCP server (e.g. `['mcp']`). Default `[]` */
242
+ mcpArgs?: string[];
243
+ /** What generates the rules, written in their header (markdown, e.g. `` `npx relizy mcp init` ``) */
244
+ generatedBy: string;
245
+ /** Line written under the title of the rules. Default: `> <name> <version> is installed: these rules match this version.` */
246
+ versionNote?: (version: string) => string;
247
+ }
248
+ declare const MAZ_UI_AGENTS_TOOL: AgentsTool;
249
+ type PackageManager = 'pnpm' | 'yarn' | 'bun' | 'npm';
250
+ /** `true` when the `package.json` of the project declares the MCP package of the tool (`@maz-ui/mcp` by default) */
251
+ declare function installsMcpPackage(rootDir: string, tool?: AgentsTool): boolean;
252
+ /** Package manager of a project: the `packageManager` field, else its lockfile */
253
+ declare function detectPackageManager(rootDir: string): PackageManager;
254
+ /**
255
+ * Command of the MCP server of a tool (maz-ui by default) for a project. When
256
+ * the project installs its package, the package manager starts the installed
257
+ * version in about a second, at the version of the lockfile. Otherwise
258
+ * `npx -y` downloads it, pinned to `version`: a dist-tag (`latest`) makes npx
259
+ * query the registry at every start, which can exceed the start timeout of the
260
+ * agents behind a slow registry.
261
+ */
262
+ declare function mcpServerCommand(rootDir: string, version?: string, tool?: AgentsTool): McpServerCommand;
263
+ /**
264
+ * `true` when generated rules were edited by hand since they were written:
265
+ * they are kept, a newer version of the rules would overwrite the edits.
266
+ */
267
+ declare function isEditedManagedContent(content: string | undefined, tool?: AgentsTool): boolean;
222
268
  /** Detects the AI agents configured in a project */
223
269
  declare function detectAgentTargets(rootDir: string): AgentTarget[];
224
270
  interface AgentDocs {
@@ -233,23 +279,41 @@ interface AgentDocs {
233
279
  * package installed for the project is found.
234
280
  */
235
281
  declare function findInstalledPackage(fromDir: string, name: string): string | undefined;
236
- /** Reads the agent files shipped in `maz-ui/docs/agents` */
237
282
  /** Version of an installed package, read from its `package.json` */
238
283
  declare function readPackageVersion(packageDir: string): string | undefined;
284
+ /**
285
+ * Workspace patterns of a monorepo: the `packages` of `pnpm-workspace.yaml`,
286
+ * else the `workspaces` of `package.json` (npm, yarn, bun), else `apps/*` and
287
+ * `packages/*`. Negated patterns are ignored.
288
+ */
289
+ declare function readWorkspacePatterns(rootDir: string): string[];
290
+ /** Folders of the workspaces of a monorepo, in the order of the patterns. Only the fixed part of each pattern is read (`apps/*` reads `apps`) */
291
+ declare function findWorkspaceDirs(rootDir: string): string[];
239
292
  /**
240
293
  * Finds a package installed in a project: in the `node_modules` of the
241
- * project root and of its workspace folders (`apps/*`, `packages/*`, for
242
- * monorepos without hoisting), then in the parent folders.
294
+ * project root and of its workspaces (monorepos without hoisting), then in
295
+ * the parent folders.
243
296
  */
244
297
  declare function findProjectPackage(rootDir: string, name: string): string | undefined;
298
+ /** Reads the agent files shipped in `maz-ui/docs/agents` */
245
299
  declare function readAgentDocs(rootDir: string, docsDir?: string): AgentDocs;
246
300
  /**
247
- * Configures the AI coding agents of a project to use maz-ui: rules (version
248
- * matched with the installed maz-ui) and the maz-ui MCP server. Idempotent and
249
- * conservative: it only writes files it owns, a delimited section of
250
- * `AGENTS.md` and its own entry of the MCP config files.
301
+ * Writes a temporary file then renames it: a concurrent reader (several Nuxt
302
+ * apps of a monorepo starting together) sees the old or the new file, never a
303
+ * truncated one.
304
+ */
305
+ declare function writeFileAtomic(path: string, content: string): void;
306
+ /** Compares two semver versions: negative when `a < b`, positive when `a > b`, `0` when equal */
307
+ declare function compareVersions(a: string, b: string): number;
308
+ /** Version of the tool (maz-ui by default) that generated a managed content, read from its header */
309
+ declare function readManagedVersion(content: string | undefined, tool?: AgentsTool): string | undefined;
310
+ /**
311
+ * Configures the AI coding agents of a project to use a tool, maz-ui by
312
+ * default: rules (version matched with the installed tool) and its MCP server.
313
+ * Idempotent and conservative: it only writes files it owns, a delimited
314
+ * section of `AGENTS.md` and its own entry of the MCP config files.
251
315
  */
252
316
  declare function setupAgents(options: SetupAgentsOptions): SetupAgentsResult;
253
317
 
254
- export { AGENTS_MD_BEGIN, AGENTS_MD_END, AGENT_TARGETS, DEFAULT_MCP_SERVER, MANAGED_MARKER, MCP_SERVER_NAME, createLogger, detectAgentTargets, execPromise, findInstalledPackage, findProjectPackage, logger, printBanner, readAgentDocs, readPackageVersion, redactError, redactSecrets, setupAgents };
255
- export type { AgentDocs, AgentTarget, ExecPromiseOptions, LogLevel, Logger, LoggerOptions, SetupAgentsAction, SetupAgentsChange, SetupAgentsOptions, SetupAgentsResult };
318
+ export { AGENTS_MD_BEGIN, AGENTS_MD_END, AGENT_TARGETS, DEFAULT_MCP_SERVER, MANAGED_MARKER, MAZ_UI_AGENTS_TOOL, MCP_SERVER_NAME, compareVersions, createLogger, detectAgentTargets, detectPackageManager, execPromise, findInstalledPackage, findProjectPackage, findWorkspaceDirs, installsMcpPackage, isEditedManagedContent, logger, mcpServerCommand, printBanner, readAgentDocs, readManagedVersion, readPackageVersion, readWorkspacePatterns, redactError, redactSecrets, setupAgents, writeFileAtomic };
319
+ export type { AgentDocs, AgentTarget, AgentsTool, ExecPromiseOptions, LogLevel, Logger, LoggerOptions, McpServerCommand, PackageManager, SetupAgentsAction, SetupAgentsChange, SetupAgentsOptions, SetupAgentsResult };
package/dist/index.mjs CHANGED
@@ -3,8 +3,9 @@ import process$1 from 'node:process';
3
3
  import { blueBright } from 'colorette';
4
4
  import { createConsola } from 'consola';
5
5
  import figlet from 'figlet';
6
- import { existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
7
- import { join, dirname } from 'node:path';
6
+ import { createHash } from 'node:crypto';
7
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync, rmSync, readdirSync } from 'node:fs';
8
+ import { join, dirname, basename } from 'node:path';
8
9
  import { parse, applyEdits, modify } from 'jsonc-parser';
9
10
 
10
11
  const LogLevelMap = {
@@ -189,16 +190,93 @@ const AGENTS_MD_BEGIN = "<!-- BEGIN:maz-ui -->";
189
190
  const AGENTS_MD_END = "<!-- END:maz-ui -->";
190
191
  const MCP_SERVER_NAME = "maz-ui";
191
192
  const DEFAULT_MCP_SERVER = { command: "npx", args: ["-y", "@maz-ui/mcp@latest"] };
192
- const CLAUDE_RULE_FILE = ".claude/rules/maz-ui.md";
193
+ const MAZ_UI_AGENTS_TOOL = {
194
+ name: "maz-ui",
195
+ mcpPackage: "@maz-ui/mcp",
196
+ mcpBin: "maz-ui-mcp",
197
+ generatedBy: "`npx @maz-ui/mcp init` (or the `mazUi.agents` Nuxt option)",
198
+ versionNote: (version) => `> maz-ui ${version} is installed: these rules and the docs in \`node_modules/maz-ui/docs\` match this version.`
199
+ };
200
+ function escapeRegExp(value) {
201
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
202
+ }
203
+ const toolSetups = /* @__PURE__ */ new WeakMap();
204
+ function resolveTool(tool) {
205
+ let setup = toolSetups.get(tool);
206
+ if (!setup) {
207
+ const marker = `Managed by ${tool.name}`;
208
+ setup = {
209
+ tool,
210
+ marker,
211
+ agentsMdBegin: `<!-- BEGIN:${tool.name} -->`,
212
+ agentsMdEnd: `<!-- END:${tool.name} -->`,
213
+ ruleFile: `.claude/rules/${tool.name}.md`,
214
+ managedHeader: new RegExp(`<!-- ${escapeRegExp(marker)}[^\\n]*?-->`),
215
+ managedVersion: new RegExp(`${escapeRegExp(marker)} (\\d[^\\s:]*)`)
216
+ };
217
+ toolSetups.set(tool, setup);
218
+ }
219
+ return setup;
220
+ }
221
+ function readPackageJson(rootDir) {
222
+ try {
223
+ return JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"));
224
+ } catch {
225
+ return void 0;
226
+ }
227
+ }
228
+ function installsMcpPackage(rootDir, tool = MAZ_UI_AGENTS_TOOL) {
229
+ const pkg = readPackageJson(rootDir);
230
+ return ["dependencies", "devDependencies", "optionalDependencies"].some((field) => pkg?.[field]?.[tool.mcpPackage] !== void 0);
231
+ }
232
+ function detectPackageManager(rootDir) {
233
+ const declared = String(readPackageJson(rootDir)?.packageManager ?? "").split("@")[0];
234
+ if (["pnpm", "yarn", "bun", "npm"].includes(declared))
235
+ return declared;
236
+ const lockfiles = [["pnpm-lock.yaml", "pnpm"], ["yarn.lock", "yarn"], ["bun.lock", "bun"], ["bun.lockb", "bun"]];
237
+ return lockfiles.find(([file]) => existsSync(join(rootDir, file)))?.[1] ?? "npm";
238
+ }
239
+ function localMcpCommands(tool) {
240
+ const bin = [tool.mcpBin, ...tool.mcpArgs ?? []];
241
+ return {
242
+ pnpm: { command: "pnpm", args: ["exec", ...bin] },
243
+ yarn: { command: "yarn", args: ["run", ...bin] },
244
+ bun: { command: "bun", args: ["x", "--no-install", ...bin] },
245
+ npm: { command: "npx", args: ["--no-install", ...bin] }
246
+ };
247
+ }
248
+ function mcpServerCommand(rootDir, version = "latest", tool = MAZ_UI_AGENTS_TOOL) {
249
+ return installsMcpPackage(rootDir, tool) ? localMcpCommands(tool)[detectPackageManager(rootDir)] : { command: "npx", args: ["-y", `${tool.mcpPackage}@${version}`, ...tool.mcpArgs ?? []] };
250
+ }
251
+ function pinnedMcpVersion(server, tool) {
252
+ const pinned = new RegExp(`^${escapeRegExp(tool.mcpPackage)}@(\\d\\S*)$`);
253
+ return server.args.map((arg) => pinned.exec(arg)?.[1]).find(Boolean);
254
+ }
255
+ function isGeneratedMcpCommand(server, tool) {
256
+ const command = [server.command, ...Array.isArray(server.args) ? server.args : []].join(" ");
257
+ const args = (tool.mcpArgs ?? []).map((arg) => ` ${escapeRegExp(arg)}`).join("");
258
+ return new RegExp(`^npx -y ${escapeRegExp(tool.mcpPackage)}(?:@\\S+)?${args}$`).test(command) || Object.values(localMcpCommands(tool)).some((local) => command === [local.command, ...local.args].join(" "));
259
+ }
193
260
  const CLAUDE_MEMORY_FILES = ["CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"];
194
- function withVersion(content, version) {
195
- return content.replace(/^(#{1,2} .+)\n/, `$1
261
+ function withVersion(content, version, tool) {
262
+ const note = tool.versionNote?.(version) ?? `> ${tool.name} ${version} is installed: these rules match this version.`;
263
+ return content.replace(/^(#{1,2} .+)\n/, (_, title) => `${title}
196
264
 
197
- > maz-ui ${version} is installed: these rules and the docs in \`node_modules/maz-ui/docs\` match this version.
265
+ ${note}
198
266
  `);
199
267
  }
200
- function managedHeader(version) {
201
- return `<!-- ${MANAGED_MARKER} ${version}: generated by \`npx @maz-ui/mcp init\` (or the \`mazUi.agents\` Nuxt option). Do not edit, your changes would be overwritten: write your own rules elsewhere. -->`;
268
+ function checksum(body) {
269
+ return createHash("sha256").update(body.replaceAll(/\s+/g, "")).digest("hex").slice(0, 12);
270
+ }
271
+ function managedContent(version, body, setup) {
272
+ return `<!-- ${setup.marker} ${version} (sha256:${checksum(body)}): generated by ${setup.tool.generatedBy}. Do not edit: edited rules are no longer updated. Write your own rules elsewhere. -->
273
+
274
+ ${body}`;
275
+ }
276
+ function isEditedManagedContent(content, tool = MAZ_UI_AGENTS_TOOL) {
277
+ const header = content === void 0 ? null : resolveTool(tool).managedHeader.exec(content);
278
+ const recorded = header ? /\(sha256:([\da-f]+)\)/.exec(header[0])?.[1] : void 0;
279
+ return header !== null && recorded !== void 0 && checksum(content.slice(header.index + header[0].length)) !== recorded;
202
280
  }
203
281
  function detectAgentTargets(rootDir) {
204
282
  const has = (path) => existsSync(join(rootDir, path));
@@ -229,18 +307,59 @@ function readPackageVersion(packageDir) {
229
307
  const file = join(packageDir, "package.json");
230
308
  return existsSync(file) ? JSON.parse(readFileSync(file, "utf8")).version : void 0;
231
309
  }
232
- function findProjectPackage(rootDir, name) {
233
- const bases = [rootDir];
234
- for (const folder of ["apps", "packages"]) {
235
- const root = join(rootDir, folder);
236
- if (existsSync(root)) {
237
- for (const entry of readdirSync(root, { withFileTypes: true })) {
238
- if (entry.isDirectory())
239
- bases.push(join(root, entry.name));
310
+ const DEFAULT_WORKSPACES = ["apps/*", "packages/*"];
311
+ const MAX_GLOBSTAR_DEPTH = 3;
312
+ function readWorkspacePatterns(rootDir) {
313
+ const pnpmWorkspace = join(rootDir, "pnpm-workspace.yaml");
314
+ const workspaces = readPackageJson(rootDir)?.workspaces;
315
+ const declared = existsSync(pnpmWorkspace) ? readPnpmWorkspacePackages(readFileSync(pnpmWorkspace, "utf8")) : (Array.isArray(workspaces) ? workspaces : workspaces?.packages) ?? [];
316
+ const patterns = declared.filter((pattern) => !pattern.startsWith("!")).map((pattern) => pattern.replace(/^\.\//, "").replace(/\/$/, ""));
317
+ return patterns.length > 0 ? patterns : DEFAULT_WORKSPACES;
318
+ }
319
+ function readPnpmWorkspacePackages(content) {
320
+ const patterns = [];
321
+ let inPackages = false;
322
+ for (const line of content.split(/\r?\n/)) {
323
+ const trimmed = line.trim();
324
+ if (!trimmed || trimmed.startsWith("#"))
325
+ continue;
326
+ if (line === line.trimStart())
327
+ inPackages = trimmed === "packages:";
328
+ else if (inPackages && trimmed.startsWith("-"))
329
+ patterns.push(trimmed.slice(1).split("#")[0].trim().replaceAll(/^["']|["']$/g, ""));
330
+ }
331
+ return patterns;
332
+ }
333
+ function globToRegExp(pattern) {
334
+ const source = pattern.split("/").map((segment) => segment === "**" ? ".*" : segment.replaceAll(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("*", "[^/]*").replaceAll("?", "[^/]")).join("/");
335
+ return new RegExp(`^${source.replaceAll("/.*", "(?:/.*)?")}$`);
336
+ }
337
+ function findWorkspaceDirs(rootDir) {
338
+ const folders = /* @__PURE__ */ new Set();
339
+ for (const pattern of readWorkspacePatterns(rootDir)) {
340
+ const segments = pattern.split("/");
341
+ const firstGlob = segments.findIndex((segment) => /[*?[{]/.test(segment));
342
+ const base = (firstGlob === -1 ? segments : segments.slice(0, firstGlob)).join("/");
343
+ const depth = firstGlob === -1 ? 0 : segments.includes("**") ? MAX_GLOBSTAR_DEPTH : segments.length - firstGlob;
344
+ const matcher = globToRegExp(pattern);
345
+ const visit = (folder, remaining) => {
346
+ if (!existsSync(join(rootDir, folder)))
347
+ return;
348
+ if (folder && folder !== "." && matcher.test(folder))
349
+ folders.add(join(rootDir, folder));
350
+ if (remaining === 0)
351
+ return;
352
+ for (const entry of readdirSync(join(rootDir, folder), { withFileTypes: true })) {
353
+ if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith("."))
354
+ visit(folder ? `${folder}/${entry.name}` : entry.name, remaining - 1);
240
355
  }
241
- }
356
+ };
357
+ visit(base, depth);
242
358
  }
243
- return bases.map((base) => join(base, "node_modules", name)).find((candidate) => existsSync(join(candidate, "package.json"))) ?? findInstalledPackage(rootDir, name);
359
+ return [...folders];
360
+ }
361
+ function findProjectPackage(rootDir, name) {
362
+ return [rootDir, ...findWorkspaceDirs(rootDir)].map((base) => join(base, "node_modules", name)).find((candidate) => existsSync(join(candidate, "package.json"))) ?? findInstalledPackage(rootDir, name);
244
363
  }
245
364
  function readAgentDocs(rootDir, docsDir) {
246
365
  let directory = docsDir;
@@ -264,6 +383,57 @@ function readAgentDocs(rootDir, docsDir) {
264
383
  agentsMdSection: readFileSync(agentsMdFile, "utf8").trim()
265
384
  };
266
385
  }
386
+ function writeFileAtomic(path, content) {
387
+ const temporary = join(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`);
388
+ mkdirSync(dirname(path), { recursive: true });
389
+ try {
390
+ writeFileSync(temporary, content);
391
+ renameSync(temporary, path);
392
+ } catch (error) {
393
+ rmSync(temporary, { force: true });
394
+ throw error;
395
+ }
396
+ }
397
+ function parseVersion(version) {
398
+ const [core, ...prerelease] = version.replace(/^v/, "").split("+")[0].split("-");
399
+ return {
400
+ core: core.split(".").map((part) => Number.parseInt(part, 10) || 0),
401
+ prerelease: prerelease.join("-").split(".").filter(Boolean)
402
+ };
403
+ }
404
+ function comparePrereleaseIdentifiers(a, b) {
405
+ const isNumeric = (value) => /^\d+$/.test(value);
406
+ if (isNumeric(a) !== isNumeric(b))
407
+ return isNumeric(a) ? -1 : 1;
408
+ return isNumeric(a) ? Number(a) - Number(b) : a.localeCompare(b);
409
+ }
410
+ function compareVersions(a, b) {
411
+ const left = parseVersion(a);
412
+ const right = parseVersion(b);
413
+ for (let index = 0; index < 3; index++) {
414
+ const difference = (left.core[index] ?? 0) - (right.core[index] ?? 0);
415
+ if (difference !== 0)
416
+ return difference;
417
+ }
418
+ if (left.prerelease.length === 0 || right.prerelease.length === 0)
419
+ return right.prerelease.length - left.prerelease.length;
420
+ for (let index = 0; index < Math.min(left.prerelease.length, right.prerelease.length); index++) {
421
+ const difference = comparePrereleaseIdentifiers(left.prerelease[index], right.prerelease[index]);
422
+ if (difference !== 0)
423
+ return difference;
424
+ }
425
+ return left.prerelease.length - right.prerelease.length;
426
+ }
427
+ function readManagedVersion(content, tool = MAZ_UI_AGENTS_TOOL) {
428
+ return content ? resolveTool(tool).managedVersion.exec(content)?.[1] : void 0;
429
+ }
430
+ function newerManagedVersion(content, version, tool) {
431
+ const written = readManagedVersion(content, tool);
432
+ return written && version && compareVersions(written, version) > 0 ? written : void 0;
433
+ }
434
+ function newerVersionReason(written, version, tool) {
435
+ return `written by ${tool.name} ${written}, newer than the installed ${version}: left untouched`;
436
+ }
267
437
  class ChangeSet {
268
438
  constructor(rootDir, dryRun) {
269
439
  this.rootDir = rootDir;
@@ -277,16 +447,14 @@ class ChangeSet {
277
447
  const path = this.absolute(file);
278
448
  return existsSync(path) ? readFileSync(path, "utf8") : void 0;
279
449
  }
280
- write(file, content, current) {
450
+ write(file, content, current, reason) {
281
451
  if (current === content) {
282
452
  this.changes.push({ file, action: "unchanged" });
283
453
  return;
284
454
  }
285
- this.changes.push({ file, action: current === void 0 ? "create" : "update" });
286
- if (!this.dryRun) {
287
- mkdirSync(dirname(this.absolute(file)), { recursive: true });
288
- writeFileSync(this.absolute(file), content);
289
- }
455
+ this.changes.push({ file, action: current === void 0 ? "create" : "update", ...reason ? { reason } : {} });
456
+ if (!this.dryRun)
457
+ writeFileAtomic(this.absolute(file), content);
290
458
  }
291
459
  delete(file) {
292
460
  this.changes.push({ file, action: "remove" });
@@ -300,13 +468,23 @@ class ChangeSet {
300
468
  this.changes.push({ file, action: "unchanged", reason });
301
469
  }
302
470
  }
303
- function isOwned(content) {
304
- return content === void 0 || content.includes(MANAGED_MARKER);
471
+ function isOwned(content, setup) {
472
+ return content === void 0 || content.includes(setup.marker);
305
473
  }
306
- function syncOwnedFile(changes, file, content) {
474
+ function syncOwnedFile(changes, file, content, version, setup) {
475
+ const { tool } = setup;
307
476
  const current = changes.read(file);
308
- if (!isOwned(current)) {
309
- changes.skip(file, "file exists and was not generated by maz-ui: left untouched");
477
+ if (!isOwned(current, setup)) {
478
+ changes.skip(file, `file exists and was not generated by ${tool.name}: left untouched`);
479
+ return;
480
+ }
481
+ const newer = content === void 0 ? void 0 : newerManagedVersion(current, version, tool);
482
+ if (newer) {
483
+ changes.skip(file, newerVersionReason(newer, version, tool));
484
+ return;
485
+ }
486
+ if (content !== void 0 && isEditedManagedContent(current, tool)) {
487
+ changes.skip(file, `edited since it was generated: left untouched, delete it to get the rules of ${tool.name} ${version}`);
310
488
  return;
311
489
  }
312
490
  if (content === void 0) {
@@ -316,16 +494,17 @@ function syncOwnedFile(changes, file, content) {
316
494
  }
317
495
  changes.write(file, content, current);
318
496
  }
319
- function syncAgentsMd(changes, section) {
497
+ function syncAgentsMd(changes, section, version, setup) {
498
+ const { tool, agentsMdBegin, agentsMdEnd } = setup;
320
499
  const file = "AGENTS.md";
321
500
  const current = changes.read(file);
322
- const begin = current?.indexOf(AGENTS_MD_BEGIN) ?? -1;
323
- const end = current?.indexOf(AGENTS_MD_END) ?? -1;
501
+ const begin = current?.indexOf(agentsMdBegin) ?? -1;
502
+ const end = current?.indexOf(agentsMdEnd) ?? -1;
324
503
  const hasBlock = begin !== -1 && end > begin;
325
504
  if (section === void 0) {
326
505
  if (!current || !hasBlock)
327
506
  return;
328
- const remaining = `${current.slice(0, begin)}${current.slice(end + AGENTS_MD_END.length)}`.replace(/\n{3,}/g, "\n\n").trim();
507
+ const remaining = `${current.slice(0, begin)}${current.slice(end + agentsMdEnd.length)}`.replace(/\n{3,}/g, "\n\n").trim();
329
508
  if (remaining)
330
509
  changes.write(file, `${remaining}
331
510
  `, current);
@@ -333,20 +512,30 @@ function syncAgentsMd(changes, section) {
333
512
  changes.delete(file);
334
513
  return;
335
514
  }
336
- const block = `${AGENTS_MD_BEGIN}
515
+ const block = hasBlock ? current?.slice(begin + agentsMdBegin.length, end) : void 0;
516
+ const newer = newerManagedVersion(block, version, tool);
517
+ if (newer) {
518
+ changes.skip(file, newerVersionReason(newer, version, tool));
519
+ return;
520
+ }
521
+ if (isEditedManagedContent(block, tool)) {
522
+ changes.skip(file, `the ${tool.name} section was edited since it was generated: left untouched, delete the section to get the rules of ${tool.name} ${version}`);
523
+ return;
524
+ }
525
+ const wrapped = `${agentsMdBegin}
337
526
 
338
527
  ${section}
339
528
 
340
- ${AGENTS_MD_END}`;
529
+ ${agentsMdEnd}`;
341
530
  if (!current) {
342
- changes.write(file, `${block}
531
+ changes.write(file, `${wrapped}
343
532
  `, current);
344
533
  } else if (hasBlock) {
345
- changes.write(file, `${current.slice(0, begin)}${block}${current.slice(end + AGENTS_MD_END.length)}`, current);
534
+ changes.write(file, `${current.slice(0, begin)}${wrapped}${current.slice(end + agentsMdEnd.length)}`, current);
346
535
  } else {
347
536
  changes.write(file, `${current.trimEnd()}
348
537
 
349
- ${block}
538
+ ${wrapped}
350
539
  `, current);
351
540
  }
352
541
  }
@@ -355,9 +544,10 @@ const MCP_FILES = {
355
544
  cursor: { file: ".cursor/mcp.json", rootKey: "mcpServers", withType: false },
356
545
  vscode: { file: ".vscode/mcp.json", rootKey: "servers", withType: true }
357
546
  };
358
- function referencesMazUiMcp(server) {
547
+ function referencesToolMcp(server, tool) {
359
548
  const serialized = JSON.stringify(server);
360
- return serialized.includes("@maz-ui/mcp") || serialized.includes("maz-ui-mcp");
549
+ const args = isJsonObject(server) && Array.isArray(server.args) ? server.args : [];
550
+ return (serialized.includes(tool.mcpPackage) || serialized.includes(tool.mcpBin)) && (tool.mcpArgs ?? []).every((arg) => args.includes(arg));
361
551
  }
362
552
  function detectFormatting(content) {
363
553
  const indent = /^( +|\t)"/m.exec(content)?.[1] ?? " ";
@@ -366,7 +556,31 @@ function detectFormatting(content) {
366
556
  function isJsonObject(value) {
367
557
  return typeof value === "object" && value !== null && !Array.isArray(value);
368
558
  }
369
- function syncMcpConfig(changes, config, server, remove) {
559
+ function updateMcpCommand(changes, config, [key, entry], server, tool) {
560
+ const current = entry;
561
+ const label = [server.command, ...server.args].join(" ");
562
+ if (current.command === server.command && JSON.stringify(current.args) === JSON.stringify(server.args)) {
563
+ changes.unchanged(config.file, `${tool.name} MCP server already registered as "${key}"`);
564
+ return;
565
+ }
566
+ if (!isGeneratedMcpCommand(current, tool)) {
567
+ changes.unchanged(config.file, `"${key}" runs a custom command: left untouched`);
568
+ return;
569
+ }
570
+ const pinned = pinnedMcpVersion(current, tool);
571
+ const wanted = pinnedMcpVersion(server, tool);
572
+ const isLocal = !server.args.includes("-y");
573
+ if (pinned && !isLocal && (!wanted || compareVersions(pinned, wanted) >= 0)) {
574
+ changes.unchanged(config.file, `"${key}" runs ${tool.mcpPackage} ${pinned}`);
575
+ return;
576
+ }
577
+ const file = changes.read(config.file);
578
+ const formatting = detectFormatting(file);
579
+ let content = applyEdits(file, modify(file, [config.rootKey, key, "command"], server.command, { formattingOptions: formatting }));
580
+ content = applyEdits(content, modify(content, [config.rootKey, key, "args"], server.args, { formattingOptions: formatting }));
581
+ changes.write(config.file, content, file, `"${key}" now runs ${label}`);
582
+ }
583
+ function syncMcpConfig(changes, config, server, remove, tool) {
370
584
  const current = changes.read(config.file);
371
585
  const errors = [];
372
586
  const parsed = current === void 0 ? {} : parse(current, errors, { allowTrailingComma: true });
@@ -375,12 +589,12 @@ function syncMcpConfig(changes, config, server, remove) {
375
589
  return;
376
590
  }
377
591
  const servers = parsed[config.rootKey] ?? {};
378
- const existing = Object.entries(servers).find(([, value2]) => referencesMazUiMcp(value2));
592
+ const existing = Object.entries(servers).find(([, value2]) => referencesToolMcp(value2, tool));
379
593
  const base = current ?? "{}\n";
380
594
  const formatting = detectFormatting(base);
381
595
  if (remove) {
382
- if (existing?.[0] === MCP_SERVER_NAME) {
383
- const content2 = applyEdits(base, modify(base, [config.rootKey, MCP_SERVER_NAME], void 0, { formattingOptions: formatting }));
596
+ if (existing?.[0] === tool.name) {
597
+ const content2 = applyEdits(base, modify(base, [config.rootKey, tool.name], void 0, { formattingOptions: formatting }));
384
598
  const remaining = parse(content2);
385
599
  const isEmpty = Object.keys(remaining).length === 1 && Object.keys(remaining[config.rootKey]).length === 0;
386
600
  if (isEmpty)
@@ -391,50 +605,54 @@ function syncMcpConfig(changes, config, server, remove) {
391
605
  return;
392
606
  }
393
607
  if (existing) {
394
- changes.unchanged(config.file, `maz-ui MCP server already registered as "${existing[0]}"`);
608
+ updateMcpCommand(changes, config, existing, server, tool);
395
609
  return;
396
610
  }
397
- if (servers[MCP_SERVER_NAME] !== void 0) {
398
- changes.skip(config.file, `a "${MCP_SERVER_NAME}" server already exists with another command: left untouched`);
611
+ if (servers[tool.name] !== void 0) {
612
+ changes.skip(config.file, `a "${tool.name}" server already exists with another command: left untouched`);
399
613
  return;
400
614
  }
401
615
  const value = config.withType ? { type: "stdio", ...server } : { ...server };
402
- const content = applyEdits(base, modify(base, [config.rootKey, MCP_SERVER_NAME], value, { formattingOptions: formatting }));
616
+ const content = applyEdits(base, modify(base, [config.rootKey, tool.name], value, { formattingOptions: formatting }));
403
617
  changes.write(config.file, content.endsWith("\n") ? content : `${content}
404
618
  `, current);
405
619
  }
406
- function syncRules(changes, rootDir, targets, docs) {
620
+ function syncRules(changes, rootDir, targets, docs, setup) {
407
621
  const version = docs?.version ?? "";
408
622
  const hasClaudeMemory = CLAUDE_MEMORY_FILES.some((file) => existsSync(join(rootDir, file)));
409
623
  const claudeReadsAgentsMd = targets.includes("agents-md") && !hasClaudeMemory;
410
624
  if (targets.includes("claude")) {
411
- const rule = docs && !claudeReadsAgentsMd ? `${managedHeader(version)}
412
-
413
- ${withVersion(docs.claudeRule, version)}
625
+ const rule = docs && !claudeReadsAgentsMd ? `${managedContent(version, withVersion(docs.claudeRule, version, setup.tool), setup)}
414
626
  ` : void 0;
415
- syncOwnedFile(changes, CLAUDE_RULE_FILE, rule);
627
+ syncOwnedFile(changes, setup.ruleFile, rule, version, setup);
416
628
  }
417
629
  if (targets.includes("agents-md"))
418
- syncAgentsMd(changes, docs ? `${managedHeader(version)}
419
-
420
- ${withVersion(docs.agentsMdSection, version)}` : void 0);
630
+ syncAgentsMd(changes, docs ? managedContent(version, withVersion(docs.agentsMdSection, version, setup.tool), setup) : void 0, version, setup);
631
+ }
632
+ function resolveAgentDocs(options, tool) {
633
+ if (options.agentDocs)
634
+ return options.agentDocs;
635
+ if (tool !== MAZ_UI_AGENTS_TOOL)
636
+ throw new Error(`setupAgents needs the rules of ${tool.name}: pass them with the agentDocs option.`);
637
+ return readAgentDocs(options.rootDir, options.docsDir);
421
638
  }
422
639
  function setupAgents(options) {
423
- const { rootDir, rules = true, mcp = true, dryRun = false, remove = false } = options;
640
+ const { rootDir, rules = true, mcp = true, dryRun = false, remove = false, tool = MAZ_UI_AGENTS_TOOL } = options;
641
+ const setup = resolveTool(tool);
424
642
  const targets = options.targets ?? detectAgentTargets(rootDir);
425
- const docs = remove || !rules ? void 0 : readAgentDocs(rootDir, options.docsDir);
643
+ const docs = remove || !rules ? void 0 : resolveAgentDocs(options, tool);
426
644
  const version = docs?.version ?? "";
427
645
  const changes = new ChangeSet(rootDir, dryRun);
428
- const server = options.mcpServer ?? DEFAULT_MCP_SERVER;
646
+ const server = options.mcpServer ?? mcpServerCommand(rootDir, options.mcpVersion, tool);
429
647
  if (rules || remove)
430
- syncRules(changes, rootDir, targets, docs);
648
+ syncRules(changes, rootDir, targets, docs, setup);
431
649
  if (mcp || remove) {
432
650
  for (const target of ["claude", "cursor", "vscode"]) {
433
651
  if (targets.includes(target))
434
- syncMcpConfig(changes, MCP_FILES[target], server, remove);
652
+ syncMcpConfig(changes, MCP_FILES[target], server, remove, tool);
435
653
  }
436
654
  }
437
- return { targets, version, changes: changes.changes };
655
+ return { targets, version, changes: changes.changes, mcpServer: server, localMcpServer: !server.args.includes("-y") };
438
656
  }
439
657
 
440
- export { AGENTS_MD_BEGIN, AGENTS_MD_END, AGENT_TARGETS, DEFAULT_MCP_SERVER, MANAGED_MARKER, MCP_SERVER_NAME, createLogger, detectAgentTargets, execPromise, findInstalledPackage, findProjectPackage, logger, printBanner, readAgentDocs, readPackageVersion, redactError, redactSecrets, setupAgents };
658
+ export { AGENTS_MD_BEGIN, AGENTS_MD_END, AGENT_TARGETS, DEFAULT_MCP_SERVER, MANAGED_MARKER, MAZ_UI_AGENTS_TOOL, MCP_SERVER_NAME, compareVersions, createLogger, detectAgentTargets, detectPackageManager, execPromise, findInstalledPackage, findProjectPackage, findWorkspaceDirs, installsMcpPackage, isEditedManagedContent, logger, mcpServerCommand, printBanner, readAgentDocs, readManagedVersion, readPackageVersion, readWorkspacePatterns, redactError, redactSecrets, setupAgents, writeFileAtomic };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maz-ui/node",
3
3
  "type": "module",
4
- "version": "5.1.0-beta.1",
4
+ "version": "5.2.0-beta.0",
5
5
  "description": "Node utilities for Maz-UI library",
6
6
  "author": "Louis Mazel <me@loicmazuel.com>",
7
7
  "license": "MIT",
@@ -48,7 +48,14 @@
48
48
  "jsonc-parser": "^3.3.1"
49
49
  },
50
50
  "devDependencies": {
51
- "unbuild": "^3.6.1"
51
+ "@types/node": "^22.20.5",
52
+ "@vitest/coverage-v8": "^5.0.5",
53
+ "cross-env": "^10.1.0",
54
+ "eslint": "^10.13.0",
55
+ "tsx": "^4.23.15",
56
+ "typescript": "^6.0.3",
57
+ "unbuild": "^3.6.1",
58
+ "vitest": "5.0.5"
52
59
  },
53
60
  "lint-staged": {
54
61
  "*.{js,jsx,ts,tsx,mjs,mts,cjs,md,yml,json}": [