@greatstore/cli 0.0.22 → 0.0.24

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/CHANGELOG.md +18 -0
  2. package/dist/cli.js +97 -206
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,24 @@
3
3
  All notable changes to `@greatstore/cli` are recorded here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
+ ## 0.0.24 — 2026-06-03
7
+
8
+ ### Changed
9
+ - `gs init` in an existing project now fills in any scaffold files that
10
+ are missing (for example, the `AGENTS.md` design guide added in
11
+ 0.0.23) and leaves your own files alone. Pass `--force` to refresh
12
+ every scaffold file to the latest version. Your pinned store
13
+ (`.gsrc`) is never rewritten either way.
14
+
15
+ ## 0.0.23 — 2026-06-03
16
+
17
+ ### Added
18
+ - `gs init` now scaffolds an `AGENTS.md` (with `CLAUDE.md` and
19
+ `GEMINI.md` symlinked to it) documenting the design rules every
20
+ component should follow — use `em` rather than `rem` for sizing, and
21
+ style from the provided brand CSS variables so components match the
22
+ store's theme. It doubles as guidance for AI coding agents.
23
+
6
24
  ## 0.0.22 — 2026-06-02
7
25
 
8
26
  ### Added
package/dist/cli.js CHANGED
@@ -1111,6 +1111,38 @@ function prompt(question) {
1111
1111
  // src/commands/init.ts
1112
1112
  import * as fs6 from "fs";
1113
1113
  import * as path6 from "path";
1114
+
1115
+ // src/template.ts
1116
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
1117
+ import { fileURLToPath } from "url";
1118
+ var cache = null;
1119
+ function loadTemplate() {
1120
+ if (cache) return cache;
1121
+ cache = true ? JSON.parse('{"component/component.tsx":"import React from \\"react\\";\\n\\n// Add your tool args (matching manifest.json#inputSchema.properties)\\n// alongside the GreatStore-injected lifecycle props. See AGENTS.md for\\n// what each lifecycle prop does.\\ninterface Props {\\n onSendMessage: (text: string) => void;\\n onCallTool: (name: string, args: Record<string, unknown>) => void;\\n onClose: () => void;\\n}\\n\\n// See AGENTS.md for the design rules (em-based sizing, brand CSS\\n// variables), the lifecycle props, and how to write an async,\\n// backend-backed component.\\nexport default function __GS_PASCAL__(_props: Props): React.ReactElement {\\n return (\\n <div\\n style={{\\n padding: \\"1em\\",\\n border: \\"1px solid var(--color-border-default)\\",\\n borderRadius: \\"var(--radius-lg)\\",\\n background: \\"var(--color-surface)\\",\\n color: \\"var(--color-foreground)\\",\\n fontFamily: \\"var(--font-sans)\\",\\n }}\\n >\\n <strong>__GS_NAME__</strong> \u2014 hello from your component!\\n </div>\\n );\\n}\\n","component/manifest.json":"{\\n \\"name\\": \\"__GS_NAME__\\",\\n \\"displayName\\": \\"__GS_DISPLAY_NAME__\\",\\n \\"description\\": \\"Renders the __GS_NAME__ widget.\\",\\n \\"displayMode\\": \\"inline\\",\\n \\"inputSchema\\": {\\n \\"type\\": \\"object\\",\\n \\"properties\\": {}\\n }\\n}\\n","root/.gitignore":"node_modules/\\ncomponents/*/bundle.js\\ncomponents/*/.gssync.json\\n*.tsbuildinfo\\n.DS_Store\\n","root/.gsrc":"{\\n \\"store\\": \\"__GS_STORE__\\"\\n}\\n","root/AGENTS.md":"# AGENTS.md\\n\\nGuidance for AI coding agents working on the components in this project.\\n`CLAUDE.md` and `GEMINI.md` are symlinks to this file.\\n\\nThese components render inside arbitrary publisher websites (the embed\\nwidget mounts on a host page) as well as the GreatStore storefront. You\\ncontrol neither the host page\'s base font size nor its color scheme, and\\nthe store owner controls the look from GreatStore theme settings. The\\nrules below keep components portable across both.\\n\\n## Design requirements\\n\\n### Never use `rem` for fonts and sizing \u2014 always use `em`\\n\\n`rem` resolves against the host page\'s root font size, which is\\narbitrary and outside our control. A host that sets `html { font-size:\\n8px }` (or 200%) will shrink or blow up every `rem`-based dimension.\\n`em` resolves against the component\'s own font size, so it stays\\nself-consistent wherever the component is mounted.\\n\\nUse `em` for font sizes, padding, margins, gaps, widths, heights,\\nand any other length. Borders may stay in `px` (hairlines should not\\nscale with text).\\n\\n```tsx\\n// Bad \u2014 breaks on hosts with a non-default root font size\\n<div style={{ padding: \\"1rem\\", fontSize: \\"0.875rem\\" }} />\\n\\n// Good\\n<div style={{ padding: \\"1em\\", fontSize: \\"0.875em\\" }} />\\n```\\n\\n### Don\'t hardcode colors, fonts, or corner radius \u2014 use GreatStore brand variables\\n\\nThe store owner themes the assistant from GreatStore settings. Hardcoded\\nvalues ignore that and make the component clash with the rest of the\\nstore. Read from the CSS variables GreatStore injects on the surface\\ninstead, so the component restyles itself when the theme changes.\\n\\nAvailable variables:\\n\\n| Purpose | Variable |\\n| --- | --- |\\n| Surface background | `--color-surface` |\\n| Secondary surface | `--color-surface-secondary` |\\n| Accent surface | `--color-surface-accent` |\\n| Hover surface | `--color-surface-hover` |\\n| Foreground text | `--color-foreground` |\\n| Secondary text | `--color-foreground-secondary` |\\n| Muted text | `--color-foreground-muted` |\\n| Accent text | `--color-foreground-accent` |\\n| Border | `--color-border-default` |\\n| Focus border | `--color-border-focus` |\\n| Primary / brand | `--color-primary` |\\n| Text on primary | `--color-primary-foreground` |\\n| Primary hover | `--color-primary-hover` |\\n| Primary muted | `--color-primary-muted` |\\n| Text on primary muted | `--color-primary-muted-foreground` |\\n| Primary tint | `--color-primary-tint` |\\n| Link | `--color-link` |\\n| Font family | `--font-sans` |\\n| Corner radius (scale) | `--radius-xs` \u2026 `--radius-4xl`, `--radius-pill` |\\n\\n```tsx\\n// Bad \u2014 hardcoded, ignores the store\'s theme\\n<button style={{ background: \\"#4f46e5\\", borderRadius: 8, fontFamily: \\"Inter\\" }} />\\n\\n// Good\\n<button\\n style={{\\n background: \\"var(--color-primary)\\",\\n color: \\"var(--color-primary-foreground)\\",\\n borderRadius: \\"var(--radius-lg)\\",\\n fontFamily: \\"var(--font-sans)\\",\\n }}\\n/>\\n```\\n\\nProvide a sensible fallback only when a value might be absent, e.g.\\n`var(--color-primary, currentColor)`.\\n\\n## Component props\\n\\nEach component receives its tool args (the fields you declare in\\n`manifest.json#inputSchema.properties`) plus these GreatStore-injected\\nlifecycle props, which are always present:\\n\\n| Prop | What it does |\\n| --- | --- |\\n| `onSendMessage(text)` | Send text into the chat as if the shopper typed it. |\\n| `onCallTool(name, args)` | Chain into another remote-component tool by name. |\\n| `onClose()` | Dismiss the host slot. Over-input clears the overlay, fullscreen reverts the pane, inline is a no-op. |\\n\\n## Async components (backend-backed, render-blocking data)\\n\\nIf a component must load data from a backend/API before it can render\\ncorrectly, make it async \u2014 don\'t render an empty shell and fetch in a\\n`useEffect`. Set `\\"async\\": true` in `manifest.json` and export an async\\ndefault. GreatStore waits for your promise (showing a normal loading\\nstate, so you don\'t render your own placeholder), then renders what it\\nresolves to. Components that render purely from their props stay\\nsynchronous.\\n\\nA thrown error is a **retry signal**: the in-store AI sees it and\\nusually re-calls the tool. So only throw when a *different* call could\\nhelp:\\n\\n1. `await` the backend call, then return the finished JSX.\\n2. Validate the AI-passed props first and throw on bad input \u2014 the AI\\n can fix the args and retry. (Don\'t validate the API\'s *output* and\\n throw: the AI can\'t fix the backend, it\'ll just loop.)\\n3. Broadcast a backend failure ONLY when retrying differently could\\n succeed, and say what to change (e.g. empty search \u2192 \\"try a broader\\n keyword\\"). For idempotent failures (500, timeout, missing record)\\n re-running the same call changes nothing \u2014 render a graceful fallback\\n instead of throwing.\\n\\n```tsx\\nexport default async function Example(props: Props) {\\n if (!props.query?.trim()) throw new Error(\\"missing required prop: query\\");\\n const res = await fetch(`/api/search?q=${encodeURIComponent(props.query)}`);\\n if (res.ok) {\\n const { results } = await res.json();\\n if (results.length === 0)\\n throw new Error(`no results for \\"${props.query}\\" \u2014 try a broader keyword`);\\n return <ul>{/* render results */}</ul>;\\n }\\n return <p>Couldn\'t load results right now.</p>; // idempotent: don\'t throw\\n}\\n```\\n","root/README.md":"# GreatStore components\\n\\nCustom React components published to your GreatStore tenant. Each\\ncomponent lives in its own folder under `components/`.\\n\\n```\\nnpm install\\ngs init <component_name> # add a new component\\ngs build # builds every components/<name>/bundle.js\\ngs push # uploads every changed component as a draft\\ngs publish <component_name> # promote a specific component to live\\n```\\n\\n- `gs push` (no args) hashes each component and only uploads the ones\\n that have changed since the last sync.\\n- `gs pull` (no args) refreshes every remote component into\\n `components/<name>/`. Components with unsaved local edits are skipped\\n with a warning; pass `--force` to overwrite.\\n\\nSee `AGENTS.md` for the design rules every component must follow\\n(em-based sizing, brand CSS variables) \u2014 it doubles as guidance for AI\\ncoding agents (`CLAUDE.md` / `GEMINI.md` symlink to it).\\n","root/package.json":"{\\n \\"name\\": \\"greatstore-components\\",\\n \\"version\\": \\"0.0.1\\",\\n \\"private\\": true,\\n \\"type\\": \\"module\\",\\n \\"scripts\\": {\\n \\"build\\": \\"gs build\\",\\n \\"push\\": \\"gs build && gs push\\"\\n },\\n \\"dependencies\\": {\\n \\"react\\": \\"^19.0.0\\",\\n \\"react-dom\\": \\"^19.0.0\\"\\n },\\n \\"devDependencies\\": {\\n \\"@types/react\\": \\"^19.0.0\\",\\n \\"@types/react-dom\\": \\"^19.0.0\\",\\n \\"@vitejs/plugin-react\\": \\"^4.3.0\\",\\n \\"typescript\\": \\"^5.6.0\\",\\n \\"vite\\": \\"^5.4.0\\"\\n }\\n}\\n","root/tsconfig.json":"{\\n \\"compilerOptions\\": {\\n \\"target\\": \\"ES2022\\",\\n \\"module\\": \\"ESNext\\",\\n \\"moduleResolution\\": \\"Bundler\\",\\n \\"jsx\\": \\"react-jsx\\",\\n \\"lib\\": [\\"ES2022\\", \\"DOM\\"],\\n \\"strict\\": true,\\n \\"esModuleInterop\\": true,\\n \\"skipLibCheck\\": true,\\n \\"isolatedModules\\": true,\\n \\"noEmit\\": true\\n },\\n \\"include\\": [\\"components/**/component.tsx\\", \\"vite.config.ts\\"]\\n}\\n","root/vite.config.ts":"import { defineConfig } from \\"vite\\";\\nimport react from \\"@vitejs/plugin-react\\";\\n\\n// Real builds happen in `gs build` (one Vite invocation per\\n// component, externals + runtime shim paths owned by the CLI). This\\n// file exists only so editors / language servers can resolve the\\n// React plugin when inspecting components/*/component.tsx.\\nexport default defineConfig({\\n plugins: [react()],\\n});\\n"}') : readTemplateFromDisk(new URL("../template/", import.meta.url));
1122
+ return cache;
1123
+ }
1124
+ var TOKENS = {
1125
+ name: "__GS_NAME__",
1126
+ pascalName: "__GS_PASCAL__",
1127
+ displayName: "__GS_DISPLAY_NAME__",
1128
+ store: "__GS_STORE__"
1129
+ };
1130
+ function applyTemplate(content, vars) {
1131
+ let out = content;
1132
+ for (const key of Object.keys(TOKENS)) {
1133
+ const value = vars[key];
1134
+ if (value !== void 0) out = out.split(TOKENS[key]).join(value);
1135
+ }
1136
+ const leftover = out.match(/__GS_[A-Z_]+__/);
1137
+ if (leftover) {
1138
+ throw new Error(
1139
+ `internal: template placeholder ${leftover[0]} was not provided`
1140
+ );
1141
+ }
1142
+ return out;
1143
+ }
1144
+
1145
+ // src/commands/init.ts
1114
1146
  var NAME_REGEX = /^[a-z][a-z0-9_]*$/;
1115
1147
  function initCommand(args) {
1116
1148
  const name = args.positional[0];
@@ -1134,20 +1166,28 @@ function initCommand(args) {
1134
1166
  `--store <slug> is required when scaffolding a new project root. Run \`${example}\`.`
1135
1167
  );
1136
1168
  }
1137
- ensureRoot(root, rootExisted, force, { store: storeFlag });
1169
+ const writtenRootFiles = ensureRoot(root, rootExisted, force, { store: storeFlag });
1138
1170
  if (!name) {
1139
- const created = rootExisted ? "Updated" : "Scaffolded";
1140
- process.stdout.write(
1141
- [
1142
- `${created} GreatStore project root in ${root}.`,
1143
- "",
1144
- "Next steps:",
1145
- ` cd ${path6.relative(process.cwd(), root) || "."}`,
1146
- " npm install",
1147
- " gs init <component_name> # add your first component",
1148
- ""
1149
- ].join("\n")
1171
+ const lines = [];
1172
+ if (rootExisted) {
1173
+ lines.push(`Updated GreatStore project root in ${root}.`);
1174
+ if (writtenRootFiles.length > 0) {
1175
+ lines.push("", `Added: ${writtenRootFiles.join(", ")}`);
1176
+ } else {
1177
+ lines.push("", "Already up to date \u2014 nothing to add.");
1178
+ }
1179
+ } else {
1180
+ lines.push(`Scaffolded GreatStore project root in ${root}.`);
1181
+ }
1182
+ lines.push(
1183
+ "",
1184
+ "Next steps:",
1185
+ ` cd ${path6.relative(process.cwd(), root) || "."}`,
1186
+ " npm install",
1187
+ " gs init <component_name> # add your first component",
1188
+ ""
1150
1189
  );
1190
+ process.stdout.write(lines.join("\n"));
1151
1191
  return;
1152
1192
  }
1153
1193
  const componentDir = path6.join(root, "components", name);
@@ -1156,6 +1196,7 @@ function initCommand(args) {
1156
1196
  process.stdout.write(
1157
1197
  [
1158
1198
  `Added component "${name}" at components/${name}/.`,
1199
+ ...rootExisted && writtenRootFiles.length > 0 ? ["", `Also added to the project root: ${writtenRootFiles.join(", ")}`] : [],
1159
1200
  "",
1160
1201
  "Next steps:",
1161
1202
  ...rootExisted ? [] : [` cd ${projectLabel}`, " npm install"],
@@ -1171,11 +1212,7 @@ function hasRootScaffold(dir) {
1171
1212
  }
1172
1213
  function ensureRoot(root, rootExisted, force, opts) {
1173
1214
  fs6.mkdirSync(root, { recursive: true });
1174
- if (rootExisted) {
1175
- fs6.mkdirSync(path6.join(root, "components"), { recursive: true });
1176
- return;
1177
- }
1178
- if (!force) {
1215
+ if (!rootExisted && !force) {
1179
1216
  const entries = fs6.readdirSync(root).filter((e) => e !== ".gsrc");
1180
1217
  if (entries.length > 0) {
1181
1218
  throw new Error(
@@ -1183,14 +1220,38 @@ function ensureRoot(root, rootExisted, force, opts) {
1183
1220
  );
1184
1221
  }
1185
1222
  }
1186
- for (const [relPath, content] of rootFiles({ store: opts.store })) {
1223
+ const files = rootExisted ? existingRootFiles() : rootFiles({ store: opts.store });
1224
+ const written = writeRootFiles(root, files, force);
1225
+ fs6.mkdirSync(path6.join(root, "components"), { recursive: true });
1226
+ return written;
1227
+ }
1228
+ function writeRootFiles(root, files, force) {
1229
+ const written = [];
1230
+ for (const [relPath, content] of files) {
1187
1231
  const full = path6.join(root, relPath);
1188
1232
  fs6.mkdirSync(path6.dirname(full), { recursive: true });
1189
1233
  if (force || !fs6.existsSync(full)) {
1190
1234
  fs6.writeFileSync(full, content);
1235
+ written.push(relPath);
1191
1236
  }
1192
1237
  }
1193
- fs6.mkdirSync(path6.join(root, "components"), { recursive: true });
1238
+ for (const link of ["CLAUDE.md", "GEMINI.md"]) {
1239
+ const linkPath = path6.join(root, link);
1240
+ if (force && pathExists(linkPath)) fs6.rmSync(linkPath);
1241
+ if (!pathExists(linkPath)) {
1242
+ fs6.symlinkSync("AGENTS.md", linkPath);
1243
+ written.push(link);
1244
+ }
1245
+ }
1246
+ return written;
1247
+ }
1248
+ function pathExists(p) {
1249
+ try {
1250
+ fs6.lstatSync(p);
1251
+ return true;
1252
+ } catch {
1253
+ return false;
1254
+ }
1194
1255
  }
1195
1256
  function ensureComponent(componentDir, name, force) {
1196
1257
  if (fs6.existsSync(componentDir) && !force) {
@@ -1206,40 +1267,25 @@ function ensureComponent(componentDir, name, force) {
1206
1267
  fs6.writeFileSync(path6.join(componentDir, relPath), content);
1207
1268
  }
1208
1269
  }
1270
+ function templateFiles(prefix, vars, exclude = /* @__PURE__ */ new Set()) {
1271
+ const tree = loadTemplate();
1272
+ return Object.entries(tree).filter(([rel]) => rel.startsWith(prefix)).map(([rel, content]) => [rel.slice(prefix.length), content]).filter(([dest]) => !exclude.has(dest)).map(([dest, content]) => [dest, applyTemplate(content, vars)]);
1273
+ }
1209
1274
  function rootFiles(opts) {
1210
1275
  if (!opts.store) {
1211
1276
  throw new Error("internal: rootFiles called without a store slug");
1212
1277
  }
1213
- return [
1214
- ["package.json", rootPackageJson()],
1215
- ["vite.config.ts", viteEditorConfig()],
1216
- ["tsconfig.json", rootTsconfig()],
1217
- [".gsrc", gsrc(opts.store)],
1218
- [".gitignore", gitignore()],
1219
- ["README.md", rootReadme()]
1220
- ];
1278
+ return templateFiles("root/", { store: opts.store });
1221
1279
  }
1222
- function componentFiles(name) {
1223
- return [
1224
- ["manifest.json", manifest(name)],
1225
- ["component.tsx", component(name)]
1226
- ];
1280
+ function existingRootFiles() {
1281
+ return templateFiles("root/", {}, /* @__PURE__ */ new Set([".gsrc"]));
1227
1282
  }
1228
- function manifest(name) {
1229
- return JSON.stringify(
1230
- {
1231
- name,
1232
- displayName: defaultDisplayName(name),
1233
- description: `Renders the ${name} widget.`,
1234
- displayMode: "inline",
1235
- inputSchema: {
1236
- type: "object",
1237
- properties: {}
1238
- }
1239
- },
1240
- null,
1241
- 2
1242
- ) + "\n";
1283
+ function componentFiles(name) {
1284
+ return templateFiles("component/", {
1285
+ name,
1286
+ pascalName: pascal(name),
1287
+ displayName: defaultDisplayName(name)
1288
+ });
1243
1289
  }
1244
1290
  function defaultDisplayName(name) {
1245
1291
  const words = name.split("_").filter(Boolean);
@@ -1247,161 +1293,6 @@ function defaultDisplayName(name) {
1247
1293
  const first = words[0];
1248
1294
  return first.charAt(0).toUpperCase() + first.slice(1) + (words.length > 1 ? " " + words.slice(1).join(" ") : "");
1249
1295
  }
1250
- function component(name) {
1251
- return `import React from "react";
1252
-
1253
- interface Props {
1254
- // ---- Your tool args ----
1255
- // Add fields here matching manifest.json#inputSchema.properties.
1256
-
1257
- // ---- GreatStore-injected lifecycle props (always present) ----
1258
- // Send text into the chat as if the shopper typed it.
1259
- onSendMessage: (text: string) => void;
1260
- // Chain into another remote-component tool by name.
1261
- onCallTool: (name: string, args: Record<string, unknown>) => void;
1262
- // Dismiss the host slot (over-input clears the overlay, fullscreen
1263
- // reverts the pane, inline is a no-op).
1264
- onClose: () => void;
1265
- }
1266
-
1267
- export default function ${pascal(name)}(_props: Props): React.ReactElement {
1268
- return (
1269
- <div style={{ padding: "1rem", border: "1px solid #ddd", borderRadius: 8 }}>
1270
- <strong>${name}</strong> \u2014 hello from your component!
1271
- </div>
1272
- );
1273
- }
1274
-
1275
- // ---- Async components (backend-backed, render-blocking data) ----
1276
- // If this component must load data from a backend/API before it can
1277
- // render correctly, make it async \u2014 don't render an empty shell and
1278
- // fetch in a useEffect. Set "async": true in manifest.json and export
1279
- // an async default. GreatStore waits for your promise (showing a normal
1280
- // loading state, so you don't render your own placeholder), then renders
1281
- // what it resolves to.
1282
- //
1283
- // A thrown error is a RETRY SIGNAL: the in-store AI sees it and usually
1284
- // re-calls the tool. So only throw when a *different* call could help.
1285
- // 1. await the backend call, then return the finished JSX.
1286
- // 2. Validate the AI-passed props first and throw on bad input \u2014 the
1287
- // AI can fix the args and retry. (Don't validate the API's output
1288
- // and throw: the AI can't fix the backend, it'll just loop.)
1289
- // 3. Broadcast a backend failure ONLY when retrying differently could
1290
- // succeed, and say what to change (e.g. empty search \u2192 "try a
1291
- // broader keyword"). For idempotent failures (500, timeout, missing
1292
- // record) re-running the same call changes nothing \u2014 render a
1293
- // graceful fallback instead of throwing.
1294
- //
1295
- // export default async function ${pascal(name)}(props: Props) {
1296
- // if (!props.query?.trim()) throw new Error("missing required prop: query");
1297
- // const res = await fetch(\`/api/search?q=\${encodeURIComponent(props.query)}\`);
1298
- // if (res.ok) {
1299
- // const { results } = await res.json();
1300
- // if (results.length === 0)
1301
- // throw new Error(\`no results for "\${props.query}" \u2014 try a broader keyword\`);
1302
- // return <ul>{/* render results */}</ul>;
1303
- // }
1304
- // return <p>Couldn't load results right now.</p>; // idempotent: don't throw
1305
- // }
1306
- //
1307
- // Components that render purely from their props stay synchronous.
1308
- `;
1309
- }
1310
- function viteEditorConfig() {
1311
- return `import { defineConfig } from "vite";
1312
- import react from "@vitejs/plugin-react";
1313
-
1314
- // Real builds happen in \`gs build\` (one Vite invocation per
1315
- // component, externals + runtime shim paths owned by the CLI). This
1316
- // file exists only so editors / language servers can resolve the
1317
- // React plugin when inspecting components/*/component.tsx.
1318
- export default defineConfig({
1319
- plugins: [react()],
1320
- });
1321
- `;
1322
- }
1323
- function rootTsconfig() {
1324
- return JSON.stringify(
1325
- {
1326
- compilerOptions: {
1327
- target: "ES2022",
1328
- module: "ESNext",
1329
- moduleResolution: "Bundler",
1330
- jsx: "react-jsx",
1331
- lib: ["ES2022", "DOM"],
1332
- strict: true,
1333
- esModuleInterop: true,
1334
- skipLibCheck: true,
1335
- isolatedModules: true,
1336
- noEmit: true
1337
- },
1338
- include: ["components/**/component.tsx", "vite.config.ts"]
1339
- },
1340
- null,
1341
- 2
1342
- ) + "\n";
1343
- }
1344
- function rootPackageJson() {
1345
- return JSON.stringify(
1346
- {
1347
- name: "greatstore-components",
1348
- version: "0.0.1",
1349
- private: true,
1350
- type: "module",
1351
- scripts: {
1352
- build: "gs build",
1353
- push: "gs build && gs push"
1354
- },
1355
- dependencies: {
1356
- react: "^19.0.0",
1357
- "react-dom": "^19.0.0"
1358
- },
1359
- devDependencies: {
1360
- "@types/react": "^19.0.0",
1361
- "@types/react-dom": "^19.0.0",
1362
- "@vitejs/plugin-react": "^4.3.0",
1363
- typescript: "^5.6.0",
1364
- vite: "^5.4.0"
1365
- }
1366
- },
1367
- null,
1368
- 2
1369
- ) + "\n";
1370
- }
1371
- function gsrc(store) {
1372
- return JSON.stringify({ store }, null, 2) + "\n";
1373
- }
1374
- function gitignore() {
1375
- return [
1376
- "node_modules/",
1377
- "components/*/bundle.js",
1378
- "components/*/.gssync.json",
1379
- "*.tsbuildinfo",
1380
- ".DS_Store",
1381
- ""
1382
- ].join("\n");
1383
- }
1384
- function rootReadme() {
1385
- return `# GreatStore components
1386
-
1387
- Custom React components published to your GreatStore tenant. Each
1388
- component lives in its own folder under \`components/\`.
1389
-
1390
- \`\`\`
1391
- npm install
1392
- gs init <component_name> # add a new component
1393
- gs build # builds every components/<name>/bundle.js
1394
- gs push # uploads every changed component as a draft
1395
- gs publish <component_name> # promote a specific component to live
1396
- \`\`\`
1397
-
1398
- - \`gs push\` (no args) hashes each component and only uploads the ones
1399
- that have changed since the last sync.
1400
- - \`gs pull\` (no args) refreshes every remote component into
1401
- \`components/<name>/\`. Components with unsaved local edits are skipped
1402
- with a warning; pass \`--force\` to overwrite.
1403
- `;
1404
- }
1405
1296
  function pascal(name) {
1406
1297
  return name.split(/[_-]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join("");
1407
1298
  }
@@ -1677,11 +1568,11 @@ function readCache(home) {
1677
1568
  }
1678
1569
  return null;
1679
1570
  }
1680
- function writeCache(home, cache) {
1571
+ function writeCache(home, cache2) {
1681
1572
  try {
1682
1573
  const file = cachePath(home);
1683
1574
  fs8.mkdirSync(path8.dirname(file), { recursive: true });
1684
- fs8.writeFileSync(file, JSON.stringify(cache));
1575
+ fs8.writeFileSync(file, JSON.stringify(cache2));
1685
1576
  } catch {
1686
1577
  }
1687
1578
  }
@@ -1703,8 +1594,8 @@ function parseVer(v) {
1703
1594
  }
1704
1595
 
1705
1596
  // src/index.ts
1706
- var VERSION = true ? "0.0.22" : "0.0.0-dev";
1707
- var CHANGELOG = true ? '# Changelog\n\nAll notable changes to `@greatstore/cli` are recorded here. The format\nfollows [Keep a Changelog](https://keepachangelog.com/).\n\n## 0.0.22 \u2014 2026-06-02\n\n### Added\n- `gs list` now shows a link to each component\'s page in the dashboard,\n so you can jump straight to a component to preview or publish it. The\n link is also included in `gs list --json`.\n\n## 0.0.21 \u2014 2026-05-31\n\n### Changed\n- The `gs init` component scaffold now shows how to write **async**\n components that load data before they render \u2014 including validating\n inputs up front and signalling a failure by throwing. The scaffolded\n component no longer includes an `onError` prop; throw from an async\n component to report a failure instead.\n\n## 0.0.20 \u2014 2026-05-29\n\n### Added\n- `gs pull`, `gs push`, and `gs publish` now accept several component\n names at once (e.g. `gs publish header footer cart`). Each component\n is reported on its own line and one failure no longer stops the rest.\n\n## 0.0.19 \u2014 2026-05-28\n\n### Fixed\n- `gs login` on Windows no longer opens a sign-in URL with parameters\n stripped, which surfaced as a "Missing redirect_uri or state\n parameter" page in the browser.\n\n## 0.0.18 \u2014 2026-05-28\n\n### Changed\n- Push and publish errors now name the specific reason \u2014 including\n every failing field in `manifest.json` \u2014 instead of the previous\n generic message.\n\n## 0.0.17 \u2014 2026-05-28\n\n### Added\n- Each command now prints a one-line upgrade notice when a newer\n `@greatstore/cli` is available on npm.\n\n## 0.0.16 \u2014 2026-05-28\n\n### Changed\n- Simplified error messages.\n\n## 0.0.15 \u2014 2026-05-24\n\n### Added\n- Scaffolded `component.tsx` now declares the four injected lifecycle\n props (`onSendMessage`, `onCallTool`, `onClose`, `onError`) on\n `Props`. Use `onError(message)` to report expected failures (failed\n fetch, host action rejected, invalid host state) so the AI can\n recover on its next turn. Render-time crashes are reported for you.\n\n## 0.0.14 \u2014 2026-05-24\n\n### Changed\n- `gs build` output is now whitespace-minified \u2014 typically ~50% smaller.\n\n## 0.0.13 \u2014 2026-05-24\n\n### Changed\n- `gs build` prints the next-step hint (`gs push`, then `gs publish`).\n\n## 0.0.12 \u2014 2026-05-24\n\n### Fixed\n- `gs build` failing to load Vite in some setups.\n\n## 0.0.11 \u2014 2026-05-24\n\n### Added\n- Multi-component projects. `gs init` (no args) scaffolds the project\n root; `gs init <name>` adds a component under `components/<name>/`.\n- `gs build [<name>]` \u2014 compiles every `components/<name>/bundle.js`.\n- `gs push` (no args) uploads only the components that changed.\n- `gs pull` (no args, or `*`) downloads every component. Locally\n edited components are skipped; pass `--force` to overwrite.\n- Public `CHANGELOG.md`; `gs --version` prints recent entries.\n\n### Changed\n- A project folder ships to exactly one store. Only `gs init` accepts\n `--store`; every other command reads the slug from `.gsrc`. The old\n single-component layout is rejected with a migration hint.\n- `gs init` requires `--store <slug>` for a fresh root, and rejects\n `--store` on an existing root.\n- `gs init` no longer writes `build.mjs` \u2014 scripts call `gs build`.\n\n## 0.0.10 \u2014 2026-05-23\n\n### Changed\n- The sign-in browser tab auto-closes once `gs login` finishes.\n\n## 0.0.9 \u2014 2026-05-23\n\n### Changed\n- Scaffolded manifests include a `displayName` so the admin UI has a\n friendlier label.\n\n## 0.0.8 \u2014 2026-05-23\n\n### Changed\n- `gs push` and `gs publish` print a link to view the component.\n\n## 0.0.6 \u2014 2026-05-23\n\n### Changed\n- `gs --version` reads from the published package version.\n\n## 0.0.4 \u2014 2026-05-23\n\n### Changed\n- Scaffolded projects produce browser-ready bundles out of the box.\n\n## 0.0.3 \u2014 2026-05-23\n\n### Changed\n- Trimmed public README to the essentials.\n\n## 0.0.2 \u2014 2026-05-23\n\n### Fixed\n- Sign-in callback parameter handling.\n' : "";
1597
+ var VERSION = true ? "0.0.24" : "0.0.0-dev";
1598
+ var CHANGELOG = true ? "# Changelog\n\nAll notable changes to `@greatstore/cli` are recorded here. The format\nfollows [Keep a Changelog](https://keepachangelog.com/).\n\n## 0.0.24 \u2014 2026-06-03\n\n### Changed\n- `gs init` in an existing project now fills in any scaffold files that\n are missing (for example, the `AGENTS.md` design guide added in\n 0.0.23) and leaves your own files alone. Pass `--force` to refresh\n every scaffold file to the latest version. Your pinned store\n (`.gsrc`) is never rewritten either way.\n\n## 0.0.23 \u2014 2026-06-03\n\n### Added\n- `gs init` now scaffolds an `AGENTS.md` (with `CLAUDE.md` and\n `GEMINI.md` symlinked to it) documenting the design rules every\n component should follow \u2014 use `em` rather than `rem` for sizing, and\n style from the provided brand CSS variables so components match the\n store's theme. It doubles as guidance for AI coding agents.\n\n## 0.0.22 \u2014 2026-06-02\n\n### Added\n- `gs list` now shows a link to each component's page in the dashboard,\n so you can jump straight to a component to preview or publish it. The\n link is also included in `gs list --json`.\n\n## 0.0.21 \u2014 2026-05-31\n\n### Changed\n- The `gs init` component scaffold now shows how to write **async**\n components that load data before they render \u2014 including validating\n inputs up front and signalling a failure by throwing. The scaffolded\n component no longer includes an `onError` prop; throw from an async\n component to report a failure instead.\n\n## 0.0.20 \u2014 2026-05-29\n\n### Added\n- `gs pull`, `gs push`, and `gs publish` now accept several component\n names at once (e.g. `gs publish header footer cart`). Each component\n is reported on its own line and one failure no longer stops the rest.\n\n## 0.0.19 \u2014 2026-05-28\n\n### Fixed\n- `gs login` on Windows no longer opens a sign-in URL with parameters\n stripped, which surfaced as a \"Missing redirect_uri or state\n parameter\" page in the browser.\n\n## 0.0.18 \u2014 2026-05-28\n\n### Changed\n- Push and publish errors now name the specific reason \u2014 including\n every failing field in `manifest.json` \u2014 instead of the previous\n generic message.\n\n## 0.0.17 \u2014 2026-05-28\n\n### Added\n- Each command now prints a one-line upgrade notice when a newer\n `@greatstore/cli` is available on npm.\n\n## 0.0.16 \u2014 2026-05-28\n\n### Changed\n- Simplified error messages.\n\n## 0.0.15 \u2014 2026-05-24\n\n### Added\n- Scaffolded `component.tsx` now declares the four injected lifecycle\n props (`onSendMessage`, `onCallTool`, `onClose`, `onError`) on\n `Props`. Use `onError(message)` to report expected failures (failed\n fetch, host action rejected, invalid host state) so the AI can\n recover on its next turn. Render-time crashes are reported for you.\n\n## 0.0.14 \u2014 2026-05-24\n\n### Changed\n- `gs build` output is now whitespace-minified \u2014 typically ~50% smaller.\n\n## 0.0.13 \u2014 2026-05-24\n\n### Changed\n- `gs build` prints the next-step hint (`gs push`, then `gs publish`).\n\n## 0.0.12 \u2014 2026-05-24\n\n### Fixed\n- `gs build` failing to load Vite in some setups.\n\n## 0.0.11 \u2014 2026-05-24\n\n### Added\n- Multi-component projects. `gs init` (no args) scaffolds the project\n root; `gs init <name>` adds a component under `components/<name>/`.\n- `gs build [<name>]` \u2014 compiles every `components/<name>/bundle.js`.\n- `gs push` (no args) uploads only the components that changed.\n- `gs pull` (no args, or `*`) downloads every component. Locally\n edited components are skipped; pass `--force` to overwrite.\n- Public `CHANGELOG.md`; `gs --version` prints recent entries.\n\n### Changed\n- A project folder ships to exactly one store. Only `gs init` accepts\n `--store`; every other command reads the slug from `.gsrc`. The old\n single-component layout is rejected with a migration hint.\n- `gs init` requires `--store <slug>` for a fresh root, and rejects\n `--store` on an existing root.\n- `gs init` no longer writes `build.mjs` \u2014 scripts call `gs build`.\n\n## 0.0.10 \u2014 2026-05-23\n\n### Changed\n- The sign-in browser tab auto-closes once `gs login` finishes.\n\n## 0.0.9 \u2014 2026-05-23\n\n### Changed\n- Scaffolded manifests include a `displayName` so the admin UI has a\n friendlier label.\n\n## 0.0.8 \u2014 2026-05-23\n\n### Changed\n- `gs push` and `gs publish` print a link to view the component.\n\n## 0.0.6 \u2014 2026-05-23\n\n### Changed\n- `gs --version` reads from the published package version.\n\n## 0.0.4 \u2014 2026-05-23\n\n### Changed\n- Scaffolded projects produce browser-ready bundles out of the box.\n\n## 0.0.3 \u2014 2026-05-23\n\n### Changed\n- Trimmed public README to the essentials.\n\n## 0.0.2 \u2014 2026-05-23\n\n### Fixed\n- Sign-in callback parameter handling.\n" : "";
1708
1599
  var HELP = `gs \u2014 GreatStore CLI (v${VERSION})
1709
1600
 
1710
1601
  Usage:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatstore/cli",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "CLI for authoring and shipping GreatStore custom components.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",