@higrowth/cli 0.1.1 → 0.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.
Files changed (3) hide show
  1. package/README.md +19 -6
  2. package/dist/index.js +265 -45
  3. package/package.json +6 -2
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # @higrowth/cli
2
2
 
3
3
  Log in to a Higrowth workspace from your terminal and install the
4
- Claude Code skills that drive it. The replacement for `cp -r skills/
5
- higrowth ~/.claude/skills/` and copy-paste tokens.
4
+ Higrowth playbook skills into your AI agent of choice. Supports
5
+ **Claude Code**, **Claude Desktop**, **Cursor**, and **Codex**
6
+ (CLI + Desktop + IDE extension share one config). Replaces the old
7
+ `cp -r skills/higrowth ~/.claude/skills/` + copy-paste-token dance.
6
8
 
7
9
  ## Install
8
10
 
@@ -33,7 +35,7 @@ higrowth skills install
33
35
  | `higrowth login [--host URL] [--device]` | Browser-auth handshake (PKCE by default; `--device` for SSH/containers). Writes the MCP entry into `~/.claude/mcp.json`. |
34
36
  | `higrowth whoami` | Show the currently-configured workspace + token name. |
35
37
  | `higrowth logout` | Remove the higrowth entry from `~/.claude/mcp.json`. Local-only — does NOT revoke the token (do that in Settings). |
36
- | `higrowth skills install [--target TARGET]` | Copy the five skill files into your agent's skills dir. Auto-detects Claude Code, Cursor, Claude Desktop; pass `--target claude\|cursor\|claude-desktop\|all` to override. |
38
+ | `higrowth skills install [--target TARGET]` | Copy the five skill files into your agent's skills dir. Auto-detects Claude Code / Cursor / Claude Desktop / Codex; pass `--target claude\|cursor\|claude-desktop\|codex\|all` to override. |
37
39
  | `higrowth skills list` | Show installed skills + which are out of date relative to the bundle. |
38
40
  | `higrowth skills update` | Re-install (overwrite) the latest skill versions. |
39
41
 
@@ -43,17 +45,28 @@ higrowth skills install
43
45
  |------|---------|--------------|
44
46
  | `--host URL` | `https://app.higrowth.ai` | Override the Higrowth host (for self-hosted or staging). |
45
47
  | `--device` | off | Use OAuth 2.0 device-code flow instead of PKCE. For SSH / containers. |
46
- | `--target TARGET` | auto-detect | `claude` / `cursor` / `claude-desktop` / `all`. |
48
+ | `--target TARGET` | auto-detect | `claude` / `cursor` / `claude-desktop` / `codex` / `all`. `codex` covers Codex CLI + Desktop + IDE extension via one shared `~/.codex/config.toml`. |
47
49
  | `--config PATH` | platform default | Override the MCP config file path. |
48
50
 
51
+ ## Where things land
52
+
53
+ | Target | MCP config | Skills dir |
54
+ |--------|------------|------------|
55
+ | `claude` (Claude Code) | `~/.claude/mcp.json` | `~/.claude/skills/<slug>/SKILL.md` |
56
+ | `cursor` | `~/.cursor/mcp.json` | `~/.cursor/skills/<slug>/SKILL.md` |
57
+ | `claude-desktop` | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) | `~/Library/Application Support/Claude/skills/<slug>/SKILL.md` |
58
+ | `codex` | `~/.codex/config.toml` (TOML, not JSON) | `~/.agents/skills/<slug>/SKILL.md` |
59
+
60
+ For Codex specifically: one write covers Codex CLI, Codex Desktop, and the Codex IDE extension because all three read the same `~/.codex/config.toml`. No per-app duplication.
61
+
49
62
  ## What it does NOT do
50
63
 
51
64
  - Doesn't manage tokens server-side. Token rotation / revocation
52
65
  happens in the Higrowth UI under Settings → API tokens.
