ai-agent-config 2.5.8 → 2.6.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
@@ -33,14 +33,31 @@ ai-agent update
33
33
  | Command | Description |
34
34
  |---------|-------------|
35
35
  | `init --repo <url>` | Initialize config and clone repo |
36
- | `push` / `pull` | Git push/pull with your skills repo |
37
- | `update` | Pull -> sync external skills -> push |
36
+ | `push [--message "msg"]` | Git push to your skills repo |
37
+ | `pull` | Git pull from repo + auto-install |
38
+ | `update` | Pull → sync external skills → push |
38
39
  | `install` | Copy skills to platform directories |
39
40
  | `list` | List installed skills |
40
41
  | `platforms` | Show detected platforms |
41
- | `source add/remove/list` | Manage skill sources |
42
- | `config get/set/edit` | Manage configuration |
43
42
  | `uninstall` | Remove installed skills |
43
+ | `source add <url>` | Add custom skill source |
44
+ | `source remove <name>` | Remove skill source |
45
+ | `source list` | List all sources |
46
+ | `source enable <name>` | Enable a source |
47
+ | `source disable <name>` | Disable a source |
48
+ | `source info <name>` | View source details |
49
+ | `config get <key>` | Get config value |
50
+ | `config set <key> <value>` | Set config value |
51
+ | `config edit` | Open config in $EDITOR |
52
+ | `config validate` | Validate configuration |
53
+ | `config export [file]` | Export configuration |
54
+ | `config import [file]` | Import configuration |
55
+ | `config reset --yes` | Reset to defaults |
56
+ | `secrets sync` | Sync MCP secrets from Bitwarden vault |
57
+ | `sync-external` | Alias for `update` |
58
+ | `list-external` | List available external skills |
59
+ | `version` | Show version |
60
+ | `help` | Show help |
44
61
 
45
62
  ## Supported Platforms
46
63
 
@@ -53,22 +70,44 @@ ai-agent update
53
70
  | Codex CLI | `~/.codex/skills/` |
54
71
  | GitHub Copilot | `~/.github/copilot-instructions.md` |
55
72
 
73
+ ## Secret Management
74
+
75
+ Securely sync MCP secrets from Bitwarden vault to your shell profile:
76
+
77
+ ```bash
78
+ ai-agent secrets sync
79
+ ```
80
+
81
+ **How it works:**
82
+ - Discovers required secrets from MCP config files (e.g., `${GITHUB_TOKEN}`)
83
+ - Fetches secrets from Bitwarden vault folder "MCP Secrets"
84
+ - Writes to `~/.zshrc` for persistence across sessions
85
+ - Never stores Bitwarden master password
86
+
87
+ **Setup:** See [Bitwarden MCP Setup Guide](./mcp-servers/bitwarden/README.md)
88
+
89
+ **Auto-configuration:** Package automatically configures Bitwarden MCP server in Antigravity on install
90
+
56
91
  ## Configuration
57
92
 
58
93
  User config at `~/.ai-agent/config.json`:
59
94
 
60
95
  ```json
61
96
  {
62
- "version": "2.3",
97
+ "version": "2.5",
63
98
  "repository": {
64
99
  "url": "https://github.com/youruser/my-ai-skills.git",
65
100
  "branch": "main",
66
- "local": "~/.ai-agent/sync-repo"
101
+ "local": "/Users/you/.ai-agent/sync-repo"
67
102
  },
68
- "sources": {
69
- "official": [],
70
- "custom": []
71
- }
103
+ "sources": [
104
+ {
105
+ "name": "vercel-labs",
106
+ "url": "https://github.com/vercel-labs/agent-skills.git",
107
+ "enabled": true
108
+ }
109
+ ],
110
+ "lastSync": "2026-02-13T12:00:00.000Z"
72
111
  }
73
112
  ```
74
113
 
package/bin/cli.js CHANGED
@@ -199,6 +199,21 @@ function listSkills() {
199
199
  });
200
200
  }
201
201
 
