create-nudge-ui 0.2.1 → 0.2.3

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
@@ -1,59 +1,105 @@
1
- # `create-nudge-ui`
1
+ # create-nudge-ui
2
2
 
3
- `create-nudge-ui` detects an application's host framework, installs the
4
- corresponding Nudge UI adapter, and updates the host configuration. The guided
5
- setup can also connect a coding agent to the project-local Nudge MCP server.
3
+ Add Nudge UI to an existing app with one command. `create-nudge-ui` detects your
4
+ framework, installs `nudge-ui`, and updates your development configuration. It
5
+ can also connect your coding agent so you can send design changes from the browser.
6
6
 
7
- Run the initializer from the application root:
7
+ [Nudge UI](https://www.npmjs.com/package/nudge-ui) lets you inspect your running
8
+ app, preview visual edits, and turn them into a prompt for a coding agent.
9
+
10
+ ## Get started
11
+
12
+ From your application's root directory, run:
8
13
 
9
14
  ```sh
10
15
  npm create nudge-ui@latest
11
16
  ```
12
17
 
13
- The initializer supports Next.js, Astro, Vite with React, and static HTML. Host
14
- frameworks take precedence over their underlying tools, so an Astro project
15
- with React islands receives `nudge-ui/astro`, not `nudge-ui/vite`.
18
+ Follow the prompts, then start your app with its usual development command and
19
+ open the local URL. For static HTML, run the `nudge-ui serve` command printed by
20
+ the initializer.
21
+
22
+ Requires Node.js 20 or later and a supported app. Your framework or build tool
23
+ may require a newer Node.js version.
16
24
 
17
- In an interactive terminal, the initializer asks whether to connect a coding
18
- agent. It detects installed agents, installs the matching `@nudge-ui/mcp`
19
- version in the project, and adds a project-scoped MCP entry. The entry launches
20
- the package from this project's `node_modules` directory and passes the
21
- canonical workspace path, so different Git worktrees do not share a server
22
- configuration.
25
+ ## What setup does
23
26
 
24
- Reload an agent that was already running, then ask it to “listen to Nudge.”
27
+ - Detects Next.js, Astro, Vite with React, or a static HTML directory.
28
+ - Installs `nudge-ui` as a development dependency using npm, pnpm, Yarn, or Bun.
29
+ - Adds the framework integration to your configuration. Static HTML uses the
30
+ bundled development server instead.
31
+ - Offers to connect a coding agent through
32
+ [`@nudge-ui/mcp`](https://www.npmjs.com/package/@nudge-ui/mcp).
25
33
 
26
- Use an explicit framework when automatic detection is ambiguous:
34
+ Run it inside the app directory in a monorepo. If detection is ambiguous, choose
35
+ the framework explicitly:
27
36
 
28
37
  ```sh
29
38
  npm create nudge-ui@latest -- --framework astro
30
39
  ```
31
40
 
32
- Use `--dry-run` to print the package installation and configuration change
33
- without modifying the project. Run `npm create nudge-ui@latest -- --help` for
34
- the complete command reference.
41
+ ## Connect a coding agent
35
42
 
36
- For scripts, select the MCP behavior explicitly:
43
+ Choose agent setup during initialization, or add it later:
37
44
 
38
45
  ```sh
39
- # Configure detected agents without prompts.
40
- npm create nudge-ui@latest -- --mcp --yes
46
+ npm create nudge-ui@latest -- --agent-only
47
+ ```
41
48
 
42
- # Configure one or more supported add-mcp agent IDs.
43
- npm create nudge-ui@latest -- --mcp --agent codex --agent cursor --yes
49
+ Setup installs a bridge dependency in the app and a reusable MCP adapter under
50
+ `~/.nudge-ui/adapters/`. It registers the adapter globally for the selected
51
+ agents, so other checkouts and Git worktrees can use it. Each app still needs
52
+ its own Nudge integration and bridge dependency.
44
53
 
45
- # Skip agent setup.
46
- npm create nudge-ui@latest -- --no-mcp
47
- ```
54
+ Setup verifies the adapter before changing agent settings. If automatic
55
+ configuration fails, it prints a global stdio configuration you
56
+ can add manually. npm must be available to install the reusable adapter, even
57
+ when your app uses another package manager.
48
58
 
49
- Use agent-only mode to add or repair the integration without changing the host
50
- framework configuration:
59
+ Fully restart your agent host, then ask it to **listen to Nudge in this
60
+ workspace**. Keep the agent listening while you send changes from the browser.
61
+ The MCP integration is early alpha; host behavior and setup may change.
62
+
63
+ ## Common commands
51
64
 
52
65
  ```sh
53
- npm create nudge-ui@latest -- --agent-only --agent codex --yes
66
+ # Preview setup without changing files or installing packages.
67
+ npm create nudge-ui@latest -- --dry-run
68
+
69
+ # Install the inspector without agent setup.
70
+ npm create nudge-ui@latest -- --no-mcp
71
+
72
+ # Set up a specific agent without prompts.
73
+ npm create nudge-ui@latest -- --agent codex --yes
74
+
75
+ # Connect an agent to an existing Nudge installation.
76
+ npm create nudge-ui@latest -- --agent-only --agent cursor --yes
54
77
  ```
55
78
 
56
- Agent configuration is best-effort. If an agent is not supported or its native
57
- configuration cannot be updated, the initializer prints the complete stdio
58
- configuration to add to that agent's project settings. Other MCP servers and
59
- agent settings are preserved on every run.
79
+ ## Options
80
+
81
+ Pass options after `--` when using `npm create`.
82
+
83
+ | Option | Purpose |
84
+ | --- | --- |
85
+ | `--framework <name>` | Select `nextjs`, `astro`, `vite-react`, or `standalone`. |
86
+ | `--package-manager <name>` | Select `npm`, `pnpm`, `yarn`, or `bun`. |
87
+ | `--mcp` | Enable agent setup. |
88
+ | `--no-mcp` | Skip agent setup. |
89
+ | `--agent <id>` | Select an agent, such as `codex` or `cursor`. Repeat for multiple agents. Enables agent setup. |
90
+ | `--agent-only` | Install or repair agent integration without changing framework configuration. |
91
+ | `--yes`, `-y` | Accept defaults without prompts, including setup for detected agents. Use `--no-mcp` to skip agent setup. |
92
+ | `--dry-run` | Print planned changes without applying them. |
93
+ | `--mcp-package <specifier>` | Use a specific MCP package or local tarball for both the bridge and adapter. |
94
+ | `--help`, `-h` | Show command help. |
95
+
96
+ For troubleshooting and custom integrations, see the
97
+ [coding-agent guide](https://github.com/charlessmart/nudge-ui/blob/main/docs/agent-integration.md).
98
+ For manual framework setup, see
99
+ [`nudge-ui`](https://www.npmjs.com/package/nudge-ui).
100
+
101
+ ## Links
102
+
103
+ [Repository](https://github.com/charlessmart/nudge-ui) ·
104
+ [Issues](https://github.com/charlessmart/nudge-ui/issues) ·
105
+ [MIT license](https://github.com/charlessmart/nudge-ui/blob/main/LICENSE)
@@ -6,25 +6,29 @@ export interface AgentSetupPlan {
6
6
  readonly projectRoot: string;
7
7
  readonly packageSpecifier: string;
8
8
  readonly installCommand: InstallCommand;
9
+ readonly adapterRoot: string;
10
+ readonly adapterInstallCommand: InstallCommand;
9
11
  readonly serverConfig: McpServerConfig;
10
- readonly serverEntry?: string;
12
+ readonly serverEntry: string;
11
13
  }
12
14
  export interface AgentInstallOutcome {
13
15
  readonly agent: string;
14
16
  readonly displayName: string;
15
17
  readonly result?: InstallResult;
16
18
  readonly unsupported: boolean;
19
+ readonly migrationError?: string;
20
+ readonly backups?: readonly string[];
17
21
  }
18
- /** Builds the reproducible, project-local MCP installation and launch plan. */
19
- export declare function planAgentSetup(projectRoot: string, packageManager: PackageManager, nudgeUiVersion?: string): AgentSetupPlan;
22
+ /** Plans the project bridge dependency and an independent, versioned adapter installation. */
23
+ export declare function planAgentSetup(projectRoot: string, packageManager: PackageManager, nudgeUiVersion?: string, adapterDirectory?: string, mcpPackageSpecifier?: string): AgentSetupPlan;
20
24
  /** Checks that a node_modules based install produced the configured executable. */
21
25
  export declare function isAgentServerAvailable(plan: AgentSetupPlan): boolean;
22
- /** Returns agents that can store an MCP configuration in this project. */
26
+ /** Returns agents that support a reusable stdio MCP configuration. */
23
27
  export declare function projectAgentTypes(): AgentType[];
24
- /** Detects installed or already configured agents while preserving project scope. */
28
+ /** Detects installed or already configured agents. */
25
29
  export declare function detectProjectAgentCandidates(projectRoot: string): Promise<AgentType[]>;
26
- /** Adds or repairs Nudge's project-scoped MCP entry without replacing other servers. */
27
- export declare function configureProjectAgents(plan: AgentSetupPlan, selectedAgents: readonly string[]): AgentInstallOutcome[];
30
+ /** Registers the reusable adapter and removes the current project's overriding entry. */
31
+ export declare function configureProjectAgents(plan: AgentSetupPlan, selectedAgents: readonly string[]): Promise<AgentInstallOutcome[]>;
28
32
  /** Formats a complete stdio entry for agents that add-mcp cannot configure. */
29
33
  export declare function manualAgentConfiguration(plan: AgentSetupPlan): string;
30
34
  export declare function projectAgentChoices(detected: readonly AgentType[]): Array<{
@@ -1,40 +1,40 @@
1
- import { existsSync, readFileSync, realpathSync } from "node:fs";
1
+ import { randomUUID } from "node:crypto";
2
+ import { copyFileSync, existsSync, readFileSync, realpathSync } from "node:fs";
3
+ import { homedir } from "node:os";
2
4
  import { join } from "node:path";
3
- import { agents, detectGlobalAgents, detectProjectAgents, getAgentTypes, upsertServer, } from "add-mcp";
5
+ import { agents, detectGlobalAgents, detectProjectAgents, getAgentTypes, upsertServer, listInstalledServers, removeServer, } from "add-mcp";
4
6
  import { installCommand } from "./installer.js";
5
7
  export const nudgeServerName = "nudge_ui";
6
- /** Builds the reproducible, project-local MCP installation and launch plan. */
7
- export function planAgentSetup(projectRoot, packageManager, nudgeUiVersion) {
8
+ /** Plans the project bridge dependency and an independent, versioned adapter installation. */
9
+ export function planAgentSetup(projectRoot, packageManager, nudgeUiVersion, adapterDirectory = join(homedir(), ".nudge-ui", "adapters"), mcpPackageSpecifier) {
8
10
  const canonicalRoot = realpathSync.native(projectRoot);
9
- const packageSpecifier = `@nudge-ui/mcp@${nudgeUiVersion ?? installedNudgeUiVersion(canonicalRoot) ?? initializerVersion()}`;
10
- const serverEntry = join(canonicalRoot, "node_modules", "@nudge-ui", "mcp", "dist", "cli.mjs");
11
- const usesPlugAndPlay = packageManager === "yarn";
12
- const serverConfig = usesPlugAndPlay
13
- ? {
14
- command: "yarn",
15
- args: ["--cwd", canonicalRoot, "exec", "nudge-mcp", "--workspace-root", canonicalRoot],
16
- }
17
- : {
18
- command: "node",
19
- args: [serverEntry, "--workspace-root", canonicalRoot],
20
- };
11
+ const packageSpecifier = mcpPackageSpecifier ?? `@nudge-ui/mcp@${nudgeUiVersion ?? installedNudgeUiVersion(canonicalRoot) ?? initializerVersion()}`;
12
+ const adapterPackageSpecifier = mcpPackageSpecifier ?? `@nudge-ui/mcp@${initializerVersion()}`;
13
+ const adapterRoot = join(adapterDirectory, initializerVersion());
14
+ const serverEntry = join(adapterRoot, "node_modules", "@nudge-ui", "mcp", "dist", "cli.mjs");
15
+ const serverConfig = { command: process.execPath, args: [serverEntry] };
21
16
  return {
22
17
  projectRoot: canonicalRoot,
23
18
  packageSpecifier,
24
19
  installCommand: installCommand(packageManager, packageSpecifier),
25
20
  serverConfig,
26
- serverEntry: usesPlugAndPlay ? undefined : serverEntry,
21
+ adapterRoot,
22
+ adapterInstallCommand: {
23
+ executable: "npm",
24
+ args: ["install", "--prefix", adapterRoot, "--no-save", "--package-lock=false", adapterPackageSpecifier],
25
+ },
26
+ serverEntry,
27
27
  };
28
28
  }
29
29
  /** Checks that a node_modules based install produced the configured executable. */
30
30
  export function isAgentServerAvailable(plan) {
31
- return plan.serverEntry === undefined || existsSync(plan.serverEntry);
31
+ return existsSync(plan.serverEntry);
32
32
  }
33
- /** Returns agents that can store an MCP configuration in this project. */
33
+ /** Returns agents that support a reusable stdio MCP configuration. */
34
34
  export function projectAgentTypes() {
35
- return getAgentTypes().filter((agent) => agents[agent].localConfigPath !== undefined);
35
+ return getAgentTypes().filter((agent) => agents[agent].supportedTransports.includes("stdio"));
36
36
  }
37
- /** Detects installed or already configured agents while preserving project scope. */
37
+ /** Detects installed or already configured agents. */
38
38
  export async function detectProjectAgentCandidates(projectRoot) {
39
39
  const supported = new Set(projectAgentTypes());
40
40
  const detected = [
@@ -43,25 +43,45 @@ export async function detectProjectAgentCandidates(projectRoot) {
43
43
  ].filter((agent) => supported.has(agent));
44
44
  return [...new Set(detected)];
45
45
  }
46
- /** Adds or repairs Nudge's project-scoped MCP entry without replacing other servers. */
47
- export function configureProjectAgents(plan, selectedAgents) {
46
+ /** Registers the reusable adapter and removes the current project's overriding entry. */
47
+ export async function configureProjectAgents(plan, selectedAgents) {
48
48
  const supported = new Set(projectAgentTypes());
49
- return [...new Set(selectedAgents)].map((agent) => {
49
+ const outcomes = [];
50
+ for (const agent of new Set(selectedAgents)) {
50
51
  if (!supported.has(agent)) {
51
- return { agent, displayName: agent, unsupported: true };
52
+ outcomes.push({ agent, displayName: agent, unsupported: true });
53
+ continue;
52
54
  }
53
55
  const agentType = agent;
54
- const result = upsertServer(agentType, nudgeServerName, plan.serverConfig, {
55
- cwd: plan.projectRoot,
56
- local: true,
56
+ const global = await listInstalledServers({ agents: [agentType], global: true, cwd: plan.projectRoot });
57
+ const local = agents[agentType].localConfigPath
58
+ ? await listInstalledServers({ agents: [agentType], global: false, cwd: plan.projectRoot })
59
+ : [];
60
+ const configurations = [...global, ...local];
61
+ const invalid = configurations.find((configuration) => configuration.error);
62
+ if (invalid) {
63
+ outcomes.push({ agent, displayName: agents[agentType].displayName, unsupported: false,
64
+ result: { success: false, path: invalid.configPath, error: invalid.error } });
65
+ continue;
66
+ }
67
+ const backups = [];
68
+ for (const configuration of configurations) {
69
+ if (!configuration.servers.some((server) => server.serverName === nudgeServerName))
70
+ continue;
71
+ const backup = `${configuration.configPath}.nudge-backup-${randomUUID()}`;
72
+ copyFileSync(configuration.configPath, backup);
73
+ backups.push(backup);
74
+ }
75
+ const result = upsertServer(agentType, nudgeServerName, plan.serverConfig, { local: false, cwd: plan.projectRoot });
76
+ const removal = result.success && agents[agentType].localConfigPath
77
+ ? removeServer(agentType, nudgeServerName, { local: true, cwd: plan.projectRoot })
78
+ : undefined;
79
+ outcomes.push({
80
+ agent, displayName: agents[agentType].displayName, result, unsupported: false, backups,
81
+ ...(removal && !removal.success ? { migrationError: removal.error ?? "Could not remove the project override." } : {}),
57
82
  });
58
- return {
59
- agent,
60
- displayName: agents[agentType].displayName,
61
- result,
62
- unsupported: false,
63
- };
64
- });
83
+ }
84
+ return outcomes;
65
85
  }
66
86
  /** Formats a complete stdio entry for agents that add-mcp cannot configure. */
67
87
  export function manualAgentConfiguration(plan) {
@@ -0,0 +1,3 @@
1
+ import type { McpServerConfig } from "add-mcp";
2
+ /** Verifies the configured executable, initialization, and tools without claiming a project. */
3
+ export declare function verifyAgentServer(config: McpServerConfig): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ /** Verifies the configured executable, initialization, and tools without claiming a project. */
4
+ export async function verifyAgentServer(config) {
5
+ if (!config.command)
6
+ throw new Error("The MCP adapter has no executable.");
7
+ const client = new Client({ name: "nudge-setup", version: "1.0.0" });
8
+ const transport = new StdioClientTransport({ command: config.command, args: config.args, stderr: "inherit" });
9
+ try {
10
+ await client.connect(transport, { timeout: 15_000 });
11
+ const { tools } = await client.listTools({}, { timeout: 15_000 });
12
+ const listen = tools.find((tool) => tool.name === "nudge_listen");
13
+ if (!listen?.inputSchema.required?.includes("workspaceRoot") || !tools.some((tool) => tool.name === "nudge_list_sessions")) {
14
+ throw new Error("The installed adapter does not support connection-time project selection. Upgrade @nudge-ui/mcp.");
15
+ }
16
+ }
17
+ finally {
18
+ await transport.close();
19
+ await client.close();
20
+ }
21
+ }
@@ -4,6 +4,7 @@ export interface CliOptions {
4
4
  readonly packageManager?: PackageManager;
5
5
  readonly agentOnly: boolean;
6
6
  readonly agents: readonly string[];
7
+ readonly mcpPackageSpecifier?: string;
7
8
  readonly mcp?: boolean;
8
9
  readonly yes: boolean;
9
10
  readonly dryRun: boolean;
@@ -11,4 +12,4 @@ export interface CliOptions {
11
12
  }
12
13
  /** Parses the initializer command line. */
13
14
  export declare function parseArguments(args: readonly string[]): CliOptions;
14
- export declare const helpText = "Set up Nudge UI in the current project.\n\nUsage: npm create nudge-ui@latest -- [options]\n\nOptions:\n --framework <nextjs|astro|vite-react|standalone>\n --package-manager <pnpm|npm|yarn|bun>\n --mcp Configure the local Nudge MCP server\n --no-mcp Skip coding-agent setup\n --agent <id> Configure a supported agent (repeatable)\n --agent-only Install or repair only the agent integration\n -y, --yes Accept defaults without prompting\n --dry-run\n -h, --help";
15
+ export declare const helpText = "Set up Nudge UI in the current project.\n\nUsage: npm create nudge-ui@latest -- [options]\n\nOptions:\n --framework <nextjs|astro|vite-react|standalone>\n --package-manager <pnpm|npm|yarn|bun>\n --mcp Configure the reusable Nudge MCP adapter\n --no-mcp Skip coding-agent setup\n --agent <id> Configure a supported agent (repeatable)\n --mcp-package <path|package> Use this MCP package for the project and adapter\n --agent-only Install or repair only the agent integration\n -y, --yes Accept defaults without prompting\n --dry-run\n -h, --help";
package/dist/arguments.js CHANGED
@@ -5,6 +5,7 @@ export function parseArguments(args) {
5
5
  let packageManager;
6
6
  let agentOnly = false;
7
7
  const agents = [];
8
+ let mcpPackageSpecifier;
8
9
  let mcp;
9
10
  let yes = false;
10
11
  let dryRun = false;
@@ -32,6 +33,10 @@ export function parseArguments(args) {
32
33
  agents.push(requiredValue(args, ++index, argument));
33
34
  else if (argument.startsWith("--agent="))
34
35
  agents.push(requiredValue([argument.slice("--agent=".length)], 0, "--agent"));
36
+ else if (argument === "--mcp-package")
37
+ mcpPackageSpecifier = requiredValue(args, ++index, argument);
38
+ else if (argument.startsWith("--mcp-package="))
39
+ mcpPackageSpecifier = requiredValue([argument.slice("--mcp-package=".length)], 0, "--mcp-package");
35
40
  else if (argument === "--help" || argument === "-h")
36
41
  help = true;
37
42
  else if (argument === "--framework")
@@ -51,7 +56,7 @@ export function parseArguments(args) {
51
56
  throw new Error("--agent cannot be combined with --no-mcp.");
52
57
  if (agentOnly && mcp === false)
53
58
  throw new Error("--agent-only cannot be combined with --no-mcp.");
54
- return { framework, packageManager, agentOnly, agents, mcp, yes, dryRun, help };
59
+ return { framework, packageManager, agentOnly, agents, mcpPackageSpecifier, mcp, yes, dryRun, help };
55
60
  }
56
61
  function setMcpPreference(current, next) {
57
62
  if (current !== undefined && current !== next)
@@ -71,9 +76,10 @@ Usage: npm create nudge-ui@latest -- [options]
71
76
  Options:
72
77
  --framework <nextjs|astro|vite-react|standalone>
73
78
  --package-manager <pnpm|npm|yarn|bun>
74
- --mcp Configure the local Nudge MCP server
79
+ --mcp Configure the reusable Nudge MCP adapter
75
80
  --no-mcp Skip coding-agent setup
76
81
  --agent <id> Configure a supported agent (repeatable)
82
+ --mcp-package <path|package> Use this MCP package for the project and adapter
77
83
  --agent-only Install or repair only the agent integration
78
84
  -y, --yes Accept defaults without prompting
79
85
  --dry-run
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { type PackageManager } from "./types.js";
2
2
  export interface RunAgentSetupOptions {
3
3
  readonly agents?: readonly string[];
4
+ /** Optional package specifier or tarball used for both bridge and adapter testing. */
5
+ readonly mcpPackageSpecifier?: string;
4
6
  readonly dryRun?: boolean;
5
7
  readonly nudgeUiVersion?: string;
6
8
  readonly packageManager?: PackageManager;
@@ -8,5 +10,5 @@ export interface RunAgentSetupOptions {
8
10
  }
9
11
  /** Runs the Nudge UI project initializer. */
10
12
  export declare function runCli(args?: string[], projectRoot?: string): Promise<void>;
11
- /** Installs and configures the project-local MCP companion. Safe to rerun for repairs. */
13
+ /** Installs and configures the reusable MCP adapter and project bridge dependency. Safe to rerun for repairs. */
12
14
  export declare function runAgentSetup(projectRoot?: string, options?: RunAgentSetupOptions): Promise<void>;
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
- import { rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { verifyAgentServer } from "./agent-verification.js";
2
3
  import { createInterface } from "node:readline/promises";
3
4
  import { stdin, stdout } from "node:process";
4
5
  import { basename, relative } from "node:path";
@@ -20,6 +21,7 @@ export async function runCli(args = process.argv.slice(2), projectRoot = process
20
21
  if (options.agentOnly) {
21
22
  await runAgentSetup(projectRoot, {
22
23
  agents: options.agents,
24
+ mcpPackageSpecifier: options.mcpPackageSpecifier,
23
25
  dryRun: options.dryRun,
24
26
  packageManager,
25
27
  yes: options.yes,
@@ -44,6 +46,7 @@ export async function runCli(args = process.argv.slice(2), projectRoot = process
44
46
  if (await shouldConfigureAgent(options.mcp, options.agents.length > 0, options.yes)) {
45
47
  await runAgentSetup(projectRoot, {
46
48
  agents: options.agents,
49
+ mcpPackageSpecifier: options.mcpPackageSpecifier,
47
50
  dryRun: true,
48
51
  nudgeUiVersion: initializerVersion(),
49
52
  packageManager,
@@ -73,17 +76,18 @@ export async function runCli(args = process.argv.slice(2), projectRoot = process
73
76
  if (await shouldConfigureAgent(options.mcp, options.agents.length > 0, options.yes)) {
74
77
  await runAgentSetup(projectRoot, {
75
78
  agents: options.agents,
79
+ mcpPackageSpecifier: options.mcpPackageSpecifier,
76
80
  nudgeUiVersion: initializerVersion(),
77
81
  packageManager,
78
82
  yes: options.yes,
79
83
  });
80
84
  }
81
85
  }
82
- /** Installs and configures the project-local MCP companion. Safe to rerun for repairs. */
86
+ /** Installs and configures the reusable MCP adapter and project bridge dependency. Safe to rerun for repairs. */
83
87
  export async function runAgentSetup(projectRoot = process.cwd(), options = {}) {
84
88
  const manifest = readProjectManifest(projectRoot);
85
89
  const packageManager = options.packageManager ?? detectPackageManager(projectRoot, manifest);
86
- const plan = planAgentSetup(projectRoot, packageManager, options.nudgeUiVersion);
90
+ const plan = planAgentSetup(projectRoot, packageManager, options.nudgeUiVersion, undefined, options.mcpPackageSpecifier);
87
91
  const detected = await detectProjectAgentCandidates(plan.projectRoot);
88
92
  const selected = options.agents && options.agents.length > 0
89
93
  ? options.agents
@@ -92,8 +96,9 @@ export async function runAgentSetup(projectRoot = process.cwd(), options = {}) {
92
96
  : await selectAgents(detected);
93
97
  if (options.dryRun) {
94
98
  stdout.write(`Would run: ${formatCommand(plan.installCommand)}\n`);
99
+ stdout.write(`Would run: ${formatCommand(plan.adapterInstallCommand)}\n`);
95
100
  if (selected.length > 0) {
96
- stdout.write(`Would configure project MCP for: ${selected.join(", ")}\n`);
101
+ stdout.write(`Would configure global MCP and migrate this project override for: ${selected.join(", ")}\n`);
97
102
  }
98
103
  else {
99
104
  stdout.write("No coding agent was detected. Use --agent <id> or add the configuration below manually.\n");
@@ -102,11 +107,33 @@ export async function runAgentSetup(projectRoot = process.cwd(), options = {}) {
102
107
  return;
103
108
  }
104
109
  installPackage(plan.installCommand, plan.projectRoot);
110
+ try {
111
+ mkdirSync(plan.adapterRoot, { recursive: true });
112
+ installPackage(plan.adapterInstallCommand, plan.adapterRoot);
113
+ }
114
+ catch (error) {
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ stdout.write(`Could not install the reusable MCP adapter: ${message}\n`);
117
+ stdout.write(`Retry with: ${formatCommand(plan.adapterInstallCommand)}\n`);
118
+ stdout.write("The coding-agent configuration was not changed. After the adapter install succeeds, rerun with --agent-only.\n");
119
+ return;
120
+ }
105
121
  if (!isAgentServerAvailable(plan)) {
106
- stdout.write(`Installed ${plan.packageSpecifier}, but its local MCP executable was not found at ${plan.serverEntry}.\n`);
122
+ stdout.write(`Installed ${plan.packageSpecifier}, but its reusable MCP executable was not found at ${plan.serverEntry}.\n`);
123
+ stdout.write(`Retry with: ${formatCommand(plan.adapterInstallCommand)}\n`);
107
124
  stdout.write("The coding-agent configuration was not changed. Check the package-manager install output, then rerun with --agent-only.\n");
108
125
  return;
109
126
  }
127
+ try {
128
+ await verifyAgentServer(plan.serverConfig);
129
+ stdout.write("Verified MCP initialization and tool discovery with a fresh client.\n");
130
+ }
131
+ catch (error) {
132
+ stdout.write(`MCP verification failed: ${error instanceof Error ? error.message : String(error)}\n`);
133
+ stdout.write(`Retry with: ${formatCommand(plan.adapterInstallCommand)}\n`);
134
+ stdout.write("The coding-agent configuration was not changed. After repairing the adapter install, rerun with --agent-only.\n");
135
+ return;
136
+ }
110
137
  if (selected.length === 0) {
111
138
  stdout.write(`Installed ${plan.packageSpecifier}, but no coding agent was detected.\n`);
112
139
  writeManualInstructions(plan);
@@ -114,7 +141,7 @@ export async function runAgentSetup(projectRoot = process.cwd(), options = {}) {
114
141
  }
115
142
  let outcomes;
116
143
  try {
117
- outcomes = configureProjectAgents(plan, selected);
144
+ outcomes = await configureProjectAgents(plan, selected);
118
145
  }
119
146
  catch (error) {
120
147
  const message = error instanceof Error ? error.message : String(error);
@@ -125,7 +152,13 @@ export async function runAgentSetup(projectRoot = process.cwd(), options = {}) {
125
152
  let configured = 0;
126
153
  for (const outcome of outcomes) {
127
154
  if (outcome.unsupported) {
128
- stdout.write(`${outcome.displayName} is not supported for automatic project setup.\n`);
155
+ stdout.write(`${outcome.displayName} is not supported for automatic global setup.\n`);
156
+ continue;
157
+ }
158
+ for (const backup of outcome.backups ?? [])
159
+ stdout.write(`Saved previous MCP configuration: ${backup}\n`);
160
+ if (outcome.migrationError) {
161
+ stdout.write(`Global adapter configured, but project override migration failed: ${outcome.migrationError}\n`);
129
162
  continue;
130
163
  }
131
164
  if (outcome.result?.success) {
@@ -138,7 +171,7 @@ export async function runAgentSetup(projectRoot = process.cwd(), options = {}) {
138
171
  if (configured !== outcomes.length)
139
172
  writeManualInstructions(plan);
140
173
  if (configured > 0) {
141
- stdout.write("Coding-agent setup is ready. Reload the agent if it is already open, then ask it to listen to Nudge.\n");
174
+ stdout.write("Adapter configuration is ready. Fully restart the agent host to load it, then ask it to listen to Nudge in the current workspace. Existing overrides in other projects require one setup rerun there.\n");
142
175
  }
143
176
  }
144
177
  async function shouldConfigureAgent(preference, hasExplicitAgents, yes) {
@@ -189,7 +222,7 @@ async function selectAgents(detected) {
189
222
  }
190
223
  }
191
224
  function writeManualInstructions(plan) {
192
- stdout.write("Add this project-scoped stdio server to your agent's MCP settings:\n");
225
+ stdout.write("Add this global stdio server to your agent's MCP settings:\n");
193
226
  stdout.write(`${manualAgentConfiguration(plan)}\n`);
194
227
  }
195
228
  async function selectFramework(detected) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nudge-ui",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Framework-detecting installer for Nudge UI.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,7 +30,8 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "add-mcp": "^2.4.0",
33
- "typescript": "^5.9.3"
33
+ "typescript": "^5.9.3",
34
+ "@modelcontextprotocol/sdk": "^1.30.0"
34
35
  },
35
36
  "devDependencies": {
36
37
  "vitest": "^2.1.9"