@clix-so/clix-agent-skills 0.1.1 → 0.1.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
@@ -7,9 +7,9 @@ is a self-contained package that can be loaded and executed by AI clients.
7
7
 
8
8
  Agents skills on this repository are built on the
9
9
  [open agent skills standard](https://agentskills.io/home). Please refer to the
10
- [official documentation](https://agentskills.io/docs) for up-to-date information
11
- of support AI clients. Depending on the AI client you are using, you can install
12
- skills in different ways.
10
+ [official documentation](https://agentskills.io/home#adoption) for up-to-date
11
+ information of support AI clients. Depending on the AI client you are using, you
12
+ can install skills in different ways.
13
13
 
14
14
  ### Universal CLI (Recommended)
15
15
 
@@ -18,7 +18,9 @@ For **Cursor**, **VS Code**, **Claude Desktop**, **OpenCode**, **Goose**,
18
18
  and configure the Clix MCP Server automatically:
19
19
 
20
20
  ```bash
21
- npx @clix-so/clix-agent-skills install <skill-name> --client <your-client>
21
+ npx @clix-so/clix-agent-skills@latest install <skill-name> --client <your-client>
22
+ # For example, to install integration skill on Cursor:
23
+ # npx @clix-so/clix-agent-skills@latest install integration --client cursor
22
24
  ```
23
25
 
24
26
  **Supported Clients:**
@@ -55,7 +57,7 @@ Alternatively, you can install a single skill directly by running:
55
57
  ```bash
56
58
  /plugin install <plugin-name>@<marketplace-name>
57
59
  # For example
58
- /plugin install clix-integration@clix-agent-skills
60
+ # /plugin install clix-integration@clix-agent-skills
59
61
  ```
60
62
 
61
63
  Remember to restart Claude Code after installation to load the new skills.
@@ -81,10 +83,10 @@ incorrectly** due to the non-deterministic nature of AI.
81
83
 
82
84
  It is critical that you **carefully review and verify all actions** performed by
83
85
  these skills. While they are designed to be helpful, you remain responsible for
84
- checking their output before use. Use them with caution and supervision.
86
+ checking their output before use. Please use them with caution and supervision.
85
87
 
86
88
  ## License
87
89
 
88
90
  Each skill in this repository is governed by its own license. For specific terms
89
- and conditions, please consult the `LICENSE` file located within each skill's
90
- individual directory.
91
+ and conditions, please consult the `LICENSE.txt` file located within each
92
+ skill's individual directory.
package/dist/bin/cli.js CHANGED
@@ -7,22 +7,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
9
  const install_1 = require("./commands/install");
10
+ const package_json_1 = require("../../package.json");
11
+ /**
12
+ * Extracts error message from unknown error type
13
+ */
14
+ function getErrorMessage(error) {
15
+ if (error instanceof Error) {
16
+ return error.message;
17
+ }
18
+ return String(error);
19
+ }
10
20
  const program = new commander_1.Command();
11
21
  program
12
22
  .name("clix-agent-skills")
13
23
  .description("CLI to manage and install Clix Agent Skills")
14
- .version("1.0.0");
24
+ .version(package_json_1.version);
15
25
  program
16
26
  .command("install <skill>")
17
27
  .description("Install a specific agent skill")
18
- .option("-c, --client <client>", "Target AI client (cursor, claude, vscode, manual)")
28
+ .option("-c, --client <client>", "Target AI client (cursor, claude, vscode, amp, kiro, amazonq, codex, opencode, manual)")
19
29
  .option("-p, --path <path>", "Custom installation path (default: .clix/skills)")
20
30
  .action(async (skill, options) => {
21
31
  try {
22
32
  await (0, install_1.installSkill)(skill, options);
23
33
  }
24
34
  catch (error) {
25
- console.error(chalk_1.default.red("Error installing skill:"), error.message);
35
+ console.error(chalk_1.default.red("Error installing skill:"), getErrorMessage(error));
26
36
  process.exit(1);
27
37
  }
28
38
  });
@@ -9,6 +9,15 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  const ora_1 = __importDefault(require("ora"));
11
11
  const mcp_1 = require("../utils/mcp");
12
+ /**
13
+ * Extracts error message from unknown error type
14
+ */
15
+ function getErrorMessage(error) {
16
+ if (error instanceof Error) {
17
+ return error.message;
18
+ }
19
+ return String(error);
20
+ }
12
21
  async function installSkill(skillName, options) {
13
22
  const spinner = (0, ora_1.default)(`Installing skill: ${chalk_1.default.bold(skillName)}`).start();
14
23
  // 1. Locate Skill in current package
@@ -80,6 +89,12 @@ async function installSkill(skillName, options) {
80
89
  case "amp":
81
90
  relativeDest = ".amp/skills";
82
91
  break;
92
+ case "kiro":
93
+ relativeDest = ".kiro/skills";
94
+ break;
95
+ case "amazonq":
96
+ relativeDest = ".amazonq/skills";
97
+ break;
83
98
  default:
84
99
  relativeDest = options.client.startsWith(".") ? `${options.client}/skills` : `.clix/skills`;
85
100
  }
@@ -91,16 +106,16 @@ async function installSkill(skillName, options) {
91
106
  await fs_extra_1.default.copy(skillSourcePath, destPath);
92
107
  spinner.succeed(`Skill files installed to ${chalk_1.default.green(relativeDest + "/" + skillName)}`);
93
108
  }
94
- catch (err) {
95
- spinner.fail(`Failed to copy skill files: ${err.message}`);
96
- throw err;
109
+ catch (error) {
110
+ spinner.fail(`Failed to copy skill files: ${getErrorMessage(error)}`);
111
+ throw error;
97
112
  }
98
113
  // 4. MCP Configuration
99
114
  try {
100
115
  await (0, mcp_1.configureMCP)(options.client);
101
116
  }
102
- catch (err) {
103
- console.warn(chalk_1.default.yellow(`MCP Configuration warning: ${err.message}`));
117
+ catch (error) {
118
+ console.warn(chalk_1.default.yellow(`MCP Configuration warning: ${getErrorMessage(error)}`));
104
119
  }
105
120
  console.log(`\n${chalk_1.default.green("✔")} Skill ${chalk_1.default.bold(skillName)} is ready to use!`);
106
121
  console.log(` - Docs: ${path_1.default.join(destPath, "SKILL.md")}`);
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -9,12 +42,262 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
9
42
  const os_1 = __importDefault(require("os"));
10
43
  const chalk_1 = __importDefault(require("chalk"));
11
44
  const inquirer_1 = __importDefault(require("inquirer"));
12
- const CLIX_MCP_CONFIG = {
13
- "clix-mcp-server": {
14
- command: "npx",
15
- args: ["-y", "@clix-so/clix-mcp-server@latest"],
16
- },
45
+ const TOML = __importStar(require("@iarna/toml"));
46
+ // ============================================================================
47
+ // Constants
48
+ // ============================================================================
49
+ const CLIX_MCP_SERVER_ENTRY = {
50
+ command: "npx",
51
+ args: ["-y", "@clix-so/clix-mcp-server@latest"],
17
52
  };
53
+ // ============================================================================
54
+ // Helper Functions
55
+ // ============================================================================
56
+ /**
57
+ * Extracts error message from unknown error type
58
+ */
59
+ function getErrorMessage(error) {
60
+ if (error instanceof Error) {
61
+ return error.message;
62
+ }
63
+ return String(error);
64
+ }
65
+ /**
66
+ * Gets the MCP servers object from JSON config based on client type
67
+ */
68
+ function getMcpServersFromConfig(config, configKey) {
69
+ if (configKey === "amp.mcpServers") {
70
+ return config["amp.mcpServers"];
71
+ }
72
+ return config.mcpServers;
73
+ }
74
+ /**
75
+ * Sets the MCP servers object in JSON config based on client type
76
+ */
77
+ function setMcpServersInConfig(config, configKey, servers) {
78
+ if (configKey === "amp.mcpServers") {
79
+ config["amp.mcpServers"] = servers;
80
+ }
81
+ else {
82
+ config.mcpServers = servers;
83
+ }
84
+ }
85
+ /**
86
+ * Creates empty JSON config structure based on client type
87
+ */
88
+ function createEmptyConfig(configKey) {
89
+ if (configKey === "amp.mcpServers") {
90
+ return { "amp.mcpServers": {} };
91
+ }
92
+ return { mcpServers: {} };
93
+ }
94
+ /**
95
+ * Gets config path, key, and format for a specific client
96
+ */
97
+ function getClientConfig(client) {
98
+ const home = os_1.default.homedir();
99
+ switch (client.toLowerCase()) {
100
+ case "cursor": {
101
+ // Check for project-level definition first
102
+ const projectCursorPath = path_1.default.join(process.cwd(), ".cursor", "mcp.json");
103
+ if (fs_extra_1.default.existsSync(projectCursorPath)) {
104
+ return { path: projectCursorPath, configKey: "mcpServers", format: "json" };
105
+ }
106
+ // Fallback to global
107
+ return {
108
+ path: path_1.default.join(home, ".cursor", "mcp.json"),
109
+ configKey: "mcpServers",
110
+ format: "json",
111
+ };
112
+ }
113
+ case "claude": {
114
+ let configPath = null;
115
+ if (process.platform === "darwin") {
116
+ configPath = path_1.default.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
117
+ }
118
+ else if (process.platform === "win32") {
119
+ configPath = path_1.default.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
120
+ }
121
+ else if (process.platform === "linux") {
122
+ configPath = path_1.default.join(home, ".config", "Claude", "claude_desktop_config.json");
123
+ }
124
+ return configPath ? { path: configPath, configKey: "mcpServers", format: "json" } : null;
125
+ }
126
+ case "vscode":
127
+ return {
128
+ path: path_1.default.join(home, ".vscode", "mcp.json"),
129
+ configKey: "mcpServers",
130
+ format: "json",
131
+ };
132
+ case "amp": {
133
+ let ampConfigPath;
134
+ if (process.platform === "win32") {
135
+ ampConfigPath = path_1.default.join(process.env.USERPROFILE || home, ".config", "amp", "settings.json");
136
+ }
137
+ else {
138
+ ampConfigPath = path_1.default.join(home, ".config", "amp", "settings.json");
139
+ }
140
+ return { path: ampConfigPath, configKey: "amp.mcpServers", format: "json" };
141
+ }
142
+ case "kiro":
143
+ return {
144
+ path: path_1.default.join(process.cwd(), ".kiro", "settings", "mcp.json"),
145
+ configKey: "mcpServers",
146
+ format: "json",
147
+ };
148
+ case "amazonq":
149
+ return {
150
+ path: path_1.default.join(home, ".aws", "amazonq", "agents", "default.json"),
151
+ configKey: "mcpServers",
152
+ format: "json",
153
+ };
154
+ case "codex":
155
+ return {
156
+ path: path_1.default.join(home, ".codex", "config.toml"),
157
+ configKey: "mcp_servers",
158
+ format: "toml",
159
+ };
160
+ default:
161
+ return null;
162
+ }
163
+ }
164
+ // ============================================================================
165
+ // TOML Configuration (Codex)
166
+ // ============================================================================
167
+ async function configureCodexTOML(configPath) {
168
+ const nicePath = configPath.replace(os_1.default.homedir(), "~");
169
+ console.log(chalk_1.default.blue(`Checking MCP config at ${nicePath}...`));
170
+ let config = {};
171
+ if (!fs_extra_1.default.existsSync(configPath)) {
172
+ const { create } = await inquirer_1.default.prompt([
173
+ {
174
+ type: "confirm",
175
+ name: "create",
176
+ message: `Config file not found at ${nicePath}. Create it?`,
177
+ default: true,
178
+ },
179
+ ]);
180
+ if (!create) {
181
+ console.log(chalk_1.default.yellow("Skipping MCP configuration. You can configure manually later."));
182
+ return;
183
+ }
184
+ try {
185
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(configPath));
186
+ config = { mcp_servers: {} };
187
+ await fs_extra_1.default.writeFile(configPath, TOML.stringify(config), "utf-8");
188
+ console.log(chalk_1.default.green(`✔ Created config file at ${nicePath}`));
189
+ }
190
+ catch (error) {
191
+ console.log(chalk_1.default.red(`Failed to create config file: ${getErrorMessage(error)}`));
192
+ return;
193
+ }
194
+ }
195
+ else {
196
+ // Read existing TOML config
197
+ try {
198
+ const content = await fs_extra_1.default.readFile(configPath, "utf-8");
199
+ config = TOML.parse(content);
200
+ }
201
+ catch (error) {
202
+ console.log(chalk_1.default.red(`Failed to parse existing config TOML: ${getErrorMessage(error)}`));
203
+ return;
204
+ }
205
+ }
206
+ // Initialize mcp_servers if not present
207
+ if (!config.mcp_servers) {
208
+ config.mcp_servers = {};
209
+ }
210
+ // Check if already configured
211
+ if (config.mcp_servers["clix-mcp-server"]) {
212
+ console.log(chalk_1.default.green("✔ Clix MCP Server is already configured."));
213
+ return;
214
+ }
215
+ // Ask to inject
216
+ const { inject } = await inquirer_1.default.prompt([
217
+ {
218
+ type: "confirm",
219
+ name: "inject",
220
+ message: `Add Clix MCP Server to ${nicePath}?`,
221
+ default: true,
222
+ },
223
+ ]);
224
+ if (inject) {
225
+ config.mcp_servers["clix-mcp-server"] = CLIX_MCP_SERVER_ENTRY;
226
+ await fs_extra_1.default.writeFile(configPath, TOML.stringify(config), "utf-8");
227
+ console.log(chalk_1.default.green(`✔ Added Clix MCP Server to configuration. Please restart codex.`));
228
+ }
229
+ }
230
+ async function configureOpenCode() {
231
+ const configPath = path_1.default.join(process.cwd(), "opencode.json");
232
+ const nicePath = "opencode.json";
233
+ console.log(chalk_1.default.blue(`Checking MCP config at ${nicePath}...`));
234
+ let config = {};
235
+ if (!fs_extra_1.default.existsSync(configPath)) {
236
+ const { create } = await inquirer_1.default.prompt([
237
+ {
238
+ type: "confirm",
239
+ name: "create",
240
+ message: `Config file not found at ${nicePath}. Create it?`,
241
+ default: true,
242
+ },
243
+ ]);
244
+ if (!create) {
245
+ console.log(chalk_1.default.yellow("Skipping MCP configuration. You can configure manually later."));
246
+ return;
247
+ }
248
+ try {
249
+ config = {
250
+ $schema: "https://opencode.ai/config.json",
251
+ mcp: {},
252
+ };
253
+ await fs_extra_1.default.writeJSON(configPath, config, { spaces: 2 });
254
+ console.log(chalk_1.default.green(`✔ Created config file at ${nicePath}`));
255
+ }
256
+ catch (error) {
257
+ console.log(chalk_1.default.red(`Failed to create config file: ${getErrorMessage(error)}`));
258
+ return;
259
+ }
260
+ }
261
+ else {
262
+ try {
263
+ config = await fs_extra_1.default.readJSON(configPath);
264
+ }
265
+ catch (error) {
266
+ console.log(chalk_1.default.red(`Failed to parse existing config JSON: ${getErrorMessage(error)}`));
267
+ return;
268
+ }
269
+ }
270
+ // Initialize mcp if not present
271
+ if (!config.mcp) {
272
+ config.mcp = {};
273
+ }
274
+ // Check if already configured
275
+ if (config.mcp["clix-mcp-server"]) {
276
+ console.log(chalk_1.default.green("✔ Clix MCP Server is already configured."));
277
+ return;
278
+ }
279
+ // Ask to inject
280
+ const { inject } = await inquirer_1.default.prompt([
281
+ {
282
+ type: "confirm",
283
+ name: "inject",
284
+ message: `Add Clix MCP Server to ${nicePath}?`,
285
+ default: true,
286
+ },
287
+ ]);
288
+ if (inject) {
289
+ config.mcp["clix-mcp-server"] = {
290
+ type: "local",
291
+ command: ["npx", "-y", "@clix-so/clix-mcp-server@latest"],
292
+ enabled: true,
293
+ };
294
+ await fs_extra_1.default.writeJSON(configPath, config, { spaces: 2 });
295
+ console.log(chalk_1.default.green(`✔ Added Clix MCP Server to configuration. Please restart opencode.`));
296
+ }
297
+ }
298
+ // ============================================================================
299
+ // Main Function
300
+ // ============================================================================
18
301
  async function configureMCP(client) {
19
302
  let targetClient = client;
20
303
  if (!targetClient) {
@@ -27,12 +310,14 @@ async function configureMCP(client) {
27
310
  { name: "Cursor", value: "cursor" },
28
311
  { name: "Claude Desktop", value: "claude" },
29
312
  { name: "VS Code", value: "vscode" },
313
+ { name: "Amp", value: "amp" },
314
+ { name: "Kiro", value: "kiro" },
315
+ { name: "Amazon Q", value: "amazonq" },
30
316
  { name: "Codex", value: "codex" },
31
317
  { name: "OpenCode", value: "opencode" },
32
318
  { name: "Letta", value: "letta" },
33
319
  { name: "Goose", value: "goose" },
34
320
  { name: "GitHub", value: "github" },
35
- { name: "Amp", value: "amp" },
36
321
  { name: "None / Manual", value: "manual" },
37
322
  ],
38
323
  },
@@ -43,15 +328,27 @@ async function configureMCP(client) {
43
328
  console.log(chalk_1.default.blue("Skipping automatic MCP configuration."));
44
329
  return;
45
330
  }
46
- const configPath = getConfigPath(targetClient);
47
- if (!configPath) {
48
- console.log(chalk_1.default.yellow(`Could not determine config path for ${targetClient}. skipping.`));
331
+ // Handle OpenCode separately (different JSON structure)
332
+ if (targetClient === "opencode") {
333
+ await configureOpenCode();
49
334
  return;
50
335
  }
336
+ // Get client config
337
+ const clientConfig = getClientConfig(targetClient);
338
+ if (!clientConfig) {
339
+ console.log(chalk_1.default.yellow(`Could not determine config path for ${targetClient}. Skipping.`));
340
+ return;
341
+ }
342
+ // Handle TOML format (Codex)
343
+ if (clientConfig.format === "toml") {
344
+ await configureCodexTOML(clientConfig.path);
345
+ return;
346
+ }
347
+ // Handle JSON format (all other clients)
348
+ const { path: configPath, configKey } = clientConfig;
51
349
  const nicePath = configPath.replace(os_1.default.homedir(), "~");
52
350
  console.log(chalk_1.default.blue(`Checking MCP config at ${nicePath}...`));
53
351
  if (!fs_extra_1.default.existsSync(configPath)) {
54
- // For now, confirm before creating new file
55
352
  const { create } = await inquirer_1.default.prompt([
56
353
  {
57
354
  type: "confirm",
@@ -66,26 +363,30 @@ async function configureMCP(client) {
66
363
  }
67
364
  try {
68
365
  await fs_extra_1.default.ensureDir(path_1.default.dirname(configPath));
69
- await fs_extra_1.default.writeJSON(configPath, { mcpServers: {} }, { spaces: 2 });
366
+ await fs_extra_1.default.writeJSON(configPath, createEmptyConfig(configKey), { spaces: 2 });
70
367
  console.log(chalk_1.default.green(`✔ Created config file at ${nicePath}`));
71
368
  }
72
369
  catch (error) {
73
- console.log(chalk_1.default.red(`Failed to create config file: ${error.message}`));
370
+ console.log(chalk_1.default.red(`Failed to create config file: ${getErrorMessage(error)}`));
74
371
  return;
75
372
  }
76
373
  }
77
374
  // Read config
78
- let config = {};
375
+ let config;
79
376
  try {
80
377
  config = await fs_extra_1.default.readJSON(configPath);
81
378
  }
82
- catch (e) {
83
- console.log(chalk_1.default.red("Failed to parse existing config JSON."));
379
+ catch (error) {
380
+ console.log(chalk_1.default.red(`Failed to parse existing config JSON: ${getErrorMessage(error)}`));
84
381
  return;
85
382
  }
86
- if (!config.mcpServers)
87
- config.mcpServers = {};
88
- if (config.mcpServers["clix-mcp-server"]) {
383
+ // Get or create mcpServers object
384
+ let mcpServers = getMcpServersFromConfig(config, configKey);
385
+ if (!mcpServers) {
386
+ mcpServers = {};
387
+ setMcpServersInConfig(config, configKey, mcpServers);
388
+ }
389
+ if (mcpServers["clix-mcp-server"]) {
89
390
  console.log(chalk_1.default.green("✔ Clix MCP Server is already configured."));
90
391
  return;
91
392
  }
@@ -99,33 +400,9 @@ async function configureMCP(client) {
99
400
  },
100
401
  ]);
101
402
  if (inject) {
102
- config.mcpServers["clix-mcp-server"] = CLIX_MCP_CONFIG["clix-mcp-server"];
403
+ mcpServers["clix-mcp-server"] = CLIX_MCP_SERVER_ENTRY;
404
+ setMcpServersInConfig(config, configKey, mcpServers);
103
405
  await fs_extra_1.default.writeJSON(configPath, config, { spaces: 2 });
104
406
  console.log(chalk_1.default.green(`✔ Added Clix MCP Server to configuration. Please restart ${targetClient}.`));
105
407
  }
106
408
  }
107
- function getConfigPath(client) {
108
- const home = os_1.default.homedir();
109
- switch (client.toLowerCase()) {
110
- case "cursor":
111
- // Check for project-level definition first
112
- const projectCursorPath = path_1.default.join(process.cwd(), ".cursor", "mcp.json");
113
- if (fs_extra_1.default.existsSync(projectCursorPath)) {
114
- return projectCursorPath;
115
- }
116
- // Fallback to global
117
- return path_1.default.join(home, ".cursor", "mcp.json");
118
- case "claude":
119
- if (process.platform === "darwin") {
120
- return path_1.default.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
121
- }
122
- else if (process.platform === "win32") {
123
- return path_1.default.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
124
- }
125
- return null;
126
- case "vscode":
127
- return path_1.default.join(home, ".vscode", "mcp.json"); // Standard VS Code MCP path assumption
128
- default:
129
- return null;
130
- }
131
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clix-so/clix-agent-skills",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "An open collection of agent skills for Clix.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -16,7 +16,11 @@
16
16
  "dev": "ts-node src/bin/cli.ts",
17
17
  "test": "jest",
18
18
  "lint": "prettier --check .",
19
- "lint:fix": "prettier --write ."
19
+ "lint:fix": "prettier --write .",
20
+ "sync-version": "ts-node scripts/sync-version.ts",
21
+ "preversion": "npm run sync-version",
22
+ "postversion": "npm run sync-version",
23
+ "prepublishOnly": "npm run sync-version && npm run build && npm test"
20
24
  },
21
25
  "keywords": [
22
26
  "clix",
@@ -32,6 +36,7 @@
32
36
  "url": "https://github.com/clix-so/skills"
33
37
  },
34
38
  "dependencies": {
39
+ "@iarna/toml": "^2.2.5",
35
40
  "chalk": "^4.1.2",
36
41
  "commander": "^14.0.2",
37
42
  "fs-extra": "^11.3.3",
@@ -40,6 +45,7 @@
40
45
  },
41
46
  "devDependencies": {
42
47
  "@types/fs-extra": "^11.0.4",
48
+ "@types/iarna__toml": "^2.0.5",
43
49
  "@types/inquirer": "^9.0.9",
44
50
  "@types/jest": "^30.0.0",
45
51
  "@types/node": "^25.0.3",
@@ -5,7 +5,6 @@ description:
5
5
  projects. Provides step-by-step guidance for installation, initialization, and
6
6
  verification. Use when the user asks to install, setup, or configure Clix
7
7
  analytics.
8
- version: 0.1.0
9
8
  ---
10
9
 
11
10
  # Clix SDK Integration Skill
@@ -42,7 +41,8 @@ latest verified SDK source code.
42
41
  - Run: `bash scripts/install-mcp.sh`
43
42
  - The script will:
44
43
  - Verify the package is available
45
- - Auto-detect the MCP client (Codex, Cursor, Claude Desktop, VS Code, etc.)
44
+ - Auto-detect the MCP client (OpenCode, Amp, Codex, Cursor, Claude
45
+ Desktop, VS Code, etc.)
46
46
  - Automatically configure the MCP server in the appropriate config file
47
47
  - Provide clear instructions for restart
48
48
  - Instruct user to restart their agent/IDE to load the new server.
@@ -55,8 +55,8 @@ latest verified SDK source code.
55
55
 
56
56
  ## Interaction Guidelines for Agents
57
57
 
58
- When using this skill (for example inside Claude Code, Codex, Cursor, or other
59
- AI IDEs), follow these behaviors:
58
+ When using this skill (for example inside OpenCode, Amp, Claude Code, Codex,
59
+ Cursor, or other AI IDEs), follow these behaviors:
60
60
 
61
61
  - **Start with reconnaissance**
62
62
  - Inspect the project structure first (list key files like `package.json`,
@@ -123,6 +123,9 @@ Examine the codebase structure to determine platform:
123
123
  - **Other**: If it’s not one of the above, stop and ask the user—this skill is
124
124
  mobile-only.
125
125
 
126
+ **Priority rule (important):** If React Native or Flutter is detected, treat it
127
+ as the primary platform even if native `ios/` and `android/` folders exist.
128
+
126
129
  **Step 2.2: Verify Detection**
127
130
 
128
131
  Confirm platform detection with user if ambiguous:
@@ -238,6 +241,42 @@ Run validation checks:
238
241
  - Verify environment variables are accessible
239
242
  - Verify SDK is properly imported and initialized
240
243
 
244
+ ### Platform Verification Checklists (UI Steps → Repo Verification)
245
+
246
+ Use these checklists to verify manual UI steps were actually completed.
247
+
248
+ #### iOS Verification
249
+
250
+ - **Dependencies present**: `Podfile`/`Podfile.lock` or SwiftPM/Xcode references
251
+ - **Entitlements present**: an `*.entitlements` file exists and is wired to the
252
+ correct target(s)
253
+ - **Capabilities configured**: `project.pbxproj` reflects required capabilities
254
+ (as applicable to Push Notifications / Background Modes)
255
+
256
+ #### Android Verification
257
+
258
+ - **Dependencies present**: module-level `build.gradle` / `build.gradle.kts`
259
+ includes required SDK dependencies
260
+ - **Manifest updated**: `AndroidManifest.xml` contains required permissions,
261
+ services/receivers (as required by the SDK)
262
+ - **Firebase config placed** (if used): `google-services.json` exists in the
263
+ expected module directory (commonly `app/`)
264
+
265
+ #### React Native Verification
266
+
267
+ - **JS dependency**: `package.json` includes the Clix package
268
+ - **iOS native**: `ios/Podfile.lock` updated after `pod install` (when required)
269
+ - **Android native**: Gradle + Manifest updates present if required by the SDK
270
+ - **Initialization**: root entrypoint initializes the SDK exactly once
271
+
272
+ #### Flutter Verification
273
+
274
+ - **Pub dependency**: `pubspec.yaml` includes the required packages and
275
+ `pubspec.lock` is updated after `flutter pub get`
276
+ - **iOS native**: `ios/Podfile.lock` updated after `pod install` (when required)
277
+ - **Android native**: Gradle + Manifest updates present if required
278
+ - **Initialization**: SDK initialized before `runApp`
279
+
241
280
  **Step 5.3: Documentation**
242
281
 
243
282
  Create or update documentation:
@@ -388,6 +427,20 @@ Future<void> main() async {
388
427
  6. **Document changes** - Update README and configuration files
389
428
  7. **Version control** - Add `.env` to `.gitignore`, commit `.env.example`
390
429
 
430
+ ## Docs Usage Note (MCP vs Static vs Web Docs)
431
+
432
+ - If MCP tools are available: treat `search_sdk` results as the source of truth
433
+ for initialization/API usage.
434
+ - If MCP tools are unavailable: you may reference the official quickstarts below
435
+ for **manual steps**, and use `examples/` + `references/` for code patterns.
436
+
437
+ **Official quickstarts:**
438
+
439
+ - iOS: `https://docs.clix.so/sdk-quickstart-ios`
440
+ - Android: `https://docs.clix.so/sdk-quickstart-android`
441
+ - React Native: `https://docs.clix.so/sdk-quickstart-react-native`
442
+ - Flutter: `https://docs.clix.so/sdk-quickstart-flutter`
443
+
391
444
  ## Progressive Disclosure
392
445
 
393
446
  - **Level 1**: This SKILL.md file (always loaded)
@@ -282,3 +282,65 @@ claude mcp add --transport stdio clix-mcp-server -- npx -y @clix-so/clix-mcp-ser
282
282
  }
283
283
  }
284
284
  ```
285
+
286
+ #### Amp
287
+
288
+ **Setup**
289
+
290
+ Amp uses `amp.mcpServers` in VS Code settings files. Configure in either:
291
+
292
+ - **Workspace settings**: `.vscode/settings.json` (recommended for
293
+ project-specific setup)
294
+ - **User settings**: `~/.vscode/settings.json` (for global setup)
295
+
296
+ 1. Open `.vscode/settings.json` (create if it doesn't exist).
297
+ 2. Add the configuration:
298
+
299
+ ```json
300
+ {
301
+ "amp.mcpServers": {
302
+ "clix-mcp-server": {
303
+ "command": "npx",
304
+ "args": ["-y", "@clix-so/clix-mcp-server@latest"]
305
+ }
306
+ }
307
+ }
308
+ ```
309
+
310
+ 3. Restart Amp or reload the VS Code window.
311
+
312
+ **Note**: The server name `clix-mcp-server` ensures tools appear as
313
+ `clix-mcp-server:*` (e.g., `clix-mcp-server:search_sdk`,
314
+ `clix-mcp-server:search_docs`). See
315
+ [Amp MCP documentation](https://ampcode.com/manual#mcp) for more details.
316
+
317
+ #### OpenCode
318
+
319
+ **Setup**
320
+
321
+ OpenCode uses `opencode.json` or `opencode.jsonc` files in your project root.
322
+ See [OpenCode MCP documentation](https://opencode.ai/docs/mcp-servers/) for
323
+ details.
324
+
325
+ 1. Open `opencode.json` or `opencode.jsonc` (create if it doesn't exist).
326
+ 2. Add the configuration:
327
+
328
+ ```json
329
+ {
330
+ "$schema": "https://opencode.ai/config.json",
331
+ "mcp": {
332
+ "clix-mcp-server": {
333
+ "type": "local",
334
+ "command": ["npx", "-y", "@clix-so/clix-mcp-server@latest"],
335
+ "enabled": true
336
+ }
337
+ }
338
+ }
339
+ ```
340
+
341
+ 3. Restart OpenCode or reload the configuration.
342
+
343
+ **Note**: The server name `clix-mcp-server` ensures tools appear as
344
+ `clix-mcp-server:*` (e.g., `clix-mcp-server:search_sdk`,
345
+ `clix-mcp-server:search_docs`). You can reference the server in prompts with
346
+ `use clix-mcp-server` or add it to your `AGENTS.md` file.
@@ -58,6 +58,30 @@ get_config_path() {
58
58
  vscode)
59
59
  echo "${home}/.vscode/mcp.json"
60
60
  ;;
61
+ amp)
62
+ # Amp uses VS Code settings.json format
63
+ # Check workspace settings first, then user settings
64
+ if [ -f ".vscode/settings.json" ]; then
65
+ echo ".vscode/settings.json"
66
+ elif [ -f "${home}/.vscode/settings.json" ]; then
67
+ echo "${home}/.vscode/settings.json"
68
+ else
69
+ # Default to workspace settings
70
+ echo ".vscode/settings.json"
71
+ fi
72
+ ;;
73
+ opencode)
74
+ # OpenCode uses opencode.json or opencode.jsonc in project root
75
+ # Check for .jsonc first (preferred), then .json
76
+ if [ -f "opencode.jsonc" ]; then
77
+ echo "opencode.jsonc"
78
+ elif [ -f "opencode.json" ]; then
79
+ echo "opencode.json"
80
+ else
81
+ # Default to .jsonc
82
+ echo "opencode.jsonc"
83
+ fi
84
+ ;;
61
85
  *)
62
86
  echo ""
63
87
  ;;
@@ -116,6 +140,112 @@ EOF
116
140
  log "${GREEN}✔ Configured Clix MCP Server in Codex config${RESET}"
117
141
  }
118
142
 
143
+ # Configure MCP for Amp (uses amp.mcpServers in VS Code settings.json)
144
+ configure_amp() {
145
+ local config_path="$1"
146
+ local config_dir=$(dirname "$config_path")
147
+
148
+ mkdir -p "$config_dir"
149
+
150
+ if [ ! -f "$config_path" ]; then
151
+ echo '{}' > "$config_path"
152
+ log "${GREEN}✔ Created Amp settings file${RESET}"
153
+ fi
154
+
155
+ # Check if already configured
156
+ if grep -q "clix-mcp-server" "$config_path" 2>/dev/null; then
157
+ log "${GREEN}✔ Clix MCP Server already configured in Amp${RESET}"
158
+ return 0
159
+ fi
160
+
161
+ # Use node to safely update JSON
162
+ if command -v node &> /dev/null; then
163
+ node <<EOF
164
+ const fs = require('fs');
165
+ const path = '$config_path';
166
+ let config = {};
167
+ try {
168
+ const content = fs.readFileSync(path, 'utf8');
169
+ config = JSON.parse(content);
170
+ } catch (e) {
171
+ config = {};
172
+ }
173
+ if (!config['amp.mcpServers']) config['amp.mcpServers'] = {};
174
+ config['amp.mcpServers']['clix-mcp-server'] = {
175
+ command: 'npx',
176
+ args: ['-y', '@clix-so/clix-mcp-server@latest']
177
+ };
178
+ fs.writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
179
+ EOF
180
+ log "${GREEN}✔ Configured Clix MCP Server in Amp${RESET}"
181
+ else
182
+ log "${YELLOW}⚠️ Node.js not found. Please manually add to $config_path:${RESET}"
183
+ log "${BLUE}{${RESET}"
184
+ log "${BLUE} \"amp.mcpServers\": {${RESET}"
185
+ log "${BLUE} \"clix-mcp-server\": {${RESET}"
186
+ log "${BLUE} \"command\": \"npx\",${RESET}"
187
+ log "${BLUE} \"args\": [\"-y\", \"@clix-so/clix-mcp-server@latest\"]${RESET}"
188
+ log "${BLUE} }${RESET}"
189
+ log "${BLUE} }${RESET}"
190
+ log "${BLUE}}${RESET}"
191
+ fi
192
+ }
193
+
194
+ # Configure MCP for OpenCode (uses opencode.json/jsonc with mcp section)
195
+ configure_opencode() {
196
+ local config_path="$1"
197
+
198
+ if [ ! -f "$config_path" ]; then
199
+ # Create new opencode.jsonc file
200
+ cat > "$config_path" <<'EOF'
201
+ {
202
+ "$schema": "https://opencode.ai/config.json",
203
+ "mcp": {}
204
+ }
205
+ EOF
206
+ log "${GREEN}✔ Created OpenCode config file${RESET}"
207
+ fi
208
+
209
+ # Check if already configured
210
+ if grep -q "clix-mcp-server" "$config_path" 2>/dev/null; then
211
+ log "${GREEN}✔ Clix MCP Server already configured in OpenCode${RESET}"
212
+ return 0
213
+ fi
214
+
215
+ # Use node to safely update JSON/JSONC
216
+ if command -v node &> /dev/null; then
217
+ node <<EOF
218
+ const fs = require('fs');
219
+ const path = '$config_path';
220
+ let content = fs.readFileSync(path, 'utf8');
221
+ // Remove comments for JSONC parsing (simple approach)
222
+ let jsonContent = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
223
+ let config = JSON.parse(jsonContent);
224
+ if (!config.mcp) config.mcp = {};
225
+ config.mcp['clix-mcp-server'] = {
226
+ type: 'local',
227
+ command: ['npx', '-y', '@clix-so/clix-mcp-server@latest'],
228
+ enabled: true
229
+ };
230
+ // Write back as JSONC if original was .jsonc, otherwise JSON
231
+ const isJsonc = path.endsWith('.jsonc');
232
+ fs.writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
233
+ EOF
234
+ log "${GREEN}✔ Configured Clix MCP Server in OpenCode${RESET}"
235
+ else
236
+ log "${YELLOW}⚠️ Node.js not found. Please manually add to $config_path:${RESET}"
237
+ log "${BLUE}{${RESET}"
238
+ log "${BLUE} \"mcp\": {${RESET}"
239
+ log "${BLUE} \"clix-mcp-server\": {${RESET}"
240
+ log "${BLUE} \"type\": \"local\",${RESET}"
241
+ log "${BLUE} \"command\": [\"npx\", \"-y\", \"@clix-so/clix-mcp-server@latest\"],${RESET}"
242
+ log "${BLUE} \"enabled\": true${RESET}"
243
+ log "${BLUE} }${RESET}"
244
+ log "${BLUE} }${RESET}"
245
+ log "${BLUE}}${RESET}"
246
+ fi
247
+ }
248
+
119
249
  # Configure MCP for JSON-based clients
120
250
  configure_json_client() {
121
251
  local config_path="$1"
@@ -163,6 +293,20 @@ EOF
163
293
 
164
294
  # Auto-detect client
165
295
  detect_client() {
296
+ # Check for OpenCode (check for opencode.json/jsonc or opencode command)
297
+ if [ -f "opencode.json" ] || [ -f "opencode.jsonc" ] || command -v opencode &> /dev/null; then
298
+ echo "opencode"
299
+ return
300
+ fi
301
+
302
+ # Check for Amp (check for amp.mcpServers in settings.json or amp command)
303
+ if command -v amp &> /dev/null || \
304
+ grep -q "amp.mcpServers" ".vscode/settings.json" 2>/dev/null || \
305
+ grep -q "amp.mcpServers" "${HOME}/.vscode/settings.json" 2>/dev/null; then
306
+ echo "amp"
307
+ return
308
+ fi
309
+
166
310
  # Check for Codex
167
311
  if [ -f "${HOME}/.codex/config.toml" ] || command -v codex &> /dev/null; then
168
312
  echo "codex"
@@ -222,6 +366,10 @@ if [ "$detected_client" != "unknown" ]; then
222
366
 
223
367
  if [ "$detected_client" = "codex" ]; then
224
368
  configure_codex "$config_path"
369
+ elif [ "$detected_client" = "amp" ]; then
370
+ configure_amp "$config_path"
371
+ elif [ "$detected_client" = "opencode" ]; then
372
+ configure_opencode "$config_path"
225
373
  else
226
374
  configure_json_client "$config_path"
227
375
  fi
@@ -235,6 +383,8 @@ if [ "$detected_client" != "unknown" ]; then
235
383
  else
236
384
  log "${YELLOW}⚠️ Could not auto-detect MCP client.${RESET}"
237
385
  log "${BLUE}The package is ready to use. Configure manually:${RESET}"
386
+ log "${BLUE} - OpenCode: Add to opencode.json or opencode.jsonc${RESET}"
387
+ log "${BLUE} - Amp: Add to .vscode/settings.json or ~/.vscode/settings.json${RESET}"
238
388
  log "${BLUE} - Codex: Add to ~/.codex/config.toml${RESET}"
239
389
  log "${BLUE} - Cursor: Add to .cursor/mcp.json or ~/.cursor/mcp.json${RESET}"
240
390
  log "${BLUE} - Claude Desktop: Add to config file${RESET}"