202
+ // MCP Servers
203
+ const mcpInstaller = require("../scripts/mcp-installer");
204
+ const mcpServers = mcpInstaller.getAvailableMcpServers();
205
+
206
+ console.log("\nMCP Servers:");
207
+ if (mcpServers.length === 0) {
208
+ console.log(" (no MCP servers found)");
209
+ } else {
210
+ mcpServers.forEach((server) => {
211
+ const status = server.enabled === false ? " (disabled)" : "";
212
+ const desc = server.description ? ` - ${server.description}` : "";
213
+ console.log(` • ${server.name}${desc}${status}`);
214
+ });
215
+ }
216
+
202
217
  console.log(`\nSource: ${installer.CACHE_DIR}`);
203
218
  console.log("");
204
219
  }
@@ -668,6 +683,13 @@ function install(args) {
668
683
  if (result.skillsCount > 0) parts.push(`${result.skillsCount} skill(s)`);
669
684
  if (result.workflowsCount > 0) parts.push(`${result.workflowsCount} workflow(s)`);
670
685
 
686
+ // Count MCP servers across all platforms
687
+ let mcpTotal = 0;
688
+ result.details.forEach((d) => {
689
+ if (d.mcpServers) mcpTotal += d.mcpServers.added + d.mcpServers.skipped;
690
+ });
691
+ if (mcpTotal > 0) parts.push(`${mcpTotal} MCP server(s)`);
692
+
671
693
  console.log(`\n✓ Installed ${parts.join(", ")} to ${result.platformsCount} platform(s)\n`);
672
694
  result.details.forEach((d) => {
673
695
  console.log(` ${d.platform}:`);
@@ -685,7 +707,18 @@ function install(args) {
685
707
  console.log(` • ${w.name} ${status}`);
686
708
  });
687
709
  }
710
+ if (d.mcpServers && d.mcpServers.servers.length > 0) {
711
+ console.log(` MCP Servers: ${d.mcpServers.added} added, ${d.mcpServers.skipped} skipped`);
712
+ d.mcpServers.servers.forEach((name) => {
713
+ console.log(` • ${name}`);
714
+ });
715
+ }
688
716
  });
717
+
718
+ // Hint about secrets sync if MCP servers were installed
719
+ if (mcpTotal > 0) {
720
+ console.log("\n💡 Run 'ai-agent secrets sync' to resolve Bitwarden secrets");
721
+ }
689
722
  } else {
690
723
  console.log("\n⚠️ No skills or workflows installed.");
691
724
  }
@@ -830,7 +863,7 @@ function pull(args) {
830
863
  const noInstall = args.includes("--no-install");
831
864
 
832
865
  if (!noInstall) {
833
- console.log("📥 Auto-installing skills...\n");
866
+ console.log("📥 Auto-installing skills + MCP servers...\n");
834
867
  install(["--force"]); // Force install to ensure latest
835
868
  }
836
869
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-agent-config",
3
- "version": "2.5.8",
3
+ "version": "2.6.0",
4
4
  "description": "Universal skill & workflow manager for AI coding assistants with bi-directional GitHub sync",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,6 +8,8 @@ const path = require("path");
8
8
  const { execSync } = require("child_process");
9
9
  const platforms = require("./platforms");
10
10
 
11
+ const mcpInstaller = require("./mcp-installer");
12
+
11
13
  const REPO_URL = "https://github.com/dongitran/ai-agent-config.git";
12
14
  const CACHE_DIR = path.join(platforms.HOME, ".ai-agent-config-cache");
13
15
  const REPO_SKILLS_DIR = path.join(CACHE_DIR, ".agent", "skills");
@@ -178,6 +180,7 @@ function installToPlatform(platform, options = {}) {
178
180
  workflowsPath: workflowsPath,
179
181
  skills: [],
180
182
  workflows: [],
183
+ mcpServers: null,
181
184
  };
182
185
 
183
186
  // Install skills
@@ -230,6 +233,15 @@ function installToPlatform(platform, options = {}) {
230
233
  }
231
234
  }
232
235
 
236
+ // Install MCP servers (Antigravity only for now)
237
+ if (platform.name === "antigravity") {
238
+ try {
239
+ results.mcpServers = mcpInstaller.installMcpServers({ force });
240
+ } catch (error) {
241
+ console.warn(` ⚠️ MCP install failed: ${error.message}`);
242
+ }
243
+ }
244
+
233
245
  return results;
234
246
  }
235
247
 
