clishop 0.3.0 → 1.0.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
@@ -16,6 +16,7 @@
16
16
  ## Works with
17
17
 
18
18
  <p>
19
+ <img alt="VS Code" src="https://img.shields.io/badge/VS%20Code-Copilot-007ACC?style=for-the-badge&logo=visualstudiocode&logoColor=white" />
19
20
  <img alt="Claude" src="https://img.shields.io/badge/Claude-Supported-7C3AED?style=for-the-badge&logo=anthropic&logoColor=white" />
20
21
  <img alt="GPT" src="https://img.shields.io/badge/GPT-Supported-10A37F?style=for-the-badge&logo=openai&logoColor=white" />
21
22
  <img alt="Gemini" src="https://img.shields.io/badge/Gemini-Supported-4285F4?style=for-the-badge&logo=googlegemini&logoColor=white" />
@@ -32,15 +33,56 @@ Built for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) e
32
33
 
33
34
  ## MCP Server
34
35
 
35
- CLISHOP ships as a native MCP server. Every Claude Desktop, Cursor, Windsurf, or other MCP-compatible client gets access to 19 shopping tools out of the box.
36
+ CLISHOP ships as a native MCP server. VS Code (GitHub Copilot), Claude Desktop, Cursor, Windsurf, and any MCP-compatible client gets access to 19 shopping tools out of the box.
36
37
 
37
- ### Claude Desktop
38
+ Install globally and run the dedicated binary:
38
39
 
39
- Add to `claude_desktop_config.json`:
40
+ ```bash
41
+ npm install -g clishop
42
+ clishop-mcp
43
+ ```
44
+
45
+ Or run without installing:
46
+
47
+ ```bash
48
+ npx -y clishop --mcp
49
+ ```
50
+
51
+ ### VS Code (GitHub Copilot)
52
+
53
+ Add to `.vscode/mcp.json` in your workspace:
40
54
 
