clishop 1.2.3 → 1.3.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
@@ -31,12 +31,12 @@ CLISHOP is an open-source CLI that lets AI agents and humans search for products
31
31
 
32
32
  ## Highlights
33
33
 
34
- - **Multi-store search:** one query searches every store in the network and only returns items that ship to your address
35
- - **Safety thresholds:** cap how much can be spent per order, require email confirmation before anything ships, or skip confirmation entirely
36
- - **MCP server:** native [Model Context Protocol](https://modelcontextprotocol.io/) support with 19 tools so AI agents (VS Code Copilot, Claude, Cursor, Windsurf, etc.) can shop on your behalf
37
- - **Advertise requests:** can't find what you need? Publicly post a request and let stores try to find it for you
38
- - **Support & reviews:** if something goes wrong, create support tickets and handle the entire process from the CLI — write product and store reviews too
39
- - **Open marketplace:** anyone can start selling on CLISHOP by deploying a [Dark Store](https://github.com/DavooxBv2/CLISHOP-DARKSTORE) no website required
34
+ - One query searches every store in the network. Results are filtered to what actually ships to your address.
35
+ - Set spending caps per order, require email confirmation before anything ships, or let it go through automatically — your call.
36
+ - Ships as a native [MCP server](https://modelcontextprotocol.io/) with 44 tools. Works with VS Code Copilot, Claude, Cursor, Windsurf, and anything else that speaks MCP.
37
+ - Can't find what you need? Post an advertise request and let vendors compete to fulfill it.
38
+ - Support tickets, product reviews, store reviews all from the terminal.
39
+ - Anyone can sell on CLISHOP by deploying a [Dark Store](https://github.com/DavooxBv2/CLISHOP-DARKSTORE). No website needed.
40
40
 
41
41
  ---
42
42
 
@@ -132,7 +132,7 @@ clishop buy 1
132
132
 
133
133
  ## Sell on CLISHOP
134
134
 
135
- Want to sell your own products? Use the [Dark Store](https://github.com/DavooxBv2/CLISHOP-DARKSTORE) template to create your own store — no website needed. Configure your catalog, shipping, and pricing in a few YAML files, deploy to Vercel, and start receiving orders.
135
+ You can run your own store with the [Dark Store](https://github.com/DavooxBv2/CLISHOP-DARKSTORE) template. Define your catalog, shipping rules, and pricing in YAML, deploy to Vercel, and you're live. No website needed.
136
136
 
137
137
  ---
138
138
 
@@ -150,7 +150,7 @@ npm run lint # Type-check
150
150
 
151
151
  ## MCP Server
152
152
 
153
- CLISHOP ships as a native MCP server with 19 tools. Any MCP-compatible client gets shopping capabilities out of the box.
153
+ CLISHOP ships as a native MCP server with 44 tools. Any MCP-compatible client gets shopping capabilities out of the box.
154
154
 
155
155
  ```bash
156
156
  clishop-mcp # If installed globally
@@ -3,12 +3,116 @@ import {
3
3
  } from "./chunk-X3H7SYR4.js";
4
4
 
5
5
  // src/auth.ts
6
- import keytar from "keytar";
6
+ import { createRequire } from "module";
7
7
  import axios from "axios";
8
+
9
+ // src/auth-file-store.ts
10
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
11
+ import { join } from "path";
12
+ import { homedir } from "os";
13
+ var CONFIG_DIR = join(homedir(), ".config", "clishop");
14
+ var AUTH_FILE = join(CONFIG_DIR, "auth.json");
15
+ function ensureDir() {
16
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
17
+ }
18
+ function readStore() {
19
+ try {
20
+ return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
21
+ } catch {
22
+ return {};
23
+ }
24
+ }
25
+ function writeStore(data) {
26
+ ensureDir();
27
+ writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
28
+ try {
29
+ chmodSync(AUTH_FILE, 384);
30
+ } catch {
31
+ }
32
+ }
33
+ function fileSetPassword(service, account, password) {
34
+ const store = readStore();
35
+ store[`${service}:${account}`] = password;
36
+ writeStore(store);
37
+ }
38
+ function fileGetPassword(service, account) {
39
+ const store = readStore();
40
+ return store[`${service}:${account}`] ?? null;
41
+ }
42
+ function fileDeletePassword(service, account) {
43
+ const store = readStore();
44
+ delete store[`${service}:${account}`];
45
+ writeStore(store);
46
+ }
47
+
48
+ // src/auth.ts
49
+ var require2 = createRequire(import.meta.url);
50
+ var _keytar = null;
51
+ var _keytarChecked = false;
52
+ function getKeytar() {
53
+ if (_keytarChecked) return _keytar;
54
+ _keytarChecked = true;
55
+ try {
56
+ _keytar = require2("keytar");
57
+ } catch {
58
+ _keytar = null;
59
+ }
60
+ return _keytar;
61
+ }
8
62
  var SERVICE_NAME = "clishop";
9
63
  var ACCOUNT_TOKEN = "auth-token";
10
64
  var ACCOUNT_REFRESH = "refresh-token";
11
65
  var ACCOUNT_USER = "user-info";
66
+ var _activeBackend = null;
67
+ function resolveBackend() {
68
+ if (_activeBackend) return _activeBackend;
69
+ if (process.env.CLISHOP_TOKEN) {
70
+ _activeBackend = "env";
71
+ return _activeBackend;
72
+ }
73
+ const kt = getKeytar();
74
+ if (kt) {
75
+ _activeBackend = "keytar";
76
+ return _activeBackend;
77
+ }
78
+ if (!process.env.CLISHOP_QUIET) {
79
+ console.warn(
80
+ "[clishop] Secure keychain unavailable \u2014 using file-based token storage (~/.config/clishop/auth.json).\n To enable keychain on Ubuntu/WSL: sudo apt install libsecret-1-0\n"
81
+ );
82
+ }
83
+ _activeBackend = "file";
84
+ return _activeBackend;
85
+ }
86
+ function isKeytarAvailable() {
87
+ return getKeytar() !== null;
88
+ }
89
+ async function setPassword(account, value) {
90
+ const backend = resolveBackend();
91
+ if (backend === "env") return;
92
+ if (backend === "keytar") {
93
+ return getKeytar().setPassword(SERVICE_NAME, account, value);
94
+ }
95
+ fileSetPassword(SERVICE_NAME, account, value);
96
+ }
97
+ async function getPassword(account) {
98
+ const backend = resolveBackend();
99
+ if (backend === "env" && account === ACCOUNT_TOKEN) {
100
+ return process.env.CLISHOP_TOKEN;
101
+ }
102
+ if (backend === "keytar") {
103
+ return getKeytar().getPassword(SERVICE_NAME, account);
104
+ }
105
+ return fileGetPassword(SERVICE_NAME, account);
106
+ }
107
+ async function deletePassword(account) {
108
+ const backend = resolveBackend();
109
+ if (backend === "env") return;
110
+ if (backend === "keytar") {
111
+ await getKeytar().deletePassword(SERVICE_NAME, account);
112
+ return;
113
+ }
114
+ fileDeletePassword(SERVICE_NAME, account);
115
+ }
12
116
  function assertAuthResponse(data) {
13
117
  const payload = data;
14
118
  if (!payload || typeof payload !== "object") {
@@ -29,22 +133,22 @@ function assertAuthResponse(data) {
29
133
  return payload;
30
134
  }
31
135
  async function storeToken(token) {
32
- await keytar.setPassword(SERVICE_NAME, ACCOUNT_TOKEN, token);
136
+ await setPassword(ACCOUNT_TOKEN, token);
33
137
  }
34
138
  async function storeRefreshToken(token) {
35
- await keytar.setPassword(SERVICE_NAME, ACCOUNT_REFRESH, token);
139
+ await setPassword(ACCOUNT_REFRESH, token);
36
140
  }
37
141
  async function storeUserInfo(user) {
38
- await keytar.setPassword(SERVICE_NAME, ACCOUNT_USER, JSON.stringify(user));
142
+ await setPassword(ACCOUNT_USER, JSON.stringify(user));
39
143
  }
40
144
  async function getToken() {
41
- return keytar.getPassword(SERVICE_NAME, ACCOUNT_TOKEN);
145
+ return getPassword(ACCOUNT_TOKEN);
42
146
  }
43
147
  async function getRefreshToken() {
44
- return keytar.getPassword(SERVICE_NAME, ACCOUNT_REFRESH);
148
+ return getPassword(ACCOUNT_REFRESH);
45
149
  }
46
150
  async function getUserInfo() {
47
- const raw = await keytar.getPassword(SERVICE_NAME, ACCOUNT_USER);
151
+ const raw = await getPassword(ACCOUNT_USER);
48
152
  if (!raw) return null;
49
153
  try {
50
154
  return JSON.parse(raw);
@@ -53,13 +157,12 @@ async function getUserInfo() {
53
157
  }
54
158
  }
55
159
  async function clearAuth() {
56
- await keytar.deletePassword(SERVICE_NAME, ACCOUNT_TOKEN);
57
- await keytar.deletePassword(SERVICE_NAME, ACCOUNT_REFRESH);
58
- await keytar.deletePassword(SERVICE_NAME, ACCOUNT_USER);
160
+ await deletePassword(ACCOUNT_TOKEN);
161
+ await deletePassword(ACCOUNT_REFRESH);
162
+ await deletePassword(ACCOUNT_USER);
59
163
  }
60
164
  async function isLoggedIn() {
61
- const token = await getToken();
62
- return !!token;
165
+ return !!await getToken();
63
166
  }
64
167
  async function login(email, password) {
65
168
  const baseUrl = getApiBaseUrl();
@@ -198,6 +301,9 @@ function handleApiError(error) {
198
301
  }
199
302
 
200
303
  export {
304
+ resolveBackend,
305
+ isKeytarAvailable,
306
+ getToken,
201
307
  getUserInfo,
202
308
  isLoggedIn,
203
309
  login,
package/dist/index.js CHANGED
@@ -2,18 +2,22 @@
2
2
  import {
3
3
  ensureAgentOnBackend,
4
4
  getApiClient,
5
+ getToken,
5
6
  getUserInfo,
6
7
  handleApiError,
8
+ isKeytarAvailable,
7
9
  isLoggedIn,
8
10
  login,
9
11
  logout,
10
- register
11
- } from "./chunk-CVK6G342.js";
12
+ register,
13
+ resolveBackend
14
+ } from "./chunk-3BBLDX6L.js";
12
15
  import {
13
16
  createAgent,
14
17
  deleteAgent,
15
18
  getActiveAgent,
16
19
  getAgent,
20
+ getApiBaseUrl,
17
21
  getConfig,
18
22
  listAgents,
19
23
  setActiveAgent,
@@ -22,7 +26,7 @@ import {
22
26
 
23
27
  // src/index.ts
24
28
  import { Command } from "commander";
25
- import chalk15 from "chalk";
29
+ import chalk16 from "chalk";
26
30
 
27
31
  // src/commands/auth.ts
28
32
  import chalk from "chalk";
@@ -932,7 +936,7 @@ async function runSetupWizard() {
932
936
  console.log();
933
937
  console.log(chalk4.bold.cyan(" W E L C O M E T O C L I S H O P"));
934
938
  console.log(chalk4.dim(" Order anything from your terminal."));
935
- console.log(chalk4.dim(` Build: ${"2026-03-04T12:59:38.729Z"}`));
939
+ console.log(chalk4.dim(` Build: ${"2026-03-22T16:08:08.167Z"}`));
936
940
  console.log();
937
941
  divider(chalk4.cyan);
938
942
  console.log();
@@ -1895,7 +1899,6 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
1895
1899
  }
1896
1900
  if (info.checkout && typeof info.checkout === "object") {
1897
1901
  const parts = [];
1898
- if (info.checkout.mode) parts.push(info.checkout.mode);
1899
1902
  if (info.checkout.note) parts.push(info.checkout.note);
1900
1903
  if (parts.length > 0) {
1901
1904
  console.log(` ${chalk6.bold("Checkout:")} ${parts.join(" \u2014 ")}`);
@@ -2073,7 +2076,7 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
2073
2076
  }
2074
2077
  }
2075
2078
  function registerSearchCommands(program2) {
2076
- program2.command("search <query>").description("Search for products").option("-c, --category <category>", "Filter by category").option("--brand <brand>", "Filter by brand").option("--model <model>", "Filter by model name/number").option("--sku <sku>", "Filter by SKU").option("--gtin <gtin>", "Filter by GTIN (UPC/EAN/ISBN)").option("--variant <variant>", "Filter by variant (size/color/storage/etc.)").option("--min-price <price>", "Minimum price (cents)", parseFloat).option("--max-price <price>", "Maximum price (cents)", parseFloat).option("--max-shipping <price>", "Maximum shipping cost (cents)", parseInt).option("--max-total <price>", "Maximum landed total: item + shipping (cents)", parseInt).option("--free-shipping", "Only show items with free shipping").option("--ship-to <address>", "Saved address label or ID (resolves country/city/postal automatically)").option("--country <code>", "Delivery country (ISO 3166-1 alpha-2, e.g. US, BE, NL)").option("--city <city>", "Delivery city").option("--postal-code <code>", "Delivery postal/zip code").option("--region <region>", "Delivery state/province/region").option("--lat <latitude>", "Delivery latitude (for local/proximity search)", parseFloat).option("--lng <longitude>", "Delivery longitude (for local/proximity search)", parseFloat).option("--deliver-by <date>", "Need delivery by date (YYYY-MM-DD)").option("--max-delivery-days <days>", "Maximum delivery/transit days", parseInt).option("--in-stock", "Only show in-stock items").option("--exclude-backorder", "Exclude backordered items").option("--min-qty <qty>", "Minimum quantity available", parseInt).option("--free-returns", "Only show items with free returns").option("--min-return-window-days <days>", "Minimum return window in days", parseInt).option("--store <store>", "Limit to a store (ID, slug, or name)").option("--vendor <vendor>", "Filter by vendor name (alias for --store)").option("--trusted-only", "Only show products from verified stores").option("--min-store-rating <rating>", "Minimum store rating (0-5)", parseFloat).option("--checkout-mode <mode>", "Checkout mode: instant, handoff").option("--min-rating <rating>", "Minimum product rating (1-5)", parseFloat).option("-s, --sort <field>", "Sort by: price, total-cost, rating, relevance, newest, delivery", "relevance").option("--order <dir>", "Sort order: asc, desc", "desc").option("-p, --page <page>", "Page number", parseInt, 1).option("-n, --per-page <count>", "Results per page", parseInt, 10).option("--express", "Only show items with 2-day or faster delivery").option("-e, --extended-search", "Enable extended search: query darkstores when no local results found").option("--no-extended-search", "Disable automatic extended search when no local results found").option("--extended-timeout <seconds>", "Extended search timeout in seconds (default: 30, max: 60)", parseInt).option("--json", "Output raw JSON").option("--compact", "Compact one-line-per-result output").option("--detailed", "Show full product details inline").option("-i, --interactive", "After results, interactively select products to get more info from their store").action(async (query, opts) => {
2079
+ program2.command("search <query>").description("Search for products").option("-c, --category <category>", "Filter by category").option("--brand <brand>", "Filter by brand").option("--model <model>", "Filter by model name/number").option("--sku <sku>", "Filter by SKU").option("--gtin <gtin>", "Filter by GTIN (UPC/EAN/ISBN)").option("--variant <variant>", "Filter by variant (size/color/storage/etc.)").option("--min-price <price>", "Minimum price (cents)", parseFloat).option("--max-price <price>", "Maximum price (cents)", parseFloat).option("--max-shipping <price>", "Maximum shipping cost (cents)", parseInt).option("--max-total <price>", "Maximum landed total: item + shipping (cents)", parseInt).option("--free-shipping", "Only show items with free shipping").option("--ship-to <address>", "Saved address label or ID (resolves country/city/postal automatically)").option("--country <code>", "Delivery country (ISO 3166-1 alpha-2, e.g. US, BE, NL)").option("--city <city>", "Delivery city").option("--postal-code <code>", "Delivery postal/zip code").option("--region <region>", "Delivery state/province/region").option("--lat <latitude>", "Delivery latitude (for local/proximity search)", parseFloat).option("--lng <longitude>", "Delivery longitude (for local/proximity search)", parseFloat).option("--deliver-by <date>", "Need delivery by date (YYYY-MM-DD)").option("--max-delivery-days <days>", "Maximum delivery/transit days", parseInt).option("--in-stock", "Only show in-stock items").option("--exclude-backorder", "Exclude backordered items").option("--min-qty <qty>", "Minimum quantity available", parseInt).option("--free-returns", "Only show items with free returns").option("--min-return-window-days <days>", "Minimum return window in days", parseInt).option("--store <store>", "Limit to a store (ID, slug, or name)").option("--vendor <vendor>", "Filter by vendor name (alias for --store)").option("--trusted-only", "Only show products from verified stores").option("--min-store-rating <rating>", "Minimum store rating (0-5)", parseFloat).option("--min-rating <rating>", "Minimum product rating (1-5)", parseFloat).option("-s, --sort <field>", "Sort by: price, total-cost, rating, relevance, newest, delivery", "relevance").option("--order <dir>", "Sort order: asc, desc", "desc").option("-p, --page <page>", "Page number", parseInt, 1).option("-n, --per-page <count>", "Results per page", parseInt, 10).option("--express", "Only show items with 2-day or faster delivery").option("-e, --extended-search", "Enable extended search: query darkstores when no local results found").option("--no-extended-search", "Disable automatic extended search when no local results found").option("--extended-timeout <seconds>", "Extended search timeout in seconds (default: 30, max: 60)", parseInt).option("--json", "Output raw JSON").option("--compact", "Compact one-line-per-result output").option("--detailed", "Show full product details inline").option("-i, --interactive", "After results, interactively select products to get more info from their store").action(async (query, opts) => {
2077
2080
  try {
2078
2081
  const spinner = ora5(`Searching for "${query}"...`).start();
2079
2082
  const api = getApiClient();
@@ -2186,7 +2189,6 @@ function registerSearchCommands(program2) {
2186
2189
  vendor: opts.vendor,
2187
2190
  trustedOnly: opts.trustedOnly || void 0,
2188
2191
  minStoreRating: opts.minStoreRating,
2189
- checkoutMode: opts.checkoutMode,
2190
2192
  // Rating / sorting / pagination
2191
2193
  minRating: opts.minRating,
2192
2194
  sort: opts.sort === "total-cost" ? "price" : opts.sort,
@@ -2634,9 +2636,6 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
2634
2636
  if (p.freeReturns) returnParts.push(chalk6.green("Free Returns"));
2635
2637
  if (p.returnWindowDays) returnParts.push(`${p.returnWindowDays}-day return window`);
2636
2638
  if (returnParts.length) console.log(` Returns: ${returnParts.join(" \xB7 ")}`);
2637
- if (p.checkoutMode && p.checkoutMode !== "instant") {
2638
- console.log(` Checkout: ${chalk6.yellow(p.checkoutMode)}`);
2639
- }
2640
2639
  console.log();
2641
2640
  console.log(` ${p.description}`);
2642
2641
  console.log();
@@ -4585,16 +4584,74 @@ function registerFeedbackCommands(program2) {
4585
4584
  });
4586
4585
  }
4587
4586
 
4587
+ // src/commands/doctor.ts
4588
+ import chalk15 from "chalk";
4589
+ import { join } from "path";
4590
+ import { homedir } from "os";
4591
+ import { mkdirSync } from "fs";
4592
+ import axios from "axios";
4593
+ function registerDoctorCommand(program2) {
4594
+ program2.command("doctor").description("Check system compatibility and auth status").action(async () => {
4595
+ const checks = [];
4596
+ const keytarOk = isKeytarAvailable();
4597
+ checks.push({
4598
+ name: "Secure keychain (keytar)",
4599
+ ok: keytarOk,
4600
+ detail: keytarOk ? "keytar loaded successfully" : "keytar unavailable \u2014 install libsecret:\n sudo apt install libsecret-1-0"
4601
+ });
4602
+ const backend = resolveBackend();
4603
+ checks.push({
4604
+ name: "Auth backend",
4605
+ ok: true,
4606
+ detail: backend === "env" ? "Using CLISHOP_TOKEN environment variable" : backend === "keytar" ? "Using OS keychain" : "Using file store (~/.config/clishop/auth.json)"
4607
+ });
4608
+ try {
4609
+ const dir = join(homedir(), ".config", "clishop");
4610
+ mkdirSync(dir, { recursive: true, mode: 448 });
4611
+ checks.push({ name: "File store writable", ok: true, detail: dir });
4612
+ } catch (e) {
4613
+ checks.push({
4614
+ name: "File store writable",
4615
+ ok: false,
4616
+ detail: e instanceof Error ? e.message : String(e)
4617
+ });
4618
+ }
4619
+ const token = await getToken();
4620
+ checks.push({
4621
+ name: "Authenticated",
4622
+ ok: !!token,
4623
+ detail: token ? "Token present" : "Not logged in \u2014 run: clishop setup"
4624
+ });
4625
+ const apiUrl = getApiBaseUrl();
4626
+ try {
4627
+ await axios.get(`${apiUrl}/health`, { timeout: 5e3 });
4628
+ checks.push({ name: "API reachable", ok: true, detail: apiUrl });
4629
+ } catch {
4630
+ checks.push({
4631
+ name: "API reachable",
4632
+ ok: false,
4633
+ detail: `Cannot reach ${apiUrl}`
4634
+ });
4635
+ }
4636
+ console.log(chalk15.bold("\n CLISHOP Doctor\n"));
4637
+ for (const c of checks) {
4638
+ const icon = c.ok ? chalk15.green("\u2713") : chalk15.red("\u2717");
4639
+ console.log(` ${icon} ${chalk15.bold(c.name)}: ${c.detail}`);
4640
+ }
4641
+ console.log();
4642
+ });
4643
+ }
4644
+
4588
4645
  // src/index.ts
4589
4646
  var program = new Command();
4590
- program.name("clishop").version("1.2.3").description(
4591
- 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.'
4647
+ program.name("clishop").version("1.3.0").description(
4648
+ chalk16.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.'
4592
4649
  ).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
4593
4650
  const agentOpt = thisCommand.opts().agent;
4594
4651
  if (agentOpt) {
4595
4652
  const config = getConfig();
4596
4653
  if (!config.store.agents[agentOpt]) {
4597
- console.error(chalk15.red(`\u2717 Agent "${agentOpt}" does not exist.`));
4654
+ console.error(chalk16.red(`\u2717 Agent "${agentOpt}" does not exist.`));
4598
4655
  process.exit(1);
4599
4656
  }
4600
4657
  process.env.__CLISHOP_AGENT_OVERRIDE = agentOpt;
@@ -4614,6 +4671,7 @@ registerSetupCommand(program);
4614
4671
  registerAdvertiseCommands(program);
4615
4672
  registerSupportCommands(program);
4616
4673
  registerFeedbackCommands(program);
4674
+ registerDoctorCommand(program);
4617
4675
  async function main() {
4618
4676
  if (process.argv.includes("--mcp")) {
4619
4677
  const { startMcpServer } = await import("./mcp.js");
@@ -4631,6 +4689,6 @@ async function main() {
4631
4689
  await program.parseAsync(process.argv);
4632
4690
  }
4633
4691
  main().catch((err) => {
4634
- console.error(chalk15.red(err.message));
4692
+ console.error(chalk16.red(err.message));
4635
4693
  process.exit(1);
4636
4694
  });
package/dist/mcp.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  getApiClient,
4
4
  getUserInfo,
5
5
  isLoggedIn
6
- } from "./chunk-CVK6G342.js";
6
+ } from "./chunk-3BBLDX6L.js";
7
7
  import {
8
8
  __export,
9
9
  getActiveAgent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clishop",
3
- "version": "1.2.3",
3
+ "version": "1.3.0",
4
4
  "mcpName": "io.github.StefDCL/clishop",
5
5
  "description": "CLISHOP — Order anything from your terminal",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,8 @@
17
17
  "dev:mcp": "tsx src/mcp.ts",
18
18
  "start": "node dist/index.js",
19
19
  "start:mcp": "node dist/mcp.js",
20
- "lint": "tsc --noEmit"
20
+ "lint": "tsc --noEmit",
21
+ "postinstall": "node -e \"try{require('keytar')}catch{console.warn('\\n[clishop] Optional: install libsecret for secure keychain storage:\\n sudo apt install libsecret-1-0\\nWithout it, clishop uses file-based token storage.\\n')}\""
21
22
  },
22
23
  "repository": {
23
24
  "type": "git",
@@ -49,10 +50,12 @@
49
50
  "commander": "^14.0.3",
50
51
  "conf": "^15.1.0",
51
52
  "inquirer": "^13.2.4",
52
- "keytar": "^7.9.0",
53
53
  "open": "^11.0.0",
54
54
  "ora": "^9.3.0"
55
55
  },
56
+ "optionalDependencies": {
57
+ "keytar": "^7.9.0"
58
+ },
56
59
  "devDependencies": {
57
60
  "@types/inquirer": "^9.0.9",
58
61
  "@types/keytar": "^4.4.0",