@zalify/cli 0.17.0 → 0.17.2

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 +106 -33
  2. package/dist/cli.js +192 -104
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # @zalify/cli
2
2
 
3
- Zalify CLI command-line interface for Zalify.
3
+ Zalify CLI author a brand, launch its shop, run its ads, keep the theme
4
+ current.
4
5
 
5
6
  ## Install
6
7
 
@@ -10,45 +11,119 @@ npm i -g zalify
10
11
  npm i -g @zalify/cli@latest
11
12
  ```
12
13
 
13
- ## Usage
14
+ Already installed? `zalify self-update`.
14
15
 
15
- ### Authentication
16
+ ## The command model
16
17
 
17
- ```bash
18
- zalify login # opens the browser: pick a workspace, authorize
19
- zalify login --app-url http://localhost:3000 # against a local app.zalify.com
20
- zalify whoami # show the authorized workspace
21
- zalify logout # delete local credentials
22
- ```
18
+ Four nouns, four objects:
23
19
 
24
- `login` runs a loopback flow (like `gh auth login`): the CLI listens on a
25
- random localhost port, the browser opens `app.zalify.com/cli/auth`, and on
26
- approval a **workspace-scoped API key** is handed back to the CLI and stored
27
- in `~/.zalify/config.json` (0600). Keys are ordinary Zalify Public API keys
28
- list and revoke them in **Settings Developer**.
20
+ | Noun | The object | What lives under it |
21
+ | --- | --- | --- |
22
+ | `brand` | the **data** | a brand folder: catalog, voice guide, image manifest |
23
+ | `shop` | the **shop** | Zalify-hosted at `<slug>.z1.shop`: repo + deployment + domain |
24
+ | `ads` | the **ad ops** | Autopilot brand instance: campaign intent repo (media plans, ads-as-code, policy) |
25
+ | `theme` | the **code** | Shopify-theme model: scaffold a copy, measure drift, upgrade |
29
26
 
30
- ### Assets
27
+ Everything acting on a brand folder lives under `brand`. Brand and theme
28
+ commands default their directory to the cwd; `shop refresh`/`open` resolve
29
+ their shop from the cwd (`.zalify/site.json` in a scaffold, `store.json` in
30
+ a brand folder); `ads` commands run arg-free from a checkout carrying
31
+ `.zalify/brand-repo.json`.
31
32
 
32
- Sync a directory's `images/*.png` with the workspace asset library
33
- (the same storage that backs Assets in the Zalify app):
33
+ ## The golden path (new brand, start to live)
34
34
 
35
35
  ```bash
36
- zalify assets push <dir> # upload new/changed files (sha256 dedup)
37
- zalify assets pull <dir> # download files listed in images/assets.json
36
+ zalify brand init stores/acme # 1. author the data (no login needed)
37
+ zalify login # 2. authorize; then: zalify workspace set <workspace>
38
+ zalify shop create acme # 3. live at acme.z1.shop in ~4 minutes
39
+ cd stores/acme && zalify brand push --prune
40
+ zalify brand images generate && zalify brand images attach
41
+ zalify theme status # 6. stay current; `theme upgrade` keeps your edits
38
42
  ```
39
43
 
40
- `push` writes `<dir>/images/assets.json` — a committed index mapping each
41
- file to its asset id, public URL, and checksum. `pull` restores the binaries
42
- on a fresh clone, so image masters stay out of git.
44
+ ## Authentication
43
45
 
44
- ## Commands
46
+ ```bash
47
+ zalify login # opens the browser: authorize once
48
+ zalify login --app-url http://localhost:3000 # against a local app.zalify.com
49
+ zalify whoami # workspace, plan, and key details
50
+ zalify logout # delete local credentials
51
+ ```
45
52
 
46
- - **login** `[--app-url <url>]` Browser-based authorization; stores a workspace API key locally.
47
- - **logout** Deletes `~/.zalify/config.json` (revoke the key itself in Settings → Developer).
48
- - **whoami** Shows the authorized workspace, app origin, and key prefix.
49
- - **assets push `<dir>`** – Uploads `<dir>/images/*.png` via presigned R2 PUTs; sha256 dedup; updates `assets.json`.
50
- - **assets pull `<dir>`** Downloads files listed in `<dir>/images/assets.json` that are missing locally.
51
- - **version** Prints `Zalify CLI version: x.x.x` and, if a newer version exists on npm, suggests running `npm i -g @zalify/cli@latest` to update.
53
+ `login` runs a loopback flow (like `gh auth login`): the CLI listens on a
54
+ random localhost port, the browser opens `app.zalify.com/cli/auth`, and on
55
+ approval a **user key covering all your workspaces** is handed back and
56
+ stored in `~/.zalify/config.json` (0600). Keys are ordinary Zalify Public
57
+ API keys list and revoke them in **Settings Developer**. One login
58
+ covers every workspace; `zalify workspace set` switches between them
59
+ locally, no re-login.
60
+
61
+ ## Command reference
62
+
63
+ ### Auth & workspace
64
+
65
+ | Command | What it does |
66
+ | --- | --- |
67
+ | `login [--app-url <url>]` | Browser-based authorization; stores a user key locally |
68
+ | `logout` | Deletes `~/.zalify/config.json` (revoke the key itself in Settings → Developer) |
69
+ | `whoami` | Verifies the stored key; shows workspace, plan, app origin, key prefix |
70
+ | `workspace` / `workspace list` | List authorized workspaces (`*` = active) |
71
+ | `workspace set <slug-or-id>` | Switch the active workspace (local, no re-login) |
72
+
73
+ ### `brand` — the data
74
+
75
+ | Command | What it does |
76
+ | --- | --- |
77
+ | `brand init [dir] [--name <name>] [--vertical <v>] [--currency <code>]` | Scaffold a brand folder: catalog, voice guide, image manifest, `AUTHORING.md` rules. No login needed |
78
+ | `brand validate [dir]` | Lint the brand folder against the authoring rules |
79
+ | `brand push [dir] [--prune] [--shop <slug>]` | Push `catalog.json` to the connected Shopify store — products, smart collections, publish; idempotent. `--prune` deletes store products (by catalog vendor) absent from the catalog; `--shop` narrows the post-push refresh to one shop |
80
+ | `brand images generate [dir]` | Generate the manifest's missing images on Zalify infrastructure (resumable) |
81
+ | `brand images attach [dir] [--shop <slug>]` | Attach generated images to their Shopify products per the manifest (skips already-attached) |
82
+ | `brand images push [dir]` | Upload image masters to the Zalify asset library — presigned R2 PUTs, sha256 dedup; writes `images/assets.json`, the committed index that keeps binaries out of git |
83
+ | `brand images pull [dir]` | Download masters listed in `images/assets.json` that are missing locally (fresh-clone restore) |
84
+
85
+ ### `shop` — the hosted shop
86
+
87
+ | Command | What it does |
88
+ | --- | --- |
89
+ | `shop create <dir> [-t <theme>] [--editor] [--to <version>] [--no-install] [--no-git]` | One-command launch: provision repo + deployment + domain + storefront token, scaffold locally, push, go live at `<slug>.z1.shop`. `--editor` wires in the Zalify canvas editor (z1) |
90
+ | `shop` / `shop list` | List the workspace's hosted shops |
91
+ | `shop refresh [slug-or-dir]` | Refresh the live shop's data cache (defaults to the shop linked to cwd) |
92
+ | `shop open [slug-or-dir]` | Open the live shop in the browser (defaults to the shop linked to cwd) |
93
+ | `shop domain-sync [slug-or-dir]` | Re-point the shop at the current root domain (the old domain 308-redirects) |
94
+
95
+ `site …` remains as a hidden alias for one release and prints a pointer to
96
+ its new home under `shop`.
97
+
98
+ ### `ads` — the ad ops
99
+
100
+ | Command | What it does |
101
+ | --- | --- |
102
+ | `ads create <dir>` | Provision an Autopilot brand-instance GitHub repo (POST `/api/repos`, GitHub App), scaffold the starter locally (`identity.yaml`, `brand.yaml`, `policy/`, `campaigns/`, `media-plans/`, `signals/`, `decisions/`), and push with a short-lived repo-scoped token. Writes `.zalify/brand-repo.json` so later commands run arg-free. The directory name becomes the slug — use the workspace's store slug so the service's digest writer can find the repo |
103
+ | `ads` / `ads list` | List the workspace's brand instances and their provisioning status |
104
+
105
+ The engine commands — `plan` / `apply` / `creative` / `report` — join this
106
+ group as `@zalify/ads-engine` merges in. Until then, drafting and applying
107
+ happen on the Structure canvas in app.zalify.com; the instance repo is the
108
+ durable intent layer that work lands in.
109
+
110
+ ### `theme` — the code
111
+
112
+ | Command | What it does |
113
+ | --- | --- |
114
+ | `theme create <dir> [-t <template>] [--editor] [--store-domain <domain>] [--storefront-token <token>] [--to <version>] [--no-install] [--no-git] [--tarball <path>]` | Scaffold only — no Zalify hosting (the self-host path; `shop create` is the fully-hosted launch). Templates: `liquid` \| `hydrogen` \| `nextjs`. Without `--store-domain` it runs on mock.shop demo data |
115
+ | `theme status [dir]` | Which theme files have you edited? Checksums vs `.zalify/theme.json` |
116
+ | `theme upgrade [dir] [--to <version>] [--dry-run] [--install] [--tarball <path>] [--base-tarball <path>]` | Move to a newer theme version: untouched files overwritten, edited files 3-way-merged, `theme/` + `.env` never touched. `--dry-run` classifies and reports without writing |
117
+
118
+ ### Maintenance
119
+
120
+ | Command | What it does |
121
+ | --- | --- |
122
+ | `version` | Print the CLI version and check npm for a newer one |
123
+ | `self-update` | Update the CLI to the latest version (auto-detects pnpm/bun/npm) |
124
+
125
+ Every invocation also runs a passive, non-blocking update check and prints
126
+ a one-line notice when a newer version exists.
52
127
 
53
128
  ## Development
54
129
 
@@ -76,8 +151,6 @@ Release flow:
76
151
 
77
152
  ```bash
78
153
  npm version minor --no-git-tag-version # or patch/major
79
- git commit -am "chore: release v0.x.0" && git push
154
+ # bump zalify/package.json in step and pin its @zalify/cli dependency
155
+ git commit -am "0.x.0" && git push
80
156
  ```
81
-
82
- `bun run release` (bump + local `npm publish`, needs `NODE_AUTH_TOKEN` in
83
- `.env`) remains as a manual fallback if CI is unavailable.
package/dist/cli.js CHANGED
@@ -11293,7 +11293,7 @@ function updateNotifier(options) {
11293
11293
 
11294
11294
  // src/cli.ts
11295
11295
  import { createRequire as createRequire2 } from "node:module";
11296
- import { dirname as dirname2, join as join9 } from "node:path";
11296
+ import { dirname as dirname2, join as join10 } from "node:path";
11297
11297
  import { fileURLToPath as fileURLToPath3 } from "node:url";
11298
11298
 
11299
11299
  // src/auth.ts
@@ -11762,37 +11762,123 @@ async function assetsPush(storeDir) {
11762
11762
 
11763
11763
  // src/self-update.ts
11764
11764
  import { spawnSync } from "node:child_process";
11765
- import { realpathSync } from "node:fs";
11766
- function detectManager() {
11767
- let binPath = process.argv[1] ?? "";
11765
+ import { existsSync as existsSync3, realpathSync } from "node:fs";
11766
+ import { delimiter, join as join3 } from "node:path";
11767
+ var PACKAGES = ["zalify", "@zalify/cli"];
11768
+ var real = (p) => {
11768
11769
  try {
11769
- binPath = realpathSync(binPath);
11770
- } catch {}
11770
+ return realpathSync(p);
11771
+ } catch {
11772
+ return p;
11773
+ }
11774
+ };
11775
+ function managerFor(binPath) {
11771
11776
  if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
11772
- return { cmd: "pnpm", args: ["add", "-g", "@zalify/cli@latest"] };
11777
+ return {
11778
+ cmd: "pnpm",
11779
+ update: (spec) => ["add", "-g", spec],
11780
+ remove: `pnpm rm -g ${PACKAGES.join(" ")}`
11781
+ };
11773
11782
  }
11774
11783
  if (binPath.includes("/.bun/")) {
11775
- return { cmd: "bun", args: ["add", "-g", "@zalify/cli@latest"] };
11784
+ return {
11785
+ cmd: "bun",
11786
+ update: (spec) => ["add", "-g", spec],
11787
+ remove: `bun remove -g ${PACKAGES.join(" ")}`
11788
+ };
11776
11789
  }
11777
- return { cmd: "npm", args: ["install", "-g", "@zalify/cli@latest"] };
11790
+ return {
11791
+ cmd: "npm",
11792
+ update: (spec) => ["install", "-g", spec],
11793
+ remove: `npm rm -g ${PACKAGES.join(" ")}`
11794
+ };
11795
+ }
11796
+ function packageFor(binPath) {
11797
+ return /\/node_modules\/zalify\//.test(binPath) || /\/zalify@\d/.test(binPath) ? "zalify" : "@zalify/cli";
11798
+ }
11799
+ function zalifysOnPath() {
11800
+ const hits = [];
11801
+ for (const dir2 of (process.env.PATH ?? "").split(delimiter)) {
11802
+ if (!dir2)
11803
+ continue;
11804
+ const candidate = join3(dir2, "zalify");
11805
+ if (existsSync3(candidate) && !hits.includes(candidate)) {
11806
+ hits.push(candidate);
11807
+ }
11808
+ }
11809
+ return hits;
11778
11810
  }
11779
- function selfUpdate(currentVersion) {
11780
- const { cmd, args } = detectManager();
11811
+ function versionOf(bin) {
11812
+ const res = spawnSync(bin, ["--version"], { encoding: "utf8" });
11813
+ if (res.error || res.status !== 0)
11814
+ return null;
11815
+ return res.stdout?.trim() || null;
11816
+ }
11817
+ var HASH_HINT = "If `zalify -v` still shows the old version in this terminal, the shell\n" + "has cached the old location — run `hash -r` (zsh/bash) or open a new\n" + "terminal. A CLI cannot refresh its parent shell for you.";
11818
+ async function selfUpdate(currentVersion) {
11819
+ const running = real(process.argv[1] ?? "");
11820
+ const manager = managerFor(running);
11821
+ const pkg = packageFor(running);
11822
+ let latest = null;
11823
+ try {
11824
+ const info = await updateNotifier({
11825
+ pkg: { name: "@zalify/cli", version: currentVersion }
11826
+ }).fetchInfo();
11827
+ latest = info?.latest ?? null;
11828
+ } catch {}
11829
+ const target = latest ? `${pkg}@${latest}` : `${pkg}@latest`;
11781
11830
  console.log(`Current version: ${currentVersion}`);
11782
- console.log(`Updating via: ${cmd} ${args.join(" ")}
11831
+ console.log(`Updating via: ${manager.cmd} ${manager.update(target).join(" ")}
11783
11832
  `);
11784
- const result = spawnSync(cmd, args, { stdio: "inherit" });
11833
+ const result = spawnSync(manager.cmd, manager.update(target), {
11834
+ stdio: "inherit"
11835
+ });
11785
11836
  if (result.error || result.status !== 0) {
11786
11837
  console.error(`
11787
11838
  Update failed${result.error ? `: ${result.error.message}` : ""}.` + `
11788
- Try manually: ${cmd} ${args.join(" ")}`);
11839
+ Try manually: ${manager.cmd} ${manager.update(target).join(" ")}`);
11789
11840
  process.exit(result.status ?? 1);
11790
11841
  }
11791
- const check = spawnSync("zalify", ["--version"], { encoding: "utf8" });
11792
- const updated = check.stdout?.trim();
11793
- console.log(updated && updated !== currentVersion ? `
11794
- Updated ${currentVersion} ${updated}` : `
11795
- Done${updated ? ` (version: ${updated})` : ""}`);
11842
+ const hits = zalifysOnPath();
11843
+ const first = hits[0];
11844
+ const firstVersion = first ? versionOf(first) : null;
11845
+ const mayBeHashed = hits.length > 1 || first && real(first) !== running;
11846
+ if (!first || !firstVersion) {
11847
+ console.log(`
11848
+ ✓ Done`);
11849
+ return;
11850
+ }
11851
+ const reachedLatest = latest ? firstVersion === latest : firstVersion !== currentVersion;
11852
+ if (reachedLatest) {
11853
+ console.log(firstVersion === currentVersion ? `
11854
+ ✓ Already the latest (${firstVersion})` : `
11855
+ ✓ Updated ${currentVersion} → ${firstVersion}`);
11856
+ if (mayBeHashed)
11857
+ console.log(`
11858
+ ${HASH_HINT}`);
11859
+ return;
11860
+ }
11861
+ if (latest) {
11862
+ console.log(`
11863
+ ✗ ${pkg} is now ${latest}, but your PATH still runs`);
11864
+ console.log(` ${first} (${firstVersion})`);
11865
+ if (hits.length > 1) {
11866
+ console.log(`
11867
+ Every zalify on your PATH:`);
11868
+ for (const hit of hits) {
11869
+ console.log(` ${hit} → ${versionOf(hit) ?? "unreadable"}`);
11870
+ }
11871
+ }
11872
+ console.log(`
11873
+ Remove the stale install (${managerFor(real(first)).remove}),`);
11874
+ console.log("then run `hash -r` or open a new terminal.");
11875
+ process.exit(1);
11876
+ }
11877
+ console.log(`
11878
+ ✓ Done (version: ${firstVersion})`);
11879
+ if (mayBeHashed)
11880
+ console.log(`
11881
+ ${HASH_HINT}`);
11796
11882
  }
11797
11883
 
11798
11884
  // src/workspace.ts
@@ -11867,8 +11953,8 @@ async function workspaceSet(slugOrId) {
11867
11953
  }
11868
11954
 
11869
11955
  // src/images.ts
11870
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync3, rmSync as rmSync2 } from "node:fs";
11871
- import { join as join3, resolve as resolve2 } from "node:path";
11956
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync4, rmSync as rmSync2 } from "node:fs";
11957
+ import { join as join4, resolve as resolve2 } from "node:path";
11872
11958
  var MAX_JOBS_PER_REQUEST = 40;
11873
11959
  var POLL_INTERVAL_MS = 5000;
11874
11960
  var POLL_TIMEOUT_MS = 30 * 60 * 1000;
@@ -11896,7 +11982,7 @@ async function saveProduced(imagesDir, indexPath, index, file2, asset) {
11896
11982
  return false;
11897
11983
  }
11898
11984
  const bytes = Buffer.from(await img.arrayBuffer());
11899
- writeFileSync4(join3(imagesDir, file2), bytes);
11985
+ writeFileSync4(join4(imagesDir, file2), bytes);
11900
11986
  index[file2] = {
11901
11987
  assetId: asset.id,
11902
11988
  url: asset.url,
@@ -12033,17 +12119,17 @@ async function streamingGenerate(auth, manifest, missing, imagesDir, indexPath,
12033
12119
  }
12034
12120
  async function imagesGenerate(storeDir) {
12035
12121
  const auth = requireActive();
12036
- const imagesDir = join3(resolve2(storeDir), "images");
12037
- const manifestPath = join3(imagesDir, "manifest.json");
12038
- if (!existsSync3(manifestPath)) {
12122
+ const imagesDir = join4(resolve2(storeDir), "images");
12123
+ const manifestPath = join4(imagesDir, "manifest.json");
12124
+ if (!existsSync4(manifestPath)) {
12039
12125
  throw new Error(`No images/manifest.json in ${storeDir}`);
12040
12126
  }
12041
12127
  const manifest = JSON.parse(readFileSync3(manifestPath, "utf8"));
12042
- const indexPath = join3(imagesDir, "assets.json");
12043
- const index = existsSync3(indexPath) ? JSON.parse(readFileSync3(indexPath, "utf8")) : {};
12044
- const jobStatePath = join3(imagesDir, ".imagegen-job.json");
12128
+ const indexPath = join4(imagesDir, "assets.json");
12129
+ const index = existsSync4(indexPath) ? JSON.parse(readFileSync3(indexPath, "utf8")) : {};
12130
+ const jobStatePath = join4(imagesDir, ".imagegen-job.json");
12045
12131
  const counters = { produced: 0, failed: 0 };
12046
- if (existsSync3(jobStatePath)) {
12132
+ if (existsSync4(jobStatePath)) {
12047
12133
  const state = JSON.parse(readFileSync3(jobStatePath, "utf8"));
12048
12134
  console.log(`Resuming job ${state.jobId}`);
12049
12135
  await pollJob(auth, state.jobId, state.files, imagesDir, indexPath, index, counters);
@@ -12051,7 +12137,7 @@ async function imagesGenerate(storeDir) {
12051
12137
  console.log(`Done: ${counters.produced} generated (already in the asset library), ${counters.failed} failed${counters.failed ? " — re-run to retry" : ""}.`);
12052
12138
  return;
12053
12139
  }
12054
- const missing = manifest.images.filter((e) => !existsSync3(join3(imagesDir, e.file)));
12140
+ const missing = manifest.images.filter((e) => !existsSync4(join4(imagesDir, e.file)));
12055
12141
  if (missing.length === 0) {
12056
12142
  console.log("All manifest images already exist locally.");
12057
12143
  return;
@@ -12075,20 +12161,20 @@ async function imagesGenerate(storeDir) {
12075
12161
  }
12076
12162
 
12077
12163
  // src/shopify.ts
12078
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "node:fs";
12079
- import { basename as basename3, join as join6, resolve as resolve5 } from "node:path";
12164
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "node:fs";
12165
+ import { basename as basename3, join as join7, resolve as resolve5 } from "node:path";
12080
12166
 
12081
12167
  // src/shop.ts
12082
12168
  import { spawn as spawn3, spawnSync as spawnSync3 } from "node:child_process";
12083
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
12084
- import { basename as basename2, join as join5, resolve as resolve4 } from "node:path";
12169
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
12170
+ import { basename as basename2, join as join6, resolve as resolve4 } from "node:path";
12085
12171
 
12086
12172
  // src/theme.ts
12087
12173
  import { spawnSync as spawnSync2 } from "node:child_process";
12088
12174
  import { randomBytes as randomBytes2 } from "node:crypto";
12089
12175
  import {
12090
12176
  cpSync,
12091
- existsSync as existsSync4,
12177
+ existsSync as existsSync5,
12092
12178
  mkdirSync as mkdirSync2,
12093
12179
  mkdtempSync,
12094
12180
  readdirSync as readdirSync2,
@@ -12100,7 +12186,7 @@ import {
12100
12186
  writeFileSync as writeFileSync5
12101
12187
  } from "node:fs";
12102
12188
  import { tmpdir } from "node:os";
12103
- import { basename, dirname, join as join4, resolve as resolve3 } from "node:path";
12189
+ import { basename, dirname, join as join5, resolve as resolve3 } from "node:path";
12104
12190
  var TEMPLATES_PKG = "@zalify/theme-templates";
12105
12191
  var REGISTRY = "https://registry.npmjs.org";
12106
12192
  var MANIFEST_PATH = ".zalify/theme.json";
@@ -12126,27 +12212,27 @@ async function downloadTarball(url) {
12126
12212
  const res = await fetch(url);
12127
12213
  if (!res.ok)
12128
12214
  throw new Error(`tarball download ${res.status}: ${url}`);
12129
- const file2 = join4(mkdtempSync(join4(tmpdir(), "zalify-theme-")), "pkg.tgz");
12215
+ const file2 = join5(mkdtempSync(join5(tmpdir(), "zalify-theme-")), "pkg.tgz");
12130
12216
  writeFileSync5(file2, Buffer.from(await res.arrayBuffer()));
12131
12217
  return file2;
12132
12218
  }
12133
12219
  function extractTarball(file2) {
12134
- const dir2 = mkdtempSync(join4(tmpdir(), "zalify-theme-x-"));
12220
+ const dir2 = mkdtempSync(join5(tmpdir(), "zalify-theme-x-"));
12135
12221
  const result = spawnSync2("tar", ["-xzf", resolve3(file2), "-C", dir2], {
12136
12222
  encoding: "utf8"
12137
12223
  });
12138
12224
  if (result.error || result.status !== 0) {
12139
12225
  throw new Error(`tar extract failed for ${file2}: ${result.stderr || result.error?.message}`);
12140
12226
  }
12141
- const root = join4(dir2, "package");
12142
- if (!existsSync4(root))
12227
+ const root = join5(dir2, "package");
12228
+ if (!existsSync5(root))
12143
12229
  throw new Error(`unexpected tarball layout in ${file2} (no package/)`);
12144
12230
  return root;
12145
12231
  }
12146
12232
  async function acquireTemplatePkg(localTarball, version) {
12147
12233
  if (localTarball) {
12148
12234
  const root = extractTarball(localTarball);
12149
- const v = JSON.parse(readFileSync4(join4(root, "package.json"), "utf8")).version;
12235
+ const v = JSON.parse(readFileSync4(join5(root, "package.json"), "utf8")).version;
12150
12236
  return { root, version: v };
12151
12237
  }
12152
12238
  const packument = await fetchPackument();
@@ -12157,8 +12243,8 @@ async function acquireTemplatePkg(localTarball, version) {
12157
12243
  return { root: extractTarball(await downloadTarball(entry.dist.tarball)), version: target };
12158
12244
  }
12159
12245
  function readTemplateManifest(pkgRoot, template) {
12160
- const path9 = join4(pkgRoot, "templates", template, MANIFEST_PATH);
12161
- if (!existsSync4(path9)) {
12246
+ const path9 = join5(pkgRoot, "templates", template, MANIFEST_PATH);
12247
+ if (!existsSync5(path9)) {
12162
12248
  throw new Error(`template "${template}" has no ${MANIFEST_PATH} — this ${TEMPLATES_PKG} version predates the upgrade contract`);
12163
12249
  }
12164
12250
  return JSON.parse(readFileSync4(path9, "utf8"));
@@ -12175,21 +12261,21 @@ function templateFilePath(templateDir, rel, variant) {
12175
12261
  const stashed = rel.replace(/(^|\/)\.gitignore$/, "$1gitignore");
12176
12262
  if (variant === "editor") {
12177
12263
  for (const candidate of [rel, stashed]) {
12178
- const overlay = join4(templateDir, "__editor__", candidate);
12179
- if (existsSync4(overlay))
12264
+ const overlay = join5(templateDir, "__editor__", candidate);
12265
+ if (existsSync5(overlay))
12180
12266
  return overlay;
12181
12267
  }
12182
12268
  }
12183
12269
  for (const candidate of [rel, stashed]) {
12184
- const path9 = join4(templateDir, candidate);
12185
- if (existsSync4(path9))
12270
+ const path9 = join5(templateDir, candidate);
12271
+ if (existsSync5(path9))
12186
12272
  return path9;
12187
12273
  }
12188
12274
  throw new Error(`file listed in manifest but missing from template: ${rel}`);
12189
12275
  }
12190
12276
  function readProjectManifest(dir2) {
12191
- const path9 = join4(dir2, MANIFEST_PATH);
12192
- if (!existsSync4(path9)) {
12277
+ const path9 = join5(dir2, MANIFEST_PATH);
12278
+ if (!existsSync5(path9)) {
12193
12279
  throw new Error(`no ${MANIFEST_PATH} in ${dir2} — not a scaffolded Zalify storefront.
12194
12280
  ` + ` Scaffold one with \`zalify theme create\`.`);
12195
12281
  }
@@ -12201,22 +12287,22 @@ function writeJson(path9, value) {
12201
12287
  }
12202
12288
  async function themeCreate(dir2, opts) {
12203
12289
  const targetDir = resolve3(dir2);
12204
- if (existsSync4(targetDir) && readdirSync2(targetDir).length > 0) {
12290
+ if (existsSync5(targetDir) && readdirSync2(targetDir).length > 0) {
12205
12291
  throw new Error(`${dir2} already exists and is not empty`);
12206
12292
  }
12207
12293
  const { root, version } = await acquireTemplatePkg(opts.tarball, opts.to);
12208
- const frameworks = JSON.parse(readFileSync4(join4(root, "templates.json"), "utf8"));
12294
+ const frameworks = JSON.parse(readFileSync4(join5(root, "templates.json"), "utf8"));
12209
12295
  const template = opts.template ?? "nextjs";
12210
12296
  const meta = frameworks.frameworks[template];
12211
12297
  if (!meta) {
12212
12298
  throw new Error(`unknown template "${template}" — expected one of: ${Object.keys(frameworks.frameworks).join(", ")}`);
12213
12299
  }
12214
- const templateDir = join4(root, "templates", template);
12300
+ const templateDir = join5(root, "templates", template);
12215
12301
  console.log(`Scaffolding ${meta.label} (${TEMPLATES_PKG}@${version}) into ${dir2}`);
12216
12302
  mkdirSync2(targetDir, { recursive: true });
12217
12303
  cpSync(templateDir, targetDir, { recursive: true });
12218
- const overlayDir = join4(targetDir, "__editor__");
12219
- const useEditor = Boolean(opts.editor) && existsSync4(overlayDir);
12304
+ const overlayDir = join5(targetDir, "__editor__");
12305
+ const useEditor = Boolean(opts.editor) && existsSync5(overlayDir);
12220
12306
  if (opts.editor && !useEditor) {
12221
12307
  console.log(` ! --editor has no effect for ${template} — no editor overlay in this template`);
12222
12308
  }
@@ -12225,14 +12311,14 @@ async function themeCreate(dir2, opts) {
12225
12311
  rmSync3(overlayDir, { recursive: true, force: true });
12226
12312
  (function restoreGitignores(d) {
12227
12313
  for (const entry of readdirSync2(d)) {
12228
- const full = join4(d, entry);
12314
+ const full = join5(d, entry);
12229
12315
  if (statSync(full).isDirectory())
12230
12316
  restoreGitignores(full);
12231
12317
  else if (entry === "gitignore")
12232
- renameSync(full, join4(d, ".gitignore"));
12318
+ renameSync(full, join5(d, ".gitignore"));
12233
12319
  }
12234
12320
  })(targetDir);
12235
- const pkgPath = join4(targetDir, "package.json");
12321
+ const pkgPath = join5(targetDir, "package.json");
12236
12322
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
12237
12323
  pkg.name = basename(targetDir).toLowerCase().replaceAll(/[^a-z0-9-_.]+/g, "-").replaceAll(/^[-.]+|[-.]+$/g, "") || "zalify-storefront";
12238
12324
  writeJson(pkgPath, pkg);
@@ -12246,12 +12332,12 @@ async function themeCreate(dir2, opts) {
12246
12332
  if (meta.env.sessionSecret) {
12247
12333
  lines.push("# Session cookie signing key — unique to this project.", `${meta.env.sessionSecret}=${randomBytes2(32).toString("hex")}`);
12248
12334
  }
12249
- writeFileSync5(join4(targetDir, ".env"), lines.join(`
12335
+ writeFileSync5(join5(targetDir, ".env"), lines.join(`
12250
12336
  `) + `
12251
12337
  `);
12252
12338
  }
12253
12339
  const manifest = readProjectManifest(targetDir);
12254
- writeJson(join4(targetDir, MANIFEST_PATH), resolveVariant(manifest, useEditor ? "editor" : "default"));
12340
+ writeJson(join5(targetDir, MANIFEST_PATH), resolveVariant(manifest, useEditor ? "editor" : "default"));
12255
12341
  if (opts.git !== false) {
12256
12342
  const git = (args) => spawnSync2("git", args, { cwd: targetDir, stdio: "ignore" });
12257
12343
  if (git(["init", "-b", "main"]).status === 0) {
@@ -12291,8 +12377,8 @@ function themeStatus(dir2 = ".") {
12291
12377
  const edited = [];
12292
12378
  const missing = [];
12293
12379
  for (const [rel, expected] of Object.entries(manifest.files)) {
12294
- const full = join4(projectDir, rel);
12295
- if (!existsSync4(full))
12380
+ const full = join5(projectDir, rel);
12381
+ if (!existsSync5(full))
12296
12382
  missing.push(rel);
12297
12383
  else if (sha256(readFileSync4(full)) !== expected)
12298
12384
  edited.push(rel);
@@ -12326,8 +12412,8 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12326
12412
  }
12327
12413
  const oldManifest = resolveVariant(readTemplateManifest(base.root, local.template), variant);
12328
12414
  const newManifest = resolveVariant(readTemplateManifest(target.root, local.template), variant);
12329
- const oldDir = join4(base.root, "templates", local.template);
12330
- const newDir = join4(target.root, "templates", local.template);
12415
+ const oldDir = join5(base.root, "templates", local.template);
12416
+ const newDir = join5(target.root, "templates", local.template);
12331
12417
  console.log(`Upgrading ${local.template} theme ${local.version} → ${target.version}` + (opts.dryRun ? " (dry run)" : ""));
12332
12418
  const counts = {
12333
12419
  overwrite: 0,
@@ -12345,8 +12431,8 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12345
12431
  continue;
12346
12432
  const oldHash = oldManifest.files[rel];
12347
12433
  const newHash = newManifest.files[rel];
12348
- const localPath = join4(projectDir, rel);
12349
- const localExists = existsSync4(localPath);
12434
+ const localPath = join5(projectDir, rel);
12435
+ const localExists = existsSync5(localPath);
12350
12436
  const localBytes = localExists ? readFileSync4(localPath) : null;
12351
12437
  const localHash = localBytes ? sha256(localBytes) : null;
12352
12438
  const write = (bytes) => {
@@ -12385,9 +12471,9 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12385
12471
  console.log(` ~ would merge ${rel}`);
12386
12472
  continue;
12387
12473
  }
12388
- const scratch = mkdtempSync(join4(tmpdir(), "zalify-merge-"));
12389
- const basePath = join4(scratch, "base");
12390
- const newPath = join4(scratch, "new");
12474
+ const scratch = mkdtempSync(join5(tmpdir(), "zalify-merge-"));
12475
+ const basePath = join5(scratch, "base");
12476
+ const newPath = join5(scratch, "new");
12391
12477
  writeFileSync5(basePath, oldBytes);
12392
12478
  writeFileSync5(newPath, newBytes);
12393
12479
  const result = spawnSync2("git", ["merge-file", "-L", "current", "-L", `base (${base.version})`, "-L", `new (${target.version})`, localPath, basePath, newPath], { encoding: "utf8" });
@@ -12431,7 +12517,7 @@ async function themeUpgrade(dir2 = ".", opts = {}) {
12431
12517
  }
12432
12518
  const depsChanged = mergePackageJson(projectDir, oldDir, newDir, opts.dryRun ?? false);
12433
12519
  if (!opts.dryRun) {
12434
- writeJson(join4(projectDir, MANIFEST_PATH), { ...newManifest, version: target.version });
12520
+ writeJson(join5(projectDir, MANIFEST_PATH), { ...newManifest, version: target.version });
12435
12521
  }
12436
12522
  const summary = Object.entries(counts).filter(([, n]) => n > 0).map(([k, n]) => `${n} ${k.replace("-", " ")}`).join(", ");
12437
12523
  console.log(`
@@ -12452,7 +12538,7 @@ ${opts.dryRun ? "Would apply" : "Done"}: ${summary || "no file changes"}.`);
12452
12538
  }
12453
12539
  }
12454
12540
  function mergePackageJson(projectDir, oldDir, newDir, dryRun) {
12455
- const read = (p) => JSON.parse(readFileSync4(join4(p, "package.json"), "utf8"));
12541
+ const read = (p) => JSON.parse(readFileSync4(join5(p, "package.json"), "utf8"));
12456
12542
  const localPkg = read(projectDir);
12457
12543
  const oldPkg = read(oldDir);
12458
12544
  const newPkg = read(newDir);
@@ -12501,7 +12587,7 @@ function mergePackageJson(projectDir, oldDir, newDir, dryRun) {
12501
12587
  console.log(` ✓ package.json ${key} updated`);
12502
12588
  }
12503
12589
  if (changed && !dryRun) {
12504
- writeFileSync5(join4(projectDir, "package.json"), JSON.stringify(localPkg, null, 2) + `
12590
+ writeFileSync5(join5(projectDir, "package.json"), JSON.stringify(localPkg, null, 2) + `
12505
12591
  `);
12506
12592
  }
12507
12593
  return depsChanged;
@@ -12597,9 +12683,9 @@ async function shopCreate(dir2, opts) {
12597
12683
  install: opts.install,
12598
12684
  git: opts.git
12599
12685
  });
12600
- writeFileSync6(join5(resolve4(dir2), ".zalify", "site.json"), JSON.stringify({ id: siteId, slug: view.slug, domain: view.domain, repo: view.githubRepo }, null, 2) + `
12686
+ writeFileSync6(join6(resolve4(dir2), ".zalify", "site.json"), JSON.stringify({ id: siteId, slug: view.slug, domain: view.domain, repo: view.githubRepo }, null, 2) + `
12601
12687
  `);
12602
- writeFileSync6(join5(resolve4(dir2), "theme", "pixel.json"), JSON.stringify({ workspaceId: auth.workspaceId }, null, 2) + `
12688
+ writeFileSync6(join6(resolve4(dir2), "theme", "pixel.json"), JSON.stringify({ workspaceId: auth.workspaceId }, null, 2) + `
12603
12689
  `);
12604
12690
  if (opts.git !== false && view.githubRepo) {
12605
12691
  const { token, repo } = await request(auth, "POST", `/api/sites/${siteId}/push-token`, {});
@@ -12640,14 +12726,14 @@ Next steps:
12640
12726
  }
12641
12727
  async function resolveSite(auth, slugOrDir) {
12642
12728
  const fromDir = (dir2) => {
12643
- const link = join5(dir2, ".zalify", "site.json");
12644
- if (existsSync5(link)) {
12729
+ const link = join6(dir2, ".zalify", "site.json");
12730
+ if (existsSync6(link)) {
12645
12731
  const parsed = JSON.parse(readFileSync5(link, "utf8"));
12646
12732
  if (parsed.slug)
12647
12733
  return parsed.slug;
12648
12734
  }
12649
- const storeJson = join5(dir2, "store.json");
12650
- if (existsSync5(storeJson)) {
12735
+ const storeJson = join6(dir2, "store.json");
12736
+ if (existsSync6(storeJson)) {
12651
12737
  const parsed = JSON.parse(readFileSync5(storeJson, "utf8"));
12652
12738
  if (parsed.slug)
12653
12739
  return parsed.slug;
@@ -12655,7 +12741,7 @@ async function resolveSite(auth, slugOrDir) {
12655
12741
  return null;
12656
12742
  };
12657
12743
  let slug = null;
12658
- if (slugOrDir && existsSync5(resolve4(slugOrDir)))
12744
+ if (slugOrDir && existsSync6(resolve4(slugOrDir)))
12659
12745
  slug = fromDir(resolve4(slugOrDir));
12660
12746
  if (!slug && slugOrDir)
12661
12747
  slug = slugOrDir;
@@ -12716,8 +12802,8 @@ async function maybeRefreshShops(auth, brandDir, siteSlugOverride) {
12716
12802
  try {
12717
12803
  let slug = siteSlugOverride ?? null;
12718
12804
  if (!slug) {
12719
- const storeJson = join5(resolve4(brandDir), "store.json");
12720
- if (existsSync5(storeJson)) {
12805
+ const storeJson = join6(resolve4(brandDir), "store.json");
12806
+ if (existsSync6(storeJson)) {
12721
12807
  slug = JSON.parse(readFileSync5(storeJson, "utf8")).slug ?? null;
12722
12808
  }
12723
12809
  }
@@ -12772,7 +12858,7 @@ async function gql(auth, query, variables = {}) {
12772
12858
  return json.data;
12773
12859
  }
12774
12860
  function readJson(path9, label) {
12775
- if (!existsSync6(path9)) {
12861
+ if (!existsSync7(path9)) {
12776
12862
  throw new Error(`No ${label} at ${path9}`);
12777
12863
  }
12778
12864
  return JSON.parse(readFileSync6(path9, "utf8"));
@@ -12802,7 +12888,7 @@ async function findProductId(auth, handle) {
12802
12888
  async function shopifyImport(storeDir, options = {}) {
12803
12889
  const auth = requireActive();
12804
12890
  const dir2 = resolve5(storeDir);
12805
- const catalog = readJson(join6(dir2, "catalog.json"), "catalog.json");
12891
+ const catalog = readJson(join7(dir2, "catalog.json"), "catalog.json");
12806
12892
  const { locations } = await gql(auth, `{ locations(first: 1) { nodes { id name } } }`);
12807
12893
  const locationId = locations.nodes[0]?.id;
12808
12894
  console.log(`Importing ${catalog.products.length} product(s) into the Shopify store connected to ` + `workspace "${auth.workspaceName}" (location: ${locations.nodes[0]?.name ?? "none"})`);
@@ -13011,13 +13097,13 @@ async function shopifyImport(storeDir, options = {}) {
13011
13097
  async function shopifyUploadImages(storeDir, options = {}) {
13012
13098
  const auth = requireActive();
13013
13099
  const dir2 = resolve5(storeDir);
13014
- const manifest = readJson(join6(dir2, "images", "manifest.json"), "images/manifest.json");
13100
+ const manifest = readJson(join7(dir2, "images", "manifest.json"), "images/manifest.json");
13015
13101
  console.log(`Uploading images to the Shopify store connected to workspace "${auth.workspaceName}"`);
13016
13102
  for (const entry of manifest.images) {
13017
13103
  if (entry.type === "banner" || !entry.handle)
13018
13104
  continue;
13019
- const filePath = join6(dir2, "images", entry.file);
13020
- if (!existsSync6(filePath)) {
13105
+ const filePath = join7(dir2, "images", entry.file);
13106
+ if (!existsSync7(filePath)) {
13021
13107
  console.log(` - ${entry.handle}: ${entry.file} not generated yet, skipped`);
13022
13108
  continue;
13023
13109
  }
@@ -13092,7 +13178,7 @@ async function shopifyUploadImages(storeDir, options = {}) {
13092
13178
  // src/ads.ts
13093
13179
  import { spawnSync as spawnSync4 } from "node:child_process";
13094
13180
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync7 } from "node:fs";
13095
- import { basename as basename4, join as join7, resolve as resolve6 } from "node:path";
13181
+ import { basename as basename4, join as join8, resolve as resolve6 } from "node:path";
13096
13182
  function apiError3(status, json) {
13097
13183
  if (json.code === "SLUG_TAKEN") {
13098
13184
  return new Error(`${json.error}
@@ -13144,8 +13230,8 @@ async function pollUntilReady2(auth, id) {
13144
13230
  function scaffoldStarter(dir2, slug, workspaceId) {
13145
13231
  const root = resolve6(dir2);
13146
13232
  const write = (rel, content) => {
13147
- const abs = join7(root, rel);
13148
- mkdirSync3(join7(abs, ".."), { recursive: true });
13233
+ const abs = join8(root, rel);
13234
+ mkdirSync3(join8(abs, ".."), { recursive: true });
13149
13235
  writeFileSync7(abs, content);
13150
13236
  };
13151
13237
  write("identity.yaml", `# Brand instance identity — which workspace and which platform accounts
@@ -13202,8 +13288,8 @@ async function adsCreate(dir2) {
13202
13288
  const view = await pollUntilReady2(auth, created.repoId);
13203
13289
  console.log(` ✓ provisioned: ${view.githubRepo}`);
13204
13290
  scaffoldStarter(dir2, slug, auth.workspaceId);
13205
- mkdirSync3(join7(resolve6(dir2), ".zalify"), { recursive: true });
13206
- writeFileSync7(join7(resolve6(dir2), ".zalify", "brand-repo.json"), JSON.stringify({ id: created.repoId, slug: view.slug, repo: view.githubRepo }, null, 2) + `
13291
+ mkdirSync3(join8(resolve6(dir2), ".zalify"), { recursive: true });
13292
+ writeFileSync7(join8(resolve6(dir2), ".zalify", "brand-repo.json"), JSON.stringify({ id: created.repoId, slug: view.slug, repo: view.githubRepo }, null, 2) + `
13207
13293
  `);
13208
13294
  if (view.githubRepo) {
13209
13295
  const { token, repo } = await request2(auth, "POST", `/api/repos/${created.repoId}/push-token`, {});
@@ -13241,8 +13327,8 @@ async function adsList() {
13241
13327
  }
13242
13328
 
13243
13329
  // src/brand.ts
13244
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
13245
- import { basename as basename5, join as join8, resolve as resolve7 } from "node:path";
13330
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
13331
+ import { basename as basename5, join as join9, resolve as resolve7 } from "node:path";
13246
13332
  var AUTHORING_MD = `# Brand authoring guide
13247
13333
 
13248
13334
  The rules for filling in this folder. An author — human or AI agent —
@@ -13545,17 +13631,17 @@ var MANIFEST_JSON = JSON.stringify({
13545
13631
  function brandInit(dir2 = ".", opts = {}) {
13546
13632
  const target = resolve7(dir2);
13547
13633
  const slug = basename5(target).toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "");
13548
- if (existsSync7(target) && readdirSync3(target).length > 0) {
13634
+ if (existsSync8(target) && readdirSync3(target).length > 0) {
13549
13635
  throw new Error(`${dir2} already exists and is not empty`);
13550
13636
  }
13551
13637
  const name = opts.name ?? slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
13552
13638
  const currency = (opts.currency ?? "USD").toUpperCase();
13553
- mkdirSync4(join8(target, "images"), { recursive: true });
13554
- writeFileSync8(join8(target, "AUTHORING.md"), AUTHORING_MD);
13555
- writeFileSync8(join8(target, "brand.md"), BRAND_MD(name));
13556
- writeFileSync8(join8(target, "store.json"), STORE_JSON(name, slug, opts.vertical ?? "<vertical>", currency));
13557
- writeFileSync8(join8(target, "catalog.json"), CATALOG_JSON(name, currency));
13558
- writeFileSync8(join8(target, "images", "manifest.json"), MANIFEST_JSON);
13639
+ mkdirSync4(join9(target, "images"), { recursive: true });
13640
+ writeFileSync8(join9(target, "AUTHORING.md"), AUTHORING_MD);
13641
+ writeFileSync8(join9(target, "brand.md"), BRAND_MD(name));
13642
+ writeFileSync8(join9(target, "store.json"), STORE_JSON(name, slug, opts.vertical ?? "<vertical>", currency));
13643
+ writeFileSync8(join9(target, "catalog.json"), CATALOG_JSON(name, currency));
13644
+ writeFileSync8(join9(target, "images", "manifest.json"), MANIFEST_JSON);
13559
13645
  console.log(`Scaffolded brand data in ${dir2}:`);
13560
13646
  for (const f of ["AUTHORING.md", "brand.md", "store.json", "catalog.json", "images/manifest.json"]) {
13561
13647
  console.log(` + ${f}`);
@@ -13570,8 +13656,8 @@ function brandValidate(dir2 = ".") {
13570
13656
  const problems = [];
13571
13657
  const warnings = [];
13572
13658
  const readJson2 = (rel) => {
13573
- const path9 = join8(target, rel);
13574
- if (!existsSync7(path9)) {
13659
+ const path9 = join9(target, rel);
13660
+ if (!existsSync8(path9)) {
13575
13661
  problems.push(`missing ${rel}`);
13576
13662
  return null;
13577
13663
  }
@@ -13582,7 +13668,7 @@ function brandValidate(dir2 = ".") {
13582
13668
  return null;
13583
13669
  }
13584
13670
  };
13585
- if (!existsSync7(join8(target, "brand.md")))
13671
+ if (!existsSync8(join9(target, "brand.md")))
13586
13672
  problems.push("missing brand.md");
13587
13673
  const store = readJson2("store.json");
13588
13674
  if (store && (!store.name || !store.slug))
@@ -13758,7 +13844,7 @@ function brandValidate(dir2 = ".") {
13758
13844
  // src/cli.ts
13759
13845
  var __dirname4 = dirname2(fileURLToPath3(import.meta.url));
13760
13846
  var require2 = createRequire2(import.meta.url);
13761
- var pkg = require2(join9(__dirname4, "..", "package.json"));
13847
+ var pkg = require2(join10(__dirname4, "..", "package.json"));
13762
13848
  try {
13763
13849
  updateNotifier({ pkg }).notify({
13764
13850
  isGlobal: true,
@@ -13847,7 +13933,9 @@ theme.command("status [dir]").description("Which theme files have you edited? (c
13847
13933
  theme.command("upgrade [dir]").description("Move to a newer theme version: untouched files overwritten, edited files 3-way-merged, theme/ + .env never touched").option("--to <version>", "target theme version (default: latest)").option("--dry-run", "classify and report without writing anything").option("--install", "run install when dependencies changed").option("--tarball <path>", "target version as a local .tgz (testing)").option("--base-tarball <path>", "base version as a local .tgz (testing)").action(async (dir2, options) => {
13848
13934
  await themeUpgrade(dir2, options);
13849
13935
  });
13850
- program2.command("self-update").description("Update the CLI to the latest version (auto-detects pnpm/bun/npm)").action(() => selfUpdate(pkg.version));
13936
+ program2.command("self-update").description("Update the CLI to the latest version (auto-detects pnpm/bun/npm)").action(async () => {
13937
+ await selfUpdate(pkg.version);
13938
+ });
13851
13939
  program2.command("version").description("Print Zalify CLI version and check for updates").action(async () => {
13852
13940
  console.log(`Zalify CLI version: ${pkg.version}`);
13853
13941
  const isNewer = (a, b) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalify/cli",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Zalify CLI - command-line interface for Zalify",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",