53
66
  - Doesn't restart Claude Code for you. After `login` or `skills
54
67
  install`, restart manually so the new config takes effect.
55
- - Doesn't store secrets outside `~/.claude/mcp.json` (mode 600 where
56
- possible).
68
+ - Doesn't store secrets outside the per-target config file you're
69
+ installing into (mode 600 where the filesystem supports it).
57
70
 
58
71
  ## License
59
72
 
package/dist/index.js CHANGED
@@ -74,12 +74,14 @@ function base64UrlEncode(buf) {
74
74
  import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
75
75
  import { dirname, join } from "path";
76
76
  import { homedir, platform } from "os";
77
+ import TOML from "@iarna/toml";
77
78
  function configPaths() {
78
79
  const home = homedir();
79
80
  return [
80
- { target: "claude", path: join(home, ".claude", "mcp.json") },
81
- { target: "cursor", path: join(home, ".cursor", "mcp.json") },
82
- { target: "claude-desktop", path: claudeDesktopPath(home) }
81
+ { target: "claude", path: join(home, ".claude", "mcp.json"), format: "json", label: "Claude Code" },
82
+ { target: "cursor", path: join(home, ".cursor", "mcp.json"), format: "json", label: "Cursor" },
83
+ { target: "claude-desktop", path: claudeDesktopPath(home), format: "json", label: "Claude Desktop" },
84
+ { target: "codex", path: join(home, ".codex", "config.toml"), format: "toml", label: "Codex (CLI + Desktop + IDE)" }
83
85
  ];
84
86
  }
85
87
  function claudeDesktopPath(home) {
@@ -118,55 +120,73 @@ function resolveConfigPath(target) {
118
120
  if (!found) throw new Error(`Unknown target: ${resolved}`);
119
121
  return found;
120
122
  }
121
- function readConfig(path) {
123
+ function serversKey(format) {
124
+ return format === "toml" ? "mcp_servers" : "mcpServers";
125
+ }
126
+ function headersKey(format) {
127
+ return format === "toml" ? "http_headers" : "headers";
128
+ }
129
+ function readConfig(path, format) {
122
130
  if (!existsSync(path)) return {};
131
+ const raw = readFileSync(path, "utf-8");
123
132
  try {
124
- const raw = readFileSync(path, "utf-8");
125
- return JSON.parse(raw);
133
+ return format === "toml" ? TOML.parse(raw) : JSON.parse(raw);
126
134
  } catch (err) {
127
135
  throw new Error(
128
136
  `Could not parse existing MCP config at ${path}: ${err instanceof Error ? err.message : String(err)}`
129
137
  );
130
138
  }
131
139
  }
140
+ function serialize(cfg, format) {
141
+ if (format === "toml") {
142
+ return TOML.stringify(cfg);
143
+ }
144
+ return `${JSON.stringify(cfg, null, 2)}
145
+ `;
146
+ }
132
147
  function writeHigrowthEntry(input) {
133
- const existing = readConfig(input.path);
148
+ const existing = readConfig(input.path, input.format);
149
+ const sk = serversKey(input.format);
150
+ const hk = headersKey(input.format);
151
+ const existingServers = existing[sk] ?? {};
134
152
  const next = {
135
153
  ...existing,
136
- mcpServers: {
137
- ...existing.mcpServers ?? {},
154
+ [sk]: {
155
+ ...existingServers,
138
156
  higrowth: {
139
157
  url: `${input.host}/api/mcp`,
140
- headers: {
158
+ [hk]: {
141
159
  Authorization: `Bearer ${input.token}`
142
160
  }
143
161
  }
144
162
  }
145
163
  };
146
164
  mkdirSync(dirname(input.path), { recursive: true });
147
- writeFileSync(input.path, `${JSON.stringify(next, null, 2)}
148
- `, "utf-8");
165
+ writeFileSync(input.path, serialize(next, input.format), "utf-8");
149
166
  tryChmod600(input.path);
150
167
  }
151
- function removeHigrowthEntry(path) {
168
+ function removeHigrowthEntry(path, format) {
152
169
  if (!existsSync(path)) return false;
153
- const existing = readConfig(path);
154
- if (!existing.mcpServers || !existing.mcpServers.higrowth) return false;
155
- const { higrowth: _, ...rest } = existing.mcpServers;
156
- const next = { ...existing, mcpServers: rest };
170
+ const existing = readConfig(path, format);
171
+ const sk = serversKey(format);
172
+ const servers = existing[sk];
173
+ if (!servers || !servers.higrowth) return false;
174
+ const { higrowth: _drop, ...rest } = servers;
175
+ const next = { ...existing, [sk]: rest };
157
176
  if (Object.keys(rest).length === 0) {
158
- delete next.mcpServers;
177
+ delete next[sk];
159
178
  }
160
- writeFileSync(path, `${JSON.stringify(next, null, 2)}
161
- `, "utf-8");
179
+ writeFileSync(path, serialize(next, format), "utf-8");
162
180
  return true;
163
181
  }
164
- function readHigrowthEntry(path) {
182
+ function readHigrowthEntry(path, format) {
165
183
  if (!existsSync(path)) return null;
166
- const cfg = readConfig(path);
167
- const entry = cfg.mcpServers?.higrowth;
184
+ const cfg = readConfig(path, format);
185
+ const servers = cfg[serversKey(format)];
186
+ const entry = servers?.higrowth;
168
187
  if (!entry?.url) return null;
169
- const authHeader = entry.headers?.Authorization ?? "";
188
+ const headers = entry[headersKey(format)] ?? {};
189
+ const authHeader = headers.Authorization ?? "";
170
190
  const match = authHeader.match(/^Bearer\s+(\S+)$/);
171
191
  if (!match) return null;
172
192
  return { url: entry.url, token: match[1] };
@@ -196,8 +216,14 @@ async function loginCommand(opts) {
196
216
  } else {
197
217
  ({ token, organizationId } = await runPkceFlow(api, client));
198
218
  }
199
- const cfg = opts.configOverride !== void 0 ? { target: opts.target === "auto" ? "claude" : opts.target, path: opts.configOverride } : resolveConfigPath(opts.target);
200
- writeHigrowthEntry({ path: cfg.path, host, token });
219
+ const cfg = opts.configOverride !== void 0 ? {
220
+ target: opts.target === "auto" ? "claude" : opts.target,
221
+ path: opts.configOverride,
222
+ // Infer format from extension when the user supplied a custom path.
223
+ // `.toml` → codex-style; anything else (.json by convention) → JSON.
224
+ format: opts.configOverride.toLowerCase().endsWith(".toml") ? "toml" : "json"
225
+ } : resolveConfigPath(opts.target);
226
+ writeHigrowthEntry({ path: cfg.path, format: cfg.format, host, token });
201
227
  process.stdout.write(`