@@ -0,0 +1,280 @@
1
+ /**
2
+ * MCP Installer Module
3
+ * Handles discovering, validating, and installing MCP servers from repo to platforms
4
+ */
5
+
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const platforms = require("./platforms");
9
+ const configManager = require("./config-manager");
10
+
11
+ const SKIP_FOLDERS = ["bitwarden"];
12
+
13
+ /**
14
+ * Get the MCP servers directory from the user's sync repo
15
+ * @returns {string|null} Path to .agent/mcp-servers/ or null
16
+ */
17
+ function getMcpServersDir() {
18
+ const config = configManager.loadConfig();
19
+ const repoLocal = config.repository && config.repository.local;
20
+ if (!repoLocal) return null;
21
+
22
+ const expanded = repoLocal.replace(/^~/, process.env.HOME || process.env.USERPROFILE);
23
+ const mcpDir = path.join(expanded, ".agent", "mcp-servers");
24
+ return fs.existsSync(mcpDir) ? mcpDir : null;
25
+ }
26
+
27
+ /**
28
+ * Validate an MCP server config
29
+ * @param {Object} config - Parsed config.json
30
+ * @returns {{ valid: boolean, errors: string[] }}
31
+ */
32
+ function validateMcpConfig(config) {
33
+ const errors = [];
34
+
35
+ if (!config.name || typeof config.name !== "string") {
36
+ errors.push("Missing or invalid 'name' field");
37
+ }
38
+ if (!config.command || typeof config.command !== "string") {
39
+ errors.push("Missing or invalid 'command' field");
40
+ }
41
+ if (!Array.isArray(config.args)) {
42
+ errors.push("Missing or invalid 'args' field (must be array)");
43
+ }
44
+ if (config.bitwardenEnv && typeof config.bitwardenEnv !== "object") {
45
+ errors.push("'bitwardenEnv' must be an object");
46
+ }
47
+ if (config.disabledTools && !Array.isArray(config.disabledTools)) {
48
+ errors.push("'disabledTools' must be an array");
49
+ }
50
+
51
+ return { valid: errors.length === 0, errors };
52
+ }
53
+
54
+ /**
55
+ * Get all available MCP servers from the repo
56
+ * @returns {Array<Object>} Array of parsed and validated MCP server configs
57
+ */
58
+ function getAvailableMcpServers() {
59
+ const mcpDir = getMcpServersDir();
60
+ if (!mcpDir) return [];
61
+
62
+ const servers = [];
63
+
64
+ const entries = fs.readdirSync(mcpDir, { withFileTypes: true });
65
+ for (const entry of entries) {
66
+ if (!entry.isDirectory()) continue;
67
+ if (SKIP_FOLDERS.includes(entry.name)) continue;
68
+
69
+ const configPath = path.join(mcpDir, entry.name, "config.json");
70
+ if (!fs.existsSync(configPath)) continue;
71
+
72
+ try {
73
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
74
+ const validation = validateMcpConfig(config);
75
+ if (!validation.valid) {
76
+ console.warn(` ⚠️ Invalid MCP config in ${entry.name}/: ${validation.errors.join(", ")}`);
77
+ continue;
78
+ }
79
+ servers.push(config);
80
+ } catch (error) {
81
+ console.warn(` ⚠️ Failed to parse ${entry.name}/config.json: ${error.message}`);
82
+ }
83
+ }
84
+
85
+ return servers;
86
+ }
87
+
88
+ /**
89
+ * Collect all bitwardenEnv entries from MCP server configs
90
+ * @returns {Array<{ serverName: string, envVar: string, bitwardenItem: string }>}
91
+ */
92
+ function collectBitwardenEnvs() {
93
+ const servers = getAvailableMcpServers();
94
+ const envs = [];
95
+
96
+ for (const server of servers) {
97
+ if (server.enabled === false) continue;
98
+ if (!server.bitwardenEnv) continue;
99
+
100
+ for (const [envVar, bitwardenItem] of Object.entries(server.bitwardenEnv)) {
101
+ envs.push({
102
+ serverName: server.name,
103
+ envVar,
104
+ bitwardenItem,
105
+ });
106
+ }
107
+ }
108
+
109
+ return envs;
110
+ }
111
+
112
+ /**
113
+ * Install MCP servers to Antigravity's mcp_config.json
114
+ * This is the "pull" flow - installs structure without resolved secrets
115
+ * @param {Object} options - { force: boolean }
116
+ * @returns {{ added: number, skipped: number, servers: string[] }}
117
+ */
118
+ function installMcpServers(options = {}) {
119
+ const { force = false } = options;
120
+ const antigravity = platforms.getByName("antigravity");
121
+
122
+ if (!antigravity || !antigravity.mcpConfigPath) {
123
+ return { added: 0, skipped: 0, servers: [] };
124
+ }
125
+
126
+ const servers = getAvailableMcpServers().filter((s) => s.enabled !== false);
127
+ if (servers.length === 0) {
128
+ return { added: 0, skipped: 0, servers: [] };
129
+ }
130
+
131
+ // Read existing mcp_config.json
132
+ let mcpConfig = { mcpServers: {} };
133
+ if (fs.existsSync(antigravity.mcpConfigPath)) {
134
+ try {
135
+ mcpConfig = JSON.parse(fs.readFileSync(antigravity.mcpConfigPath, "utf-8"));
136
+ if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
137
+ } catch {
138
+ mcpConfig = { mcpServers: {} };
139
+ }
140
+ }
141
+
142
+ let added = 0;
143
+ let skipped = 0;
144
+ const serverNames = [];
145
+
146
+ for (const server of servers) {
147
+ const existing = mcpConfig.mcpServers[server.name];
148
+
149
+ if (existing && !force) {
150
+ skipped++;
151
+ serverNames.push(server.name);
152
+ continue;
153
+ }
154
+
155
+ // Build server entry for Antigravity format
156
+ const entry = {
157
+ command: server.command,
158
+ args: server.args,
159
+ };
160
+
161
+ // Preserve existing env if not forcing
162
+ if (existing && existing.env) {
163
+ entry.env = existing.env;
164
+ }
165
+
166
+ if (server.disabledTools && server.disabledTools.length > 0) {
167
+ entry.disabledTools = server.disabledTools;
168
+ }
169
+
170
+ mcpConfig.mcpServers[server.name] = entry;
171
+ added++;
172
+ serverNames.push(server.name);
173
+ }
174
+
175
+ // Write back
176
+ if (added > 0) {
177
+ const configDir = path.dirname(antigravity.mcpConfigPath);
178
+ if (!fs.existsSync(configDir)) {
179
+ fs.mkdirSync(configDir, { recursive: true });
180
+ }
181
+ fs.writeFileSync(antigravity.mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
182
+ }
183
+
184
+ return { added, skipped, servers: serverNames };
185
+ }
186
+
187
+ /**
188
+ * Install MCP servers with resolved secrets to Antigravity
189
+ * This is the "secrets sync" flow - updates env with real values
190
+ * @param {Object} resolvedSecrets - Map of envVar → resolvedValue
191
+ * @returns {{ installed: number, servers: Array<{ name: string, secretsCount: number }> }}
192
+ */
193
+ function installMcpServersWithSecrets(resolvedSecrets) {
194
+ const antigravity = platforms.getByName("antigravity");
195
+
196
+ if (!antigravity || !antigravity.mcpConfigPath) {
197
+ return { installed: 0, servers: [] };
198
+ }
199
+
200
+ const allServers = getAvailableMcpServers().filter((s) => s.enabled !== false);
201
+ if (allServers.length === 0) {
202
+ return { installed: 0, servers: [] };
203
+ }
204
+
205
+ // Read existing mcp_config.json
206
+ let mcpConfig = { mcpServers: {} };
207
+ if (fs.existsSync(antigravity.mcpConfigPath)) {
208
+ try {
209
+ mcpConfig = JSON.parse(fs.readFileSync(antigravity.mcpConfigPath, "utf-8"));
210
+ if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
211
+ } catch {
212
+ mcpConfig = { mcpServers: {} };
213
+ }
214
+ }
215
+
216
+ let installed = 0;
217
+ const serverResults = [];
218
+
219
+ for (const server of allServers) {
220
+ const existing = mcpConfig.mcpServers[server.name] || {};
221
+
222
+ // Build entry: keep existing custom fields (disabledTools user may have customized)
223
+ const entry = {
224
+ command: server.command,
225
+ args: server.args,
226
+ };
227
+
228
+ // Build env from resolved secrets
229
+ if (server.bitwardenEnv) {
230
+ const env = {};
231
+ let secretsCount = 0;
232
+
233
+ for (const [envVar, bitwardenItem] of Object.entries(server.bitwardenEnv)) {
234
+ if (resolvedSecrets[bitwardenItem]) {
235
+ env[envVar] = resolvedSecrets[bitwardenItem];
236
+ secretsCount++;
237
+ }
238
+ }
239
+
240
+ if (Object.keys(env).length > 0) {
241
+ entry.env = { ...(existing.env || {}), ...env };
242
+ }
243
+
244
+ serverResults.push({ name: server.name, secretsCount });
245
+ } else {
246
+ serverResults.push({ name: server.name, secretsCount: 0 });
247
+ }
248
+
249
+ // Preserve user-customized disabledTools from existing config
250
+ if (existing.disabledTools) {
251
+ entry.disabledTools = existing.disabledTools;
252
+ } else if (server.disabledTools && server.disabledTools.length > 0) {
253
+ entry.disabledTools = server.disabledTools;
254
+ }
255
+
256
+ mcpConfig.mcpServers[server.name] = entry;
257
+ installed++;
258
+ }
259
+
260
+ // Write back
261
+ if (installed > 0) {
262
+ const configDir = path.dirname(antigravity.mcpConfigPath);
263
+ if (!fs.existsSync(configDir)) {
264
+ fs.mkdirSync(configDir, { recursive: true });
265
+ }
266
+ fs.writeFileSync(antigravity.mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
267
+ }
268
+
269
+ return { installed, servers: serverResults };
270
+ }
271
+
272
+ module.exports = {
273
+ getMcpServersDir,
274
+ validateMcpConfig,
275
+ getAvailableMcpServers,
276
+ collectBitwardenEnvs,
277
+ installMcpServers,
278
+ installMcpServersWithSecrets,
279
+ SKIP_FOLDERS,
280
+ };
@@ -42,6 +42,7 @@ const SUPPORTED = [
42
42
  configDir: ".gemini/antigravity",
43
43
  skillsDir: "skills",
44
44
  workflowsDir: "workflows",
45
+ mcpConfigFile: "mcp_config.json",
45
46
  get configPath() {
46
47
  return path.join(HOME, this.configDir);
47
48
  },
@@ -51,6 +52,9 @@ const SUPPORTED = [
51
52
  get workflowsPath() {
52
53
  return path.join(HOME, this.configDir, this.workflowsDir);
53
54
  },
55
+ get mcpConfigPath() {
56
+ return path.join(HOME, this.configDir, this.mcpConfigFile);
57
+ },
54
58
  detect() {
55
59
  // Check for .gemini directory or Antigravity app
56
60
  return (
@@ -73,6 +73,24 @@ function main() {
73
73
  BW_CLIENT_ID: "${BW_CLIENT_ID}",
74
74
  BW_CLIENT_SECRET: "${BW_CLIENT_SECRET}",
75
75
  },
76
+ disabledTools: [
77
+ "lock", "sync", "status", "confirm",
78
+ "create_org_collection", "edit_org_collection", "edit_item_collections", "move",
79
+ "device_approval_list", "device_approval_approve", "device_approval_approve_all",
80
+ "device_approval_deny", "device_approval_deny_all",
81
+ "create_text_send", "create_file_send", "list_send", "get_send",
82
+ "edit_send", "delete_send", "remove_send_password",
83
+ "create_attachment",
84
+ "list_org_collections", "get_org_collection", "update_org_collection", "delete_org_collection",
85
+ "list_org_members", "get_org_member", "get_org_member_groups",
86
+ "invite_org_member", "update_org_member", "update_org_member_groups",
87
+ "remove_org_member", "reinvite_org_member",
88
+ "list_org_groups", "get_org_group", "get_org_group_members",
89
+ "create_org_group", "update_org_group", "delete_org_group", "update_org_group_members",
90
+ "list_org_policies", "get_org_policy", "update_org_policy",
91
+ "get_org_events", "get_org_subscription", "update_org_subscription",
92
+ "import_org_users_and_groups"
93
+ ],
76
94
  };
77
95
  changed = true;
78
96
  console.log("🔐 Bitwarden MCP server added to Antigravity (✓ enabled)");
@@ -89,6 +107,30 @@ function main() {
89
107
  changed = true;
90
108
  console.log("🔓 Bitwarden MCP server configuration repaired and enabled");
91
109
  }
110
+
111
+ // Phase 4: Add disabledTools if not present (don't override if user customized)
112
+ if (!bw.disabledTools) {
113
+ bw.disabledTools = [
114
+ "lock", "sync", "status", "confirm",
115
+ "create_org_collection", "edit_org_collection", "edit_item_collections", "move",
116
+ "device_approval_list", "device_approval_approve", "device_approval_approve_all",
117
+ "device_approval_deny", "device_approval_deny_all",
118
+ "create_text_send", "create_file_send", "list_send", "get_send",
119
+ "edit_send", "delete_send", "remove_send_password",
120
+ "create_attachment",
121
+ "list_org_collections", "get_org_collection", "update_org_collection", "delete_org_collection",
122
+ "list_org_members", "get_org_member", "get_org_member_groups",
123
+ "invite_org_member", "update_org_member", "update_org_member_groups",
124
+ "remove_org_member", "reinvite_org_member",
125
+ "list_org_groups", "get_org_group", "get_org_group_members",
126
+ "create_org_group", "update_org_group", "delete_org_group", "update_org_group_members",
127
+ "list_org_policies", "get_org_policy", "update_org_policy",
128
+ "get_org_events", "get_org_subscription", "update_org_subscription",
129
+ "import_org_users_and_groups"
130
+ ];
131
+ changed = true;
132
+ console.log("🎛️ Bitwarden MCP: Added tool filters (disabled org-management tools)");
133
+ }
92
134
  }
93
135
 
94
136
  if (changed) {
@@ -153,35 +153,25 @@ function unlockBitwarden(password) {
153
153
  }
154
154
 
155
155
  /**
156
- * Discover required secrets from MCP configs
157
- * Scans ~/.gemini/antigravity/mcp_config.json for ${VAR} patterns
156
+ * Discover required secrets from MCP server configs in the repo
157
+ * Scans .agent/mcp-servers/{name}/config.json for bitwardenEnv fields
158
158
  */
159
159
  function discoverRequiredSecrets() {
160
- const platforms = require("./platforms");
161
- const antigravity = platforms.getByName("antigravity");
160
+ const mcpInstaller = require("./mcp-installer");
161
+ const envs = mcpInstaller.collectBitwardenEnvs();
162
162
 
163
- if (!antigravity) {
164
- return { found: false, secrets: [], reason: "Antigravity platform not detected" };
165
- }
166
-
167
- const mcpConfigPath = path.join(antigravity.configPath, "mcp_config.json");
168
-
169
- if (!fs.existsSync(mcpConfigPath)) {
170
- return { found: false, secrets: [], reason: "MCP config not found" };
163
+ if (envs.length === 0) {
164
+ const mcpDir = mcpInstaller.getMcpServersDir();
165
+ if (!mcpDir) {
166
+ return { found: false, secrets: [], reason: "No repository configured or no MCP servers found" };
167
+ }
168
+ return { found: true, secrets: [] };
171
169
  }
172
170
 
173
- try {
174
- const content = fs.readFileSync(mcpConfigPath, "utf-8");
175
-
176
- // Extract all ${VAR_NAME} patterns (supports mixed/lowercase)
177
- const regex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
178
- const matches = [...content.matchAll(regex)];
179
- const secretNames = [...new Set(matches.map((m) => m[1]))];
171
+ // Collect unique Bitwarden item names to fetch
172
+ const secretNames = [...new Set(envs.map((e) => e.bitwardenItem))];
180
173
 
181
- return { found: true, secrets: secretNames };
182
- } catch (error) {
183
- return { found: false, secrets: [], reason: error.message };
184
- }
174
+ return { found: true, secrets: secretNames, envs };
185
175
  }
186
176
 
187
177
  /**
@@ -334,22 +324,22 @@ async function syncSecrets() {
334
324
  console.log("✓ Vault unlocked\n");
335
325
  sessionKey = unlockResult.sessionKey;
336
326
 
337
- // 5. Discover required secrets
338
- console.log("🔍 Scanning MCP configs for required secrets...");
327
+ // 5. Discover required secrets from repo's bitwardenEnv
328
+ console.log("🔍 Scanning MCP server configs for required secrets...");
339
329
  const discovery = discoverRequiredSecrets();
340
330
 
341
331
  if (!discovery.found) {
342
332
  console.log(`⚠️ ${discovery.reason}`);
343
- console.log("\n💡 No secrets to sync. MCP configs will be available after implementing Plan 01.\n");
333
+ console.log("\n💡 Configure a repository first: ai-agent init --repo <url>\n");
344
334
  return;
345
335
  }
346
336
 
347
337
  if (discovery.secrets.length === 0) {
348
- console.log("No environment variables found in MCP configs.\n");
338
+ console.log("No bitwardenEnv entries found in MCP server configs.\n");
349
339
  return;
350
340
  }
351
341
 
352
- console.log(`Found ${discovery.secrets.length} secret(s):`);
342
+ console.log(`Found ${discovery.secrets.length} secret(s) to fetch:`);
353
343
  discovery.secrets.forEach((name) => {
354
344
  console.log(` • ${name}`);
355
345
  });
@@ -359,32 +349,42 @@ async function syncSecrets() {
359
349
  const fetchResults = fetchSecretsFromBitwarden(sessionKey, discovery.secrets);
360
350
 
361
351
  fetchResults.found.forEach((secret) => {
362
- console.log(`✓ ${secret.name} (found)`);
352
+ console.log(` ✓ ${secret.name}`);
363
353
  });
364
354
 
365
355
  fetchResults.missing.forEach((name) => {
366
- console.log(`⚠ ${name} (not found in vault)`);
356
+ console.log(` ⚠ ${name} (not found in vault)`);
367
357
  });
368
358
 
369
- // 7. Write to shell profile
359
+ // 7. Install MCP servers with resolved secrets to Antigravity
370
360
  if (fetchResults.found.length > 0) {
371
- console.log("\n💾 Writing secrets to shell profile...");
372
- const writeResult = writeToShellProfile(fetchResults.found);
373
- console.log(`✓ Added ${writeResult.count} environment variable(s) to ${writeResult.path}`);
361
+ const mcpInstaller = require("./mcp-installer");
362
+
363
+ // Build resolvedSecrets map: bitwardenItemName value
364
+ const resolvedSecrets = {};
365
+ fetchResults.found.forEach((s) => {
366
+ resolvedSecrets[s.name] = s.value;
367
+ });
368
+
369
+ console.log("\n📦 Installing MCP servers to Antigravity...");
370
+ const installResult = mcpInstaller.installMcpServersWithSecrets(resolvedSecrets);
371
+
372
+ installResult.servers.forEach((s) => {
373
+ if (s.secretsCount > 0) {
374
+ console.log(` ✓ ${s.name}: ${s.secretsCount} secret(s) resolved`);
375
+ } else {
376
+ console.log(` ✓ ${s.name}: no secrets needed`);
377
+ }
378
+ });
374
379
  }
375
380
 
376
381
  // 8. Summary
377
382
  console.log("\n✅ Secrets synced successfully!\n");
378
383
 
379
- console.log("ℹ️ Next steps:");
380
- console.log(" 1. Restart terminal or run: source ~/.zshrc");
381
-
382
384
  if (fetchResults.missing.length > 0) {
383
- console.log(` 2. Missing secrets: ${fetchResults.missing.join(", ")}`);
384
- console.log(" Add them to Bitwarden or set manually");
385
+ console.log(`⚠️ Missing secrets: ${fetchResults.missing.join(", ")}`);
386
+ console.log(` Add them to Bitwarden vault folder "${BITWARDEN_FOLDER}"\n`);
385
387
  }
386
-
387
- console.log("");
388
388
  } finally {
389
389
  // Cleanup: Lock vault to invalidate session key
390
390
  if (sessionKey) {
@@ -168,9 +168,13 @@ class SyncManager {
168
168
  // Add all .agent/ files except bundled package skills
169
169
  execSync("git add .agent/workflows/", { cwd: this.repoPath, stdio: "pipe" });
170
170
 
171
+ // Add MCP servers
172
+ const mcpServersDir = path.join(this.repoPath, ".agent/mcp-servers");
173
+ if (fs.existsSync(mcpServersDir)) {
174
+ execSync("git add .agent/mcp-servers/", { cwd: this.repoPath, stdio: "pipe" });
175
+ }
176
+
171
177
  // Add skills individually, excluding bundled ones
172
- const fs = require("fs");
173
- const path = require("path");
174
178
  const skillsDir = path.join(this.repoPath, ".agent/skills");
175
179
  const bundledSkills = ["ai-agent-config", "config-manager"];
176
180