41
55
  ```json
42
56
  {
43
- "mcpServers": {
57
+ "servers": {
58
+ "clishop": {
59
+ "command": "clishop-mcp",
60
+ "args": []
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ Or add to your VS Code `settings.json`:
67
+
68
+ ```json
69
+ {
70
+ "mcp": {
71
+ "servers": {
72
+ "clishop": {
73
+ "command": "clishop-mcp",
74
+ "args": []
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ If you don't have clishop installed globally, use npx instead:
82
+
83
+ ```json
84
+ {
85
+ "servers": {
44
86
  "clishop": {
45
87
  "command": "npx",
46
88
  "args": ["-y", "clishop", "--mcp"]
@@ -49,7 +91,9 @@ Add to `claude_desktop_config.json`:
49
91
  }
50
92
  ```
51
93
 
52
- Or if installed globally:
94
+ ### Claude Desktop
95
+
96
+ Add to `claude_desktop_config.json`:
53
97
 
54
98
  ```json
55
99
  {
@@ -61,6 +105,19 @@ Or if installed globally:
61
105
  }
62
106
  ```
63
107
 
108
+ Or if not installed globally:
109
+
110
+ ```json
111
+ {
112
+ "mcpServers": {
113
+ "clishop": {
114
+ "command": "npx",
115
+ "args": ["-y", "clishop", "--mcp"]
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
64
121
  ### Cursor
65
122
 
66
123
  Add to `.cursor/mcp.json`:
@@ -69,8 +126,7 @@ Add to `.cursor/mcp.json`:
69
126
  {
70
127
  "mcpServers": {
71
128
  "clishop": {
72
- "command": "npx",
73
- "args": ["-y", "clishop", "--mcp"]
129
+ "command": "clishop-mcp"
74
130
  }
75
131
  }
76
132
  }
@@ -84,8 +140,7 @@ Add to `~/.windsurf/mcp.json`:
84
140
  {
85
141
  "mcpServers": {
86
142
  "clishop": {
87
- "command": "npx",
88
- "args": ["-y", "clishop", "--mcp"]
143
+ "command": "clishop-mcp"
89
144
  }
90
145
  }
91
146
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getApiBaseUrl
3
- } from "./chunk-ZF3JIQRK.js";
3
+ } from "./chunk-X3H7SYR4.js";
4
4
 
5
5
  // src/auth.ts
6
6
  import keytar from "keytar";
@@ -155,6 +155,22 @@ function getApiClient() {
155
155
  );
156
156
  return client;
157
157
  }
158
+ async function ensureAgentOnBackend(agentName, maxOrderAmountInCents, requireConfirmation = true) {
159
+ const api = getApiClient();
160
+ try {
161
+ const res = await api.get("/agents");
162
+ const agents = res.data.agents || [];
163
+ const exists = agents.some((a) => a.name === agentName);
164
+ if (!exists) {
165
+ await api.post("/agents", {
166
+ name: agentName,
167
+ maxOrderAmountInCents: maxOrderAmountInCents || void 0,
168
+ requireConfirmation
169
+ });
170
+ }
171
+ } catch {
172
+ }
173
+ }
158
174
  function handleApiError(error) {
159
175
  if (axios2.isAxiosError(error)) {
160
176
  const data = error.response?.data;
@@ -188,5 +204,6 @@ export {
188
204
  register,
189
205
  logout,
190
206
  getApiClient,
207
+ ensureAgentOnBackend,
191
208
  handleApiError
192
209
  };
@@ -92,6 +92,18 @@ function deleteAgent(name) {
92
92
  function listAgents() {
93
93
  return Object.values(config.store.agents);
94
94
  }
95
+ function resetAgents() {
96
+ config.set("agents", {
97
+ default: {
98
+ name: "default",
99
+ requireConfirmation: true,
100
+ maxOrderAmount: 500,
101
+ allowedCategories: [],
102
+ blockedCategories: []
103
+ }
104
+ });
105
+ config.set("activeAgent", "default");
106
+ }
95
107
 
96
108
  export {
97
109
  __export,
@@ -104,5 +116,6 @@ export {
104
116
  createAgent,
105
117
  updateAgent,
106
118
  deleteAgent,
107
- listAgents
119
+ listAgents,
120
+ resetAgents
108
121
  };
@@ -7,9 +7,10 @@ import {
7
7
  getApiBaseUrl,
8
8
  getConfig,
9
9
  listAgents,
10
+ resetAgents,
10
11
  setActiveAgent,
11
12
  updateAgent
12
- } from "./chunk-ZF3JIQRK.js";
13
+ } from "./chunk-X3H7SYR4.js";
13
14
  export {
14
15
  DEFAULT_API_BASE_URL,
15
16
  createAgent,
@@ -19,6 +20,7 @@ export {
19
20
  getApiBaseUrl,
20
21
  getConfig,
21
22
  listAgents,
23
+ resetAgents,
22
24
  setActiveAgent,
23
25
  updateAgent
24
26
  };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ ensureAgentOnBackend,
3
4
  getApiClient,
4
5
  getUserInfo,
5
6
  handleApiError,
@@ -7,7 +8,7 @@ import {
7
8
  login,
8
9
  logout,
9
10
  register
10
- } from "./chunk-RHBOI27D.js";
11
+ } from "./chunk-CVK6G342.js";
11
12
  import {
12
13
  createAgent,
13
14
  deleteAgent,
@@ -17,7 +18,7 @@ import {
17
18
  listAgents,
18
19
  setActiveAgent,
19
20
  updateAgent
20
- } from "./chunk-ZF3JIQRK.js";
21
+ } from "./chunk-X3H7SYR4.js";
21
22
 
22
23
  // src/index.ts
23
24
  import { Command } from "commander";
@@ -43,12 +44,12 @@ function registerAuthCommands(program2) {
43
44
  return;
44
45
  }
45
46
  if (await isLoggedIn()) {
46
- const user2 = await getUserInfo();
47
+ const user = await getUserInfo();
47
48
  const { confirm } = await inquirer.prompt([
48
49
  {
49
50
  type: "confirm",
50
51
  name: "confirm",
51
- message: `You are already logged in as ${chalk.cyan(user2?.email || "unknown")}. Log in as a different user?`,
52
+ message: `You are already logged in as ${chalk.cyan(user?.email || "unknown")}. Log in as a different user?`,
52
53
  default: false
53
54
  }
54
55
  ]);
@@ -77,8 +78,14 @@ function registerAuthCommands(program2) {
77
78
  password = password || answers.password;
78
79
  }
79
80
  const spinner = ora("Logging in...").start();
80
- const user = await login(email, password);
81
- spinner.succeed(chalk.green(`Logged in as ${chalk.bold(user.name)} (${user.email})`));
81
+ try {
82
+ const user = await login(email, password);
83
+ spinner.succeed(chalk.green(`Logged in as ${chalk.bold(user.name)} (${user.email})`));
84
+ } catch (loginErr) {
85
+ const msg = loginErr?.response?.data?.message || loginErr.message;
86
+ spinner.fail(chalk.red(`Login failed: ${msg}`));
87
+ process.exitCode = 1;
88
+ }
82
89
  } catch (error) {
83
90
  const msg = error?.response?.data?.message || error.message;
84
91
  console.error(chalk.red(`
@@ -110,8 +117,14 @@ function registerAuthCommands(program2) {
110
117
  return;
111
118
  }
112
119
  const spinner = ora("Creating account...").start();
113
- const user = await register(answers.email, answers.password, answers.name);
114
- spinner.succeed(chalk.green(`Account created! Welcome, ${chalk.bold(user.name)}.`));
120
+ try {
121
+ const user = await register(answers.email, answers.password, answers.name);
122
+ spinner.succeed(chalk.green(`Account created! Welcome, ${chalk.bold(user.name)}.`));
123
+ } catch (regErr) {
124
+ const msg = regErr?.response?.data?.message || regErr.message;
125
+ spinner.fail(chalk.red(`Registration failed: ${msg}`));
126
+ process.exitCode = 1;
127
+ }
115
128
  } catch (error) {
116
129
  const msg = error?.response?.data?.message || error.message;
117
130
  console.error(chalk.red(`
@@ -122,7 +135,19 @@ function registerAuthCommands(program2) {
122
135
  program2.command("logout").description("Log out of your CLISHOP account").action(async () => {
123
136
  const spinner = ora("Logging out...").start();
124
137
  await logout();
125
- spinner.succeed(chalk.green("Logged out."));
138
+ const config = getConfig();
139
+ config.set("agents", {
140
+ default: {
141
+ name: "default",
142
+ requireConfirmation: true,
143
+ maxOrderAmount: 500,
144
+ allowedCategories: [],
145
+ blockedCategories: []
146
+ }
147
+ });
148
+ config.set("activeAgent", "default");
149
+ config.set("setupCompleted", false);
150
+ spinner.succeed(chalk.green("Logged out. Local config reset."));
126
151
  });
127
152
  program2.command("whoami").description("Show the currently logged-in user").action(async () => {
128
153
  if (!await isLoggedIn()) {
@@ -377,6 +402,7 @@ function registerAddressCommands(program2) {
377
402
  address.command("list").alias("ls").description("List all addresses for the active agent").action(async () => {
378
403
  try {
379
404
  const agent = getActiveAgent();
405
+ await ensureAgentOnBackend(agent.name);
380
406
  const spinner = ora2("Fetching addresses...").start();
381
407
  const api = getApiClient();
382
408
  const res = await api.get("/addresses", {
@@ -403,7 +429,6 @@ Addresses for agent "${agent.name}":
403
429
  console.log(` ${addr.country}`);
404
430
  if (addr.recipientPhone) console.log(` ${chalk3.dim("Phone:")} ${addr.recipientPhone}`);
405
431
  if (addr.vatNumber) console.log(` ${chalk3.dim("VAT:")} ${addr.vatNumber}`);
406
- if (addr.taxId) console.log(` ${chalk3.dim("Tax ID:")} ${addr.taxId}`);
407
432
  if (addr.instructions) console.log(` ${chalk3.dim("Instructions:")} ${addr.instructions}`);
408
433
  console.log();
409
434
  }
@@ -416,7 +441,12 @@ Addresses for agent "${agent.name}":
416
441
  const agent = getActiveAgent();
417
442
  const answers = await inquirer3.prompt([
418
443
  { type: "input", name: "label", message: "Label (e.g. Home, Office):" },
419
- { type: "input", name: "recipientName", message: "Recipient name (optional):" },
444
+ {
445
+ type: "input",
446
+ name: "recipientName",
447
+ message: "Recipient name:",
448
+ validate: (v) => v.trim() ? true : "Recipient name is required"
449
+ },
420
450
  { type: "input", name: "recipientPhone", message: "Recipient phone (optional):" },
421
451
  {
422
452
  type: "input",
@@ -452,7 +482,7 @@ Addresses for agent "${agent.name}":
452
482
  default: false
453
483
  }
454
484
  ]);
455
- let companyAnswers = { companyName: "", vatNumber: "", taxId: "" };
485
+ let companyAnswers = { companyName: "", vatNumber: "" };
456
486
  if (answers.isCompany) {
457
487
  companyAnswers = await inquirer3.prompt([
458
488
  {
@@ -461,8 +491,7 @@ Addresses for agent "${agent.name}":
461
491
  message: "Company name:",
462
492
  validate: (v) => v.trim() ? true : "Required for company addresses"
463
493
  },
464
- { type: "input", name: "vatNumber", message: "VAT number (optional):" },
465
- { type: "input", name: "taxId", message: "Tax ID / EIN (optional):" }
494
+ { type: "input", name: "vatNumber", message: "VAT number (optional):" }
466
495
  ]);
467
496
  }
468
497
  const { setDefault } = await inquirer3.prompt([
@@ -474,6 +503,7 @@ Addresses for agent "${agent.name}":
474
503
  }
475
504
  ]);
476
505
  const spinner = ora2("Saving address...").start();
506
+ await ensureAgentOnBackend(agent.name);
477
507
  const api = getApiClient();
478
508
  const res = await api.post("/addresses", {
479
509
  agent: agent.name,
@@ -482,7 +512,6 @@ Addresses for agent "${agent.name}":
482
512
  recipientPhone: answers.recipientPhone || void 0,
483
513
  companyName: companyAnswers.companyName || void 0,
484
514
  vatNumber: companyAnswers.vatNumber || void 0,
485
- taxId: companyAnswers.taxId || void 0,
486
515
  line1: answers.line1,
487
516
  line2: answers.line2 || void 0,
488
517
  city: answers.city,
@@ -539,6 +568,7 @@ function registerPaymentCommands(program2) {
539
568
  payment.command("list").alias("ls").description("List payment methods for the active agent").action(async () => {
540
569
  try {
541
570
  const agent = getActiveAgent();
571
+ await ensureAgentOnBackend(agent.name);
542
572
  const spinner = ora3("Fetching payment methods...").start();
543
573
  const api = getApiClient();
544
574
  const res = await api.get("/payment-methods", {
@@ -567,6 +597,7 @@ Payment methods for agent "${agent.name}":
567
597
  payment.command("add").description("Add a payment method (opens browser for secure entry)").action(async () => {
568
598
  try {
569
599
  const agent = getActiveAgent();
600
+ await ensureAgentOnBackend(agent.name);
570
601
  const spinner = ora3("Requesting secure payment setup link...").start();
571
602
  const api = getApiClient();
572
603
  const res = await api.post("/payment-methods/setup", {
@@ -1056,6 +1087,13 @@ function registerSearchCommands(program2) {
1056
1087
  } catch {
1057
1088
  }
1058
1089
  }
1090
+ if (!shipToCountry) {
1091
+ const savedCountry = getConfig().get("defaultCountry");
1092
+ if (savedCountry) {
1093
+ shipToCountry = savedCountry;
1094
+ spinner.text = `Searching for "${query}" (delivering to: ${shipToCountry})...`;
1095
+ }
1096
+ }
1059
1097
  }
1060
1098
  const forceExtended = opts.extendedSearch === true;
1061
1099
  const disableExtended = opts.extendedSearch === false;
@@ -1359,6 +1397,20 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
1359
1397
  }
1360
1398
  console.log();
1361
1399
  }
1400
+ const cacheItems = allProducts.filter((p) => p.id).map((p) => ({
1401
+ id: p.id,
1402
+ name: p.name,
1403
+ priceInCents: p.priceInCents,
1404
+ currency: p.currency,
1405
+ isExtended: p.isExtended
1406
+ }));
1407
+ getConfig().set("lastSearchResults", cacheItems);
1408
+ if (cacheItems.length > 0) {
1409
+ console.log(
1410
+ chalk5.dim(" \u{1F4A1} ") + chalk5.white("Buy a result by number: ") + chalk5.cyan("clishop buy 1") + chalk5.dim(" (buys the first result)")
1411
+ );
1412
+ console.log();
1413
+ }
1362
1414
  }
1363
1415
  const hasExtendedProducts = extended?.products?.length > 0;
1364
1416
  if (opts.interactive && allProducts.length > 0) {
@@ -1604,9 +1656,29 @@ var STATUS_COLORS = {
1604
1656
  };
1605
1657
  function registerOrderCommands(program2) {
1606
1658
  const order = program2.command("order").description("Place and manage orders");
1607
- program2.command("buy <productId>").description("Quick-buy a product").option("-q, --quantity <qty>", "Quantity", parseInt, 1).option("--address <id>", "Shipping address ID (uses agent default if omitted)").option("--payment <id>", "Payment method ID (uses agent default if omitted)").option("-y, --yes", "Skip confirmation prompt").action(async (productId, opts) => {
1659
+ program2.command("buy <productIdOrNumber>").description("Quick-buy a product (use product ID or search result number like 1, 2, 3)").option("-q, --quantity <qty>", "Quantity", parseInt, 1).option("--address <id>", "Shipping address ID (uses agent default if omitted)").option("--payment <id>", "Payment method ID (uses agent default if omitted)").option("-y, --yes", "Skip confirmation prompt").action(async (productIdOrNumber, opts) => {
1608
1660
  try {
1609
1661
  const agent = getActiveAgent();
1662
+ let productId = productIdOrNumber;
1663
+ const asNumber = parseInt(productIdOrNumber, 10);
1664
+ if (!isNaN(asNumber) && asNumber > 0 && String(asNumber) === productIdOrNumber.trim()) {
1665
+ const cached = getConfig().get("lastSearchResults") || [];
1666
+ if (cached.length === 0) {
1667
+ console.error(chalk6.red("\n\u2717 No recent search results. Run a search first: clishop search <query>"));
1668
+ process.exitCode = 1;
1669
+ return;
1670
+ }
1671
+ const index = asNumber - 1;
1672
+ if (index < 0 || index >= cached.length) {
1673
+ console.error(chalk6.red(`
1674
+ \u2717 Result #${asNumber} doesn't exist. Last search had ${cached.length} result(s).`));
1675
+ process.exitCode = 1;
1676
+ return;
1677
+ }
1678
+ productId = cached[index].id;
1679
+ console.log(chalk6.dim(` Resolved #${asNumber} \u2192 ${cached[index].name} (${productId})
1680
+ `));
1681
+ }
1610
1682
  const addressId = opts.address || agent.defaultAddressId;
1611
1683
  const paymentId = opts.payment || agent.defaultPaymentMethodId;
1612
1684
  if (!addressId) {
@@ -2469,6 +2541,7 @@ function registerStatusCommand(program2) {
2469
2541
  import chalk11 from "chalk";
2470
2542
  import ora9 from "ora";
2471
2543
  import inquirer7 from "inquirer";
2544
+ import open2 from "open";
2472
2545
  function divider(color = chalk11.cyan) {
2473
2546
  console.log(" " + color("\u2500".repeat(48)));
2474
2547
  }
@@ -2511,6 +2584,62 @@ async function runSetupWizard() {
2511
2584
  chalk11.dim(" It only takes a minute. You can re-run it anytime with:")
2512
2585
  );
2513
2586
  console.log(chalk11.dim(" ") + chalk11.white("clishop setup"));
2587
+ console.log();
2588
+ console.log(
2589
+ chalk11.bold.yellow(" \u{1F4CB} PATH Setup")
2590
+ );
2591
+ console.log(
2592
+ chalk11.dim(" Make sure clishop is in your PATH so your AI agents can use it.")
2593
+ );
2594
+ console.log(
2595
+ chalk11.dim(" If you installed via npm globally (") + chalk11.white("npm i -g clishop") + chalk11.dim("), it should already be available.")
2596
+ );
2597
+ console.log(
2598
+ chalk11.dim(" Verify with: ") + chalk11.white("clishop --version")
2599
+ );
2600
+ console.log(
2601
+ chalk11.dim(" If not, add the npm global bin to your PATH:")
2602
+ );
2603
+ console.log(
2604
+ chalk11.dim(" macOS/Linux: ") + chalk11.white('export PATH="$(npm config get prefix)/bin:$PATH"')
2605
+ );
2606
+ console.log(
2607
+ chalk11.dim(" Windows: ") + chalk11.white("npm config get prefix") + chalk11.dim(" \u2192 add that path to your system PATH")
2608
+ );
2609
+ console.log();
2610
+ const { setupMethod } = await inquirer7.prompt([
2611
+ {
2612
+ type: "select",
2613
+ name: "setupMethod",
2614
+ message: "How would you like to configure CLISHOP?",
2615
+ choices: [
2616
+ { name: "Here in the CLI", value: "cli" },
2617
+ { name: "On the website (opens browser)", value: "web" }
2618
+ ]
2619
+ }
2620
+ ]);
2621
+ if (setupMethod === "web") {
2622
+ const webSetupUrl = "https://clishop.ai/setup";
2623
+ console.log();
2624
+ console.log(chalk11.bold(" Opening the setup wizard on the website..."));
2625
+ console.log();
2626
+ console.log(" " + chalk11.cyan.underline(webSetupUrl));
2627
+ console.log();
2628
+ console.log(
2629
+ chalk11.dim(" Complete the setup there, then return here and run:")
2630
+ );
2631
+ console.log(chalk11.dim(" ") + chalk11.white("clishop login"));
2632
+ console.log();
2633
+ try {
2634
+ await open2(webSetupUrl);
2635
+ } catch {
2636
+ console.log(
2637
+ chalk11.yellow(" Could not open browser automatically. Please visit the link above.")
2638
+ );
2639
+ }
2640
+ config.set("setupCompleted", true);
2641
+ return;
2642
+ }
2514
2643
  stepHeader(1, 6, "Account");
2515
2644
  let loggedIn = await isLoggedIn();
2516
2645
  let isNewAccount = false;
@@ -2665,7 +2794,71 @@ async function runSetupWizard() {
2665
2794
  }
2666
2795
  }
2667
2796
  }
2668
- stepHeader(2, 6, "Monthly Spending Limit");
2797
+ stepHeader(2, 6, "Agent");
2798
+ console.log(
2799
+ chalk11.dim(
2800
+ " Agents are safety profiles that control per-order limits and categories."
2801
+ )
2802
+ );
2803
+ console.log(
2804
+ chalk11.dim(" A default agent is ready. You can customize it or create a new one.")
2805
+ );
2806
+ console.log();
2807
+ const { agentChoice } = await inquirer7.prompt([
2808
+ {
2809
+ type: "confirm",
2810
+ name: "agentChoice",
2811
+ message: "Configure a custom agent?",
2812
+ default: false
2813
+ }
2814
+ ]);
2815
+ if (agentChoice) {
2816
+ const answers = await inquirer7.prompt([
2817
+ {
2818
+ type: "input",
2819
+ name: "name",
2820
+ message: "Agent name:",
2821
+ validate: (v) => v.trim() ? true : "Name is required"
2822
+ },
2823
+ {
2824
+ type: "number",
2825
+ name: "maxOrderAmount",
2826
+ message: "Max order amount ($):",
2827
+ default: 500
2828
+ },
2829
+ {
2830
+ type: "confirm",
2831
+ name: "requireConfirmation",
2832
+ message: "Require confirmation before ordering?",
2833
+ default: true
2834
+ }
2835
+ ]);
2836
+ try {
2837
+ const agent = createAgent(answers.name.trim(), {
2838
+ maxOrderAmount: answers.maxOrderAmount,
2839
+ requireConfirmation: answers.requireConfirmation
2840
+ });
2841
+ setActiveAgent(agent.name);
2842
+ const syncSpinner = ora9("Syncing agent to backend...").start();
2843
+ await ensureAgentOnBackend(
2844
+ agent.name,
2845
+ agent.maxOrderAmount ? agent.maxOrderAmount * 100 : void 0,
2846
+ agent.requireConfirmation
2847
+ );
2848
+ syncSpinner.succeed(
2849
+ chalk11.green(
2850
+ `Agent "${chalk11.bold(agent.name)}" created and set as active.`
2851
+ )
2852
+ );
2853
+ } catch (error) {
2854
+ console.error(chalk11.red(`
2855
+ \u2717 ${error.message}`));
2856
+ console.log(chalk11.dim(" Continuing with the default agent."));
2857
+ }
2858
+ } else {
2859
+ console.log(chalk11.green(" \u2713 Using default agent."));
2860
+ }
2861
+ stepHeader(3, 6, "Monthly Spending Limit");
2669
2862
  if (isNewAccount) {
2670
2863
  console.log(
2671
2864
  chalk11.green(" \u2713 Spending limit was set during registration.")
@@ -2745,70 +2938,15 @@ async function runSetupWizard() {
2745
2938
  );
2746
2939
  }
2747
2940
  }
2748
- stepHeader(3, 6, "Agent (optional)");
2749
- const activeAgent = getActiveAgent();
2941
+ stepHeader(4, 6, "Shipping Address");
2750
2942
  console.log(
2751
2943
  chalk11.dim(
2752
- ` A default agent is ready ($${activeAgent.maxOrderAmount} limit, confirmation on).`
2944
+ " Add an address so products can be delivered to you."
2753
2945
  )
2754
2946
  );
2755
- console.log(
2756
- chalk11.dim(" Agents control spending limits and category restrictions.")
2757
- );
2758
- console.log();
2759
- const { agentChoice } = await inquirer7.prompt([
2760
- {
2761
- type: "confirm",
2762
- name: "agentChoice",
2763
- message: "Configure a custom agent?",
2764
- default: false
2765
- }
2766
- ]);
2767
- if (agentChoice) {
2768
- const answers = await inquirer7.prompt([
2769
- {
2770
- type: "input",
2771
- name: "name",
2772
- message: "Agent name:",
2773
- validate: (v) => v.trim() ? true : "Name is required"
2774
- },
2775
- {
2776
- type: "number",
2777
- name: "maxOrderAmount",
2778
- message: "Max order amount ($):",
2779
- default: 500
2780
- },
2781
- {
2782
- type: "confirm",
2783
- name: "requireConfirmation",
2784
- message: "Require confirmation before ordering?",
2785
- default: true
2786
- }
2787
- ]);
2788
- try {
2789
- const agent = createAgent(answers.name.trim(), {
2790
- maxOrderAmount: answers.maxOrderAmount,
2791
- requireConfirmation: answers.requireConfirmation
2792
- });
2793
- setActiveAgent(agent.name);
2794
- console.log(
2795
- chalk11.green(
2796
- `
2797
- \u2713 Agent "${chalk11.bold(agent.name)}" created and set as active.`
2798
- )
2799
- );
2800
- } catch (error) {
2801
- console.error(chalk11.red(`
2802
- \u2717 ${error.message}`));
2803
- console.log(chalk11.dim(" Continuing with the default agent."));
2804
- }
2805
- } else {
2806
- console.log(chalk11.green(" \u2713 Using default agent."));
2807
- }
2808
- stepHeader(4, 6, "Shipping Address");
2809
2947
  console.log(
2810
2948
  chalk11.dim(
2811
- " Add an address so products can be delivered to you."
2949
+ " A shipping country is required for product searches to work correctly."
2812
2950
  )
2813
2951
  );
2814
2952
  console.log();
@@ -2821,6 +2959,7 @@ async function runSetupWizard() {
2821
2959
  }
2822
2960
  ]);
2823
2961
  let addressCity = "";
2962
+ let addressCountry = "";
2824
2963
  if (addAddress) {
2825
2964
  const addr = await inquirer7.prompt([
2826
2965
  {
@@ -2832,7 +2971,8 @@ async function runSetupWizard() {
2832
2971
  {
2833
2972
  type: "input",
2834
2973
  name: "recipientName",
2835
- message: "Recipient name (optional):"
2974
+ message: "Recipient name:",
2975
+ validate: (v) => v.trim() ? true : "Recipient name is required"
2836
2976
  },
2837
2977
  {
2838
2978
  type: "input",
@@ -2885,7 +3025,7 @@ async function runSetupWizard() {
2885
3025
  default: false
2886
3026
  }
2887
3027
  ]);
2888
- let companyInfo = { companyName: "", vatNumber: "", taxId: "" };
3028
+ let companyInfo = { companyName: "", vatNumber: "" };
2889
3029
  if (addr.isCompany) {
2890
3030
  companyInfo = await inquirer7.prompt([
2891
3031
  {
@@ -2898,19 +3038,16 @@ async function runSetupWizard() {
2898
3038
  type: "input",
2899
3039
  name: "vatNumber",
2900
3040
  message: "VAT number (optional):"
2901
- },
2902
- {
2903
- type: "input",
2904
- name: "taxId",
2905
- message: "Tax ID / EIN (optional):"
2906
3041
  }
2907
3042
  ]);
2908
3043
  }
2909
3044
  addressCity = addr.city;
3045
+ addressCountry = addr.country;
2910
3046
  const spinner = ora9("Saving address...").start();
2911
3047
  try {
2912
3048
  const api = getApiClient();
2913
3049
  const agent = getActiveAgent();
3050
+ await ensureAgentOnBackend(agent.name);
2914
3051
  const res = await api.post("/addresses", {
2915
3052
  agent: agent.name,
2916
3053
  label: addr.label,
@@ -2918,7 +3055,6 @@ async function runSetupWizard() {
2918
3055
  recipientPhone: addr.recipientPhone || void 0,
2919
3056
  companyName: companyInfo.companyName || void 0,
2920
3057
  vatNumber: companyInfo.vatNumber || void 0,
2921
- taxId: companyInfo.taxId || void 0,
2922
3058
  line1: addr.line1,
2923
3059
  line2: addr.line2 || void 0,
2924
3060
  city: addr.city,
@@ -2946,9 +3082,29 @@ async function runSetupWizard() {
2946
3082
  );
2947
3083
  }
2948
3084
  } else {
3085
+ console.log();
3086
+ console.log(
3087
+ chalk11.yellow(
3088
+ " \u26A0 A country is required for product searches to show relevant results."
3089
+ )
3090
+ );
3091
+ console.log();
3092
+ const { country } = await inquirer7.prompt([
3093
+ {
3094
+ type: "input",
3095
+ name: "country",
3096
+ message: "Which country should we search products for? (e.g. US, NL, DE):",
3097
+ validate: (v) => v.trim() ? true : "Country is required for searches to work"
3098
+ }
3099
+ ]);
3100
+ addressCountry = country.trim();
3101
+ config.set("defaultCountry", addressCountry);
3102
+ console.log(
3103
+ chalk11.green(` \u2713 Country set to "${addressCountry}" for product searches.`)
3104
+ );
2949
3105
  console.log(
2950
3106
  chalk11.dim(
2951
- "\n You can add one later with: " + chalk11.white("clishop address add")
3107
+ "\n You can add a full address later with: " + chalk11.white("clishop address add")
2952
3108
  )
2953
3109
  );
2954
3110
  }
@@ -2975,6 +3131,7 @@ async function runSetupWizard() {
2975
3131
  try {
2976
3132
  const api = getApiClient();
2977
3133
  const agent = getActiveAgent();
3134
+ await ensureAgentOnBackend(agent.name);
2978
3135
  const res = await api.post("/payment-methods/setup", {
2979
3136
  agent: agent.name
2980
3137
  });
@@ -2994,6 +3151,11 @@ async function runSetupWizard() {
2994
3151
  " Once done, verify with: " + chalk11.white("clishop payment list")
2995
3152
  )
2996
3153
  );
3154
+ try {
3155
+ await open2(setupUrl);
3156
+ console.log(chalk11.dim(" (Browser opened automatically)"));
3157
+ } catch {
3158
+ }
2997
3159
  } catch (error) {
2998
3160
  spinner.fail(
2999
3161
  chalk11.red(
@@ -3020,6 +3182,12 @@ async function runSetupWizard() {
3020
3182
  ` Let's find something! Products can be shipped to ${chalk11.white(addressCity)}.`
3021
3183
  )
3022
3184
  );
3185
+ } else if (addressCountry) {
3186
+ console.log(
3187
+ chalk11.dim(
3188
+ ` Let's find something! Searching products available in ${chalk11.white(addressCountry)}.`
3189
+ )
3190
+ );
3023
3191
  } else {
3024
3192
  console.log(chalk11.dim(" Let's find something to order!"));
3025
3193
  }
@@ -3036,43 +3204,87 @@ async function runSetupWizard() {
3036
3204
  const spinner = ora9(`Searching for "${searchQuery}"...`).start();
3037
3205
  try {
3038
3206
  const api = getApiClient();
3207
+ const searchParams = {
3208
+ q: searchQuery,
3209
+ page: 1,
3210
+ pageSize: 5,
3211
+ extendedSearch: true,
3212
+ extendedTimeout: 30
3213
+ };
3214
+ if (addressCountry) {
3215
+ searchParams.country = addressCountry;
3216
+ }
3217
+ if (addressCity) {
3218
+ searchParams.city = addressCity;
3219
+ }
3220
+ spinner.text = `Searching for "${searchQuery}" (extended search across all stores)...`;
3039
3221
  const res = await api.get("/products/search", {
3040
- params: { q: searchQuery, page: 1, pageSize: 5 }
3222
+ params: searchParams,
3223
+ timeout: 35e3
3041
3224
  });
3042
3225
  spinner.stop();
3043
3226
  const result = res.data;
3044
- if (result.products.length === 0) {
3227
+ const extended = result.extended;
3228
+ const allProducts = [
3229
+ ...result.products || [],
3230
+ ...extended?.products || []
3231
+ ];
3232
+ if (allProducts.length === 0) {
3045
3233
  console.log(
3046
3234
  chalk11.yellow(
3047
3235
  `
3048
- No results for "${searchQuery}". Try other terms later!`
3236
+ No results for "${searchQuery}".`
3049
3237
  )
3050
3238
  );
3239
+ if (!addressCountry) {
3240
+ console.log(
3241
+ chalk11.yellow(
3242
+ " This might be because no shipping country was configured."
3243
+ )
3244
+ );
3245
+ console.log(
3246
+ chalk11.dim(
3247
+ " Add a shipping address with: " + chalk11.white("clishop address add")
3248
+ )
3249
+ );
3250
+ } else {
3251
+ console.log(
3252
+ chalk11.dim(" Try other search terms later!")
3253
+ );
3254
+ }
3051
3255
  } else {
3256
+ const totalCount = (result.total || 0) + (extended?.total || 0);
3052
3257
  console.log(
3053
3258
  chalk11.bold(
3054
3259
  `
3055
- Found ${result.total} result${result.total !== 1 ? "s" : ""} for "${searchQuery}":
3260
+ Found ${totalCount} result${totalCount !== 1 ? "s" : ""} for "${searchQuery}":
3056
3261
  `
3057
3262
  )
3058
3263
  );
3059
- for (const p of result.products) {
3264
+ for (let i = 0; i < Math.min(allProducts.length, 5); i++) {
3265
+ const p = allProducts[i];
3060
3266
  const price = formatPrice4(p.priceInCents, p.currency || "USD");
3061
- const stock = p.inStock ? chalk11.green("In Stock") : chalk11.red("Out of Stock");
3267
+ const stock = p.inStock !== false ? chalk11.green("In Stock") : chalk11.red("Out of Stock");
3268
+ const num = i + 1;
3062
3269
  console.log(
3063
- ` ${chalk11.bold.cyan(p.name)} ${chalk11.bold.white(price)} ${stock}`
3064
- );
3065
- console.log(chalk11.dim(` ID: ${p.id}`));
3066
- console.log(
3067
- chalk11.dim(
3068
- ` ${p.description.length > 100 ? p.description.slice(0, 100) + "..." : p.description}`
3069
- )
3270
+ ` ${chalk11.dim(`[${num}]`)} ${chalk11.bold.cyan(p.name)} ${chalk11.bold.white(price)} ${stock}`
3070
3271
  );
3272
+ console.log(chalk11.dim(` ID: ${p.id}`));
3273
+ if (p.description) {
3274
+ console.log(
3275
+ chalk11.dim(
3276
+ ` ${p.description.length > 100 ? p.description.slice(0, 100) + "..." : p.description}`
3277
+ )
3278
+ );
3279
+ }
3071
3280
  console.log();
3072
3281
  }
3073
3282
  console.log(
3074
3283
  chalk11.dim(" Buy a product with: ") + chalk11.white("clishop buy <product-id>")
3075
3284
  );
3285
+ console.log(
3286
+ chalk11.dim(" Or search again with: ") + chalk11.white("clishop search <query>")
3287
+ );
3076
3288
  }
3077
3289
  } catch (error) {
3078
3290
  spinner.fail(
@@ -4077,7 +4289,7 @@ function registerFeedbackCommands(program2) {
4077
4289
 
4078
4290
  // src/index.ts
4079
4291
  var program = new Command();
4080
- program.name("clishop").version("0.3.0").description(
4292
+ program.name("clishop").version("0.3.1").description(
4081
4293
  chalk15.bold("CLISHOP") + ' \u2014 Order anything from your terminal.\n\n Use agents to set safety limits, addresses, and payment methods.\n The "default" agent is used when no agent is specified.'
4082
4294
  ).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
4083
4295
  const agentOpt = thisCommand.opts().agent;
package/dist/mcp.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  getApiClient,
4
4
  getUserInfo,
5
5
  isLoggedIn
6
- } from "./chunk-RHBOI27D.js";
6
+ } from "./chunk-CVK6G342.js";
7
7
  import {
8
8
  __export,
9
9
  getActiveAgent,
10
10
  getConfig
11
- } from "./chunk-ZF3JIQRK.js";
11
+ } from "./chunk-X3H7SYR4.js";
12
12
 
13
13
  // src/mcp.ts
14
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -13810,7 +13810,7 @@ function safeCall(fn) {
13810
13810
  var server = new McpServer(
13811
13811
  {
13812
13812
  name: "clishop",
13813
- version: "0.3.0"
13813
+ version: "0.3.1"
13814
13814
  },
13815
13815
  {
13816
13816
  capabilities: {
@@ -14100,7 +14100,7 @@ server.registerTool("add_address", {
14100
14100
  instructions: args.instructions || void 0
14101
14101
  });
14102
14102
  if (args.setDefault && res.data.address?.id) {
14103
- const { updateAgent } = await import("./config-63FY6NWX.js");
14103
+ const { updateAgent } = await import("./config-WZBIQWME.js");
14104
14104
  updateAgent(agent.name, { defaultAddressId: res.data.address.id });
14105
14105
  }
14106
14106
  return res.data;
@@ -14291,7 +14291,7 @@ server.registerTool("list_agents", {
14291
14291
  }
14292
14292
  }, async () => {
14293
14293
  return safeCall(async () => {
14294
- const { listAgents, getConfig: getConfig2 } = await import("./config-63FY6NWX.js");
14294
+ const { listAgents, getConfig: getConfig2 } = await import("./config-WZBIQWME.js");
14295
14295
  const agents = listAgents();
14296
14296
  const active = getConfig2().get("activeAgent");
14297
14297
  return { agents, activeAgent: active };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clishop",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "mcpName": "io.github.StefDCL/clishop",
5
5
  "description": "CLISHOP — Order anything from your terminal",
6
6
  "main": "dist/index.js",
package/server.json CHANGED
@@ -6,24 +6,18 @@
6
6
  "url": "https://github.com/DavooxBv2/CLISHOP",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.1",
9
+ "version": "0.3.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "clishop",
14
- "version": "0.2.1",
14
+ "version": "0.3.0",
15
+ "runtime": "node",
16
+ "args": ["--mcp"],
15
17
  "transport": {
16
18
  "type": "stdio"
17
19
  },
18
- "environmentVariables": [
19
- {
20
- "description": "CLISHOP API key for authentication",
21
- "isRequired": true,
22
- "format": "string",
23
- "isSecret": true,
24
- "name": "CLISHOP_API_KEY"
25
- }
26
- ]
20
+ "environmentVariables": []
27
21
  }
28
22
  ]
29
23
  }