202
228
  \u2713 Logged in to ${host}
203
229
  `);
@@ -306,7 +332,7 @@ function tryHostname() {
306
332
  async function logoutCommand() {
307
333
  let removed = 0;
308
334
  for (const cfg of configPaths()) {
309
- if (removeHigrowthEntry(cfg.path)) {
335
+ if (removeHigrowthEntry(cfg.path, cfg.format)) {
310
336
  process.stdout.write(`\u2713 Removed higrowth entry from ${cfg.path}
311
337
  `);
312
338
  removed += 1;
@@ -325,24 +351,25 @@ async function logoutCommand() {
325
351
  async function whoamiCommand() {
326
352
  let foundAny = false;
327
353
  for (const cfg of configPaths()) {
328
- const entry = readHigrowthEntry(cfg.path);
354
+ const entry = readHigrowthEntry(cfg.path, cfg.format);
329
355
  if (!entry) continue;
330
356
  foundAny = true;
331
- process.stdout.write(`${cfg.target.padEnd(15)} ${entry.url}
357
+ const heading = cfg.label.padEnd(28);
358
+ process.stdout.write(`${heading} ${entry.url}
332
359
  `);
333
- process.stdout.write(`${"".padEnd(15)} ${maskToken(entry.token)}
360
+ process.stdout.write(`${"".padEnd(28)} ${maskToken(entry.token)}
334
361
  `);
335
- process.stdout.write(`${"".padEnd(15)} ${cfg.path}
362
+ process.stdout.write(`${"".padEnd(28)} ${cfg.path}
336
363
 
337
364
  `);
338
365
  const summary = await probeToken(entry.url, entry.token);
339
366
  if (summary) {
340
- process.stdout.write(`${"".padEnd(15)} \u2713 ${summary}
367
+ process.stdout.write(`${"".padEnd(28)} \u2713 ${summary}
341
368
 
342
369
  `);
343
370
  } else {
344
371
  process.stdout.write(
345
- `${"".padEnd(15)} \u2717 token did not authenticate (revoked or stale?)
372
+ `${"".padEnd(28)} \u2717 token did not authenticate (revoked or stale?)
346
373
 
347
374
  `
348
375
  );
@@ -1027,7 +1054,7 @@ async function skillsInstallCommand(opts) {
1027
1054
  const targets = resolveTargets(opts.target);
1028
1055
  if (targets.length === 0) {
1029
1056
  process.stdout.write(
1030
- "No supported skills directory detected. Pass --target claude|cursor|claude-desktop|all.\n"
1057
+ "No supported skills directory detected. Pass --target claude|cursor|claude-desktop|codex|all.\n"
1031
1058
  );
1032
1059
  process.exitCode = 1;
1033
1060
  return;
@@ -1089,7 +1116,8 @@ function resolveTargets(t) {
1089
1116
  const all = [
1090
1117
  "claude",
1091
1118
  "cursor",
1092
- "claude-desktop"
1119
+ "claude-desktop",
1120
+ "codex"
1093
1121
  ];
1094
1122
  if (t === "all") return all;
1095
1123
  if (t === "auto") {
@@ -1102,6 +1130,7 @@ function skillsDir(t) {
1102
1130
  const home = homedir2();
1103
1131
  if (t === "claude") return join2(home, ".claude", "skills");
1104
1132
  if (t === "cursor") return join2(home, ".cursor", "skills");
1133
+ if (t === "codex") return join2(home, ".agents", "skills");
1105
1134
  if (platform3() === "darwin") {
1106
1135
  return join2(home, "Library", "Application Support", "Claude", "skills");
1107
1136
  }
@@ -1128,8 +1157,174 @@ function compareSkill(path, skill) {
1128
1157
  }
1129
1158
  }
1130
1159
 
1160
+ // src/commands/upgrade.ts
1161
+ var PKG_NAME = "@higrowth/cli";
1162
+ async function upgradeCommand() {
1163
+ process.stdout.write(
1164
+ `
1165
+ Upgrade ${PKG_NAME} to the latest version with whichever package
1166
+ manager you used to install it:
1167
+
1168
+ npm install -g ${PKG_NAME}@latest
1169
+ pnpm add -g ${PKG_NAME}@latest
1170
+ bun add -g ${PKG_NAME}@latest
1171
+ yarn global add ${PKG_NAME}@latest
1172
+
1173
+ Or, if you installed via the curl-piped flow:
1174
+
1175
+ curl -fsSL https://app.higrowth.ai/install.sh | sh
1176
+
1177
+ After upgrade, run \`higrowth -v\` to confirm.
1178
+ `
1179
+ );
1180
+ }
1181
+
1182
+ // src/lib/update-check.ts
1183
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1184
+ import { dirname as dirname3, join as join3 } from "path";
1185
+ import { homedir as homedir3 } from "os";
1186
+ var PKG_NAME2 = "@higrowth/cli";
1187
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
1188
+ var REQUEST_TIMEOUT_MS = 2e3;
1189
+ function cachePath() {
1190
+ return join3(homedir3(), ".config", "higrowth", "update-check.json");
1191
+ }
1192
+ function readCache() {
1193
+ const path = cachePath();
1194
+ if (!existsSync3(path)) return null;
1195
+ try {
1196
+ const raw = readFileSync3(path, "utf-8");
1197
+ const parsed = JSON.parse(raw);
1198
+ if (typeof parsed.latestVersion !== "string") return null;
1199
+ if (typeof parsed.fetchedAt !== "number") return null;
1200
+ return { latestVersion: parsed.latestVersion, fetchedAt: parsed.fetchedAt };
1201
+ } catch {
1202
+ return null;
1203
+ }
1204
+ }
1205
+ function writeCache(latestVersion) {
1206
+ const path = cachePath();
1207
+ try {
1208
+ mkdirSync3(dirname3(path), { recursive: true });
1209
+ writeFileSync3(
1210
+ path,
1211
+ JSON.stringify({ latestVersion, fetchedAt: Date.now() }, null, 2),
1212
+ "utf-8"
1213
+ );
1214
+ } catch {
1215
+ }
1216
+ }
1217
+ async function fetchLatestFromNpm() {
1218
+ const controller = new AbortController();
1219
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
1220
+ try {
1221
+ const res = await fetch(`https://registry.npmjs.org/${PKG_NAME2}/latest`, {
1222
+ headers: { Accept: "application/json" },
1223
+ signal: controller.signal
1224
+ });
1225
+ if (!res.ok) return null;
1226
+ const body = await res.json();
1227
+ return body.version ?? null;
1228
+ } catch {
1229
+ return null;
1230
+ } finally {
1231
+ clearTimeout(timer);
1232
+ }
1233
+ }
1234
+ function compareVersions(a, b) {
1235
+ const parse = (v) => v.split("-")[0].split(".").map((s) => Number.parseInt(s, 10)).map((n2) => Number.isFinite(n2) ? n2 : 0);
1236
+ const aa = parse(a);
1237
+ const bb = parse(b);
1238
+ const n = Math.max(aa.length, bb.length);
1239
+ for (let i = 0; i < n; i++) {
1240
+ const ai = aa[i] ?? 0;
1241
+ const bi = bb[i] ?? 0;
1242
+ if (ai !== bi) return ai - bi;
1243
+ }
1244
+ return 0;
1245
+ }
1246
+ function shouldSkip() {
1247
+ if (process.env.HIGROWTH_NO_UPDATE_CHECK === "1") return true;
1248
+ if (process.env.CI) return true;
1249
+ if (!process.stdout.isTTY) return true;
1250
+ return false;
1251
+ }
1252
+ function beginUpdateCheck(currentVersion) {
1253
+ if (shouldSkip()) {
1254
+ return async () => {
1255
+ };
1256
+ }
1257
+ const cached = readCache();
1258
+ const fresh = cached && Date.now() - cached.fetchedAt < CHECK_INTERVAL_MS;
1259
+ const fetchPromise = fresh ? Promise.resolve(cached.latestVersion) : fetchLatestFromNpm().then((v) => {
1260
+ if (v) writeCache(v);
1261
+ return v;
1262
+ });
1263
+ return async () => {
1264
+ let latest;
1265
+ try {
1266
+ latest = await fetchPromise;
1267
+ } catch {
1268
+ return;
1269
+ }
1270
+ if (!latest) return;
1271
+ if (compareVersions(latest, currentVersion) <= 0) return;
1272
+ process.stderr.write(
1273
+ `
1274
+ \u2191 ${PKG_NAME2} ${latest} is available (you have ${currentVersion}).
1275
+ Upgrade: npm i -g ${PKG_NAME2}@latest
1276
+ (or bun add -g / pnpm add -g / yarn global add depending on how you installed.)
1277
+ Silence: set HIGROWTH_NO_UPDATE_CHECK=1
1278
+ `
1279
+ );
1280
+ };
1281
+ }
1282
+
1283
+ // package.json
1284
+ var package_default = {
1285
+ name: "@higrowth/cli",
1286
+ version: "0.3.0",
1287
+ description: "Higrowth CLI \u2014 log in via browser, install Claude Code / Codex skills, manage the MCP connection.",
1288
+ type: "module",
1289
+ license: "Apache-2.0",
1290
+ bin: {
1291
+ higrowth: "dist/index.js"
1292
+ },
1293
+ files: [
1294
+ "dist/",
1295
+ "README.md"
1296
+ ],
1297
+ scripts: {
1298
+ build: "tsup",
1299
+ dev: "tsup --watch",
1300
+ check: "tsc --noEmit"
1301
+ },
1302
+ engines: {
1303
+ node: ">=20"
1304
+ },
1305
+ publishConfig: {
1306
+ access: "public"
1307
+ },
1308
+ devDependencies: {
1309
+ "@types/iarna__toml": "^2.0.5",
1310
+ "@types/node": "^22.10.5",
1311
+ tsup: "^8.5.0",
1312
+ typescript: "^5.6.3"
1313
+ },
1314
+ keywords: ["higrowth", "seo", "aeo", "mcp", "claude-code", "agent"],
1315
+ homepage: "https://github.com/higrowth-ai/hg-engine",
1316
+ repository: {
1317
+ type: "git",
1318
+ url: "git+https://github.com/higrowth-ai/hg-engine.git",
1319
+ directory: "cli"
1320
+ },
1321
+ dependencies: {
1322
+ "@iarna/toml": "^2.2.5"
1323
+ }
1324
+ };
1325
+
1131
1326
  // src/index.ts
1132
- var VERSION = "0.1.1";
1327
+ var VERSION = package_default.version;
1133
1328
  var DEFAULT_HOST = "https://app.higrowth.ai";
1134
1329
  var USAGE = `higrowth \u2014 Connect your agent to a Higrowth workspace
1135
1330
 
@@ -1160,14 +1355,23 @@ COMMANDS
1160
1355
  skills update [--target TARGET]
1161
1356
  Re-install the bundled skill versions.
1162
1357
 
1163
- version
1358
+ upgrade
1359
+ Show the right command to upgrade @higrowth/cli to the latest
1360
+ published version, matched to your package manager (npm / pnpm /
1361
+ bun / yarn / curl-pipe). The CLI auto-checks for new versions on
1362
+ every invocation (cached 24h); set HIGROWTH_NO_UPDATE_CHECK=1 to
1363
+ silence the nudge.
1364
+
1365
+ version | --version | -v
1164
1366
  Print version + exit.
1165
1367
 
1166
1368
  FLAGS
1167
1369
  --host URL Override Higrowth host (default: ${DEFAULT_HOST}).
1168
1370
  --device Use OAuth device-code flow instead of PKCE.
1169
1371
  --target TARGET One of: auto (default) | claude | cursor |
1170
- claude-desktop | all.
1372
+ claude-desktop | codex | all.
1373
+ codex covers Codex CLI, Codex Desktop, and the
1374
+ Codex IDE extension \u2014 they share one config.
1171
1375
  --config PATH Override the MCP config file path.
1172
1376
 
1173
1377
  EXAMPLES
@@ -1207,15 +1411,32 @@ function resolveTarget(raw) {
1207
1411
  if (raw === void 0) return "auto";
1208
1412
  if (raw === true) return "auto";
1209
1413
  const s = String(raw).toLowerCase();
1210
- if (s === "auto" || s === "claude" || s === "cursor" || s === "claude-desktop" || s === "all") {
1414
+ if (s === "auto" || s === "claude" || s === "cursor" || s === "claude-desktop" || s === "codex" || s === "all") {
1211
1415
  return s;
1212
1416
  }
1213
1417
  throw new Error(
1214
- `Unknown target "${s}". Use auto | claude | cursor | claude-desktop | all.`
1418
+ `Unknown target "${s}". Use auto | claude | cursor | claude-desktop | codex | all.`
1215
1419
  );
1216
1420
  }
1217
1421
  async function main() {
1218
1422
  const args = parseArgs(process.argv.slice(2));
1423
+ if (args.flags.version === true || args.flags.v === true) {
1424
+ process.stdout.write(`higrowth ${VERSION}
1425
+ `);
1426
+ return;
1427
+ }
1428
+ if (args.command === "" && (args.flags.help === true || args.flags.h === true)) {
1429
+ process.stdout.write(USAGE);
1430
+ return;
1431
+ }
1432
+ const showUpdateNotice = beginUpdateCheck(VERSION);
1433
+ try {
1434
+ await runCommand(args);
1435
+ } finally {
1436
+ await showUpdateNotice();
1437
+ }
1438
+ }
1439
+ async function runCommand(args) {
1219
1440
  const host = typeof args.flags.host === "string" ? args.flags.host : DEFAULT_HOST;
1220
1441
  const target = resolveTarget(args.flags.target);
1221
1442
  const device = args.flags.device === true;
@@ -1223,20 +1444,16 @@ async function main() {
1223
1444
  switch (args.command) {
1224
1445
  case "":
1225
1446
  case "help":
1226
- case "--help":
1227
- case "-h":
1228
1447
  process.stdout.write(USAGE);
1229
1448
  return;
1230
1449
  case "version":
1231
- case "--version":
1232
- case "-v":
1233
1450
  process.stdout.write(`higrowth ${VERSION}
1234
1451
  `);
1235
1452
  return;
1236
1453
  case "login":
1237
1454
  if (target === "all") {
1238
1455
  process.stderr.write(
1239
- "login writes to a single config file. Use --target claude | cursor | claude-desktop (or omit for auto-detect). To install skills across all agents, use `higrowth skills install --target all` after login.\n"
1456
+ "login writes to a single config file. Use --target claude | cursor | claude-desktop | codex (or omit for auto-detect). To install skills across all agents, use `higrowth skills install --target all` after login.\n"
1240
1457
  );
1241
1458
  process.exitCode = 2;
1242
1459
  return;
@@ -1249,6 +1466,9 @@ async function main() {
1249
1466
  case "logout":
1250
1467
  await logoutCommand();
1251
1468
  return;
1469
+ case "upgrade":
1470
+ await upgradeCommand();
1471
+ return;
1252
1472
  case "skills":
1253
1473
  if (args.subcommand === "install" || args.subcommand === void 0) {
1254
1474
  await skillsInstallCommand({ target });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@higrowth/cli",
3
- "version": "0.1.1",
4
- "description": "Higrowth CLI — log in via browser, install Claude Code skills, manage the MCP connection.",
3
+ "version": "0.3.0",
4
+ "description": "Higrowth CLI — log in via browser, install Claude Code / Codex skills, manage the MCP connection.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "access": "public"
24
24
  },
25
25
  "devDependencies": {
26
+ "@types/iarna__toml": "^2.0.5",
26
27
  "@types/node": "^22.10.5",
27
28
  "tsup": "^8.5.0",
28
29
  "typescript": "^5.6.3"
@@ -33,5 +34,8 @@
33
34
  "type": "git",
34
35
  "url": "git+https://github.com/higrowth-ai/hg-engine.git",
35
36
  "directory": "cli"
37
+ },
38
+ "dependencies": {
39
+ "@iarna/toml": "^2.2.5"
36
40
  }
37
41
  }