@greatstore/cli 0.0.22 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/cli.js +62 -187
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
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.23 — 2026-06-03
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- `gs init` now scaffolds an `AGENTS.md` (with `CLAUDE.md` and
|
|
10
|
+
`GEMINI.md` symlinked to it) documenting the design rules every
|
|
11
|
+
component should follow — use `em` rather than `rem` for sizing, and
|
|
12
|
+
style from the provided brand CSS variables so components match the
|
|
13
|
+
store's theme. It doubles as guidance for AI coding agents.
|
|
14
|
+
|
|
6
15
|
## 0.0.22 — 2026-06-02
|
|
7
16
|
|
|
8
17
|
### 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];
|
|
@@ -1190,8 +1222,21 @@ function ensureRoot(root, rootExisted, force, opts) {
|
|
|
1190
1222
|
fs6.writeFileSync(full, content);
|
|
1191
1223
|
}
|
|
1192
1224
|
}
|
|
1225
|
+
for (const link of ["CLAUDE.md", "GEMINI.md"]) {
|
|
1226
|
+
const linkPath = path6.join(root, link);
|
|
1227
|
+
if (force && pathExists(linkPath)) fs6.rmSync(linkPath);
|
|
1228
|
+
if (!pathExists(linkPath)) fs6.symlinkSync("AGENTS.md", linkPath);
|
|
1229
|
+
}
|
|
1193
1230
|
fs6.mkdirSync(path6.join(root, "components"), { recursive: true });
|
|
1194
1231
|
}
|
|
1232
|
+
function pathExists(p) {
|
|
1233
|
+
try {
|
|
1234
|
+
fs6.lstatSync(p);
|
|
1235
|
+
return true;
|
|
1236
|
+
} catch {
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1195
1240
|
function ensureComponent(componentDir, name, force) {
|
|
1196
1241
|
if (fs6.existsSync(componentDir) && !force) {
|
|
1197
1242
|
const entries = fs6.readdirSync(componentDir);
|
|
@@ -1206,40 +1251,25 @@ function ensureComponent(componentDir, name, force) {
|
|
|
1206
1251
|
fs6.writeFileSync(path6.join(componentDir, relPath), content);
|
|
1207
1252
|
}
|
|
1208
1253
|
}
|
|
1254
|
+
function templateFiles(prefix, vars) {
|
|
1255
|
+
const tree = loadTemplate();
|
|
1256
|
+
return Object.entries(tree).filter(([rel]) => rel.startsWith(prefix)).map(([rel, content]) => [
|
|
1257
|
+
rel.slice(prefix.length),
|
|
1258
|
+
applyTemplate(content, vars)
|
|
1259
|
+
]);
|
|
1260
|
+
}
|
|
1209
1261
|
function rootFiles(opts) {
|
|
1210
1262
|
if (!opts.store) {
|
|
1211
1263
|
throw new Error("internal: rootFiles called without a store slug");
|
|
1212
1264
|
}
|
|
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
|
-
];
|
|
1265
|
+
return templateFiles("root/", { store: opts.store });
|
|
1221
1266
|
}
|
|
1222
1267
|
function componentFiles(name) {
|
|
1223
|
-
return
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
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";
|
|
1268
|
+
return templateFiles("component/", {
|
|
1269
|
+
name,
|
|
1270
|
+
pascalName: pascal(name),
|
|
1271
|
+
displayName: defaultDisplayName(name)
|
|
1272
|
+
});
|
|
1243
1273
|
}
|
|
1244
1274
|
function defaultDisplayName(name) {
|
|
1245
1275
|
const words = name.split("_").filter(Boolean);
|
|
@@ -1247,161 +1277,6 @@ function defaultDisplayName(name) {
|
|
|
1247
1277
|
const first = words[0];
|
|
1248
1278
|
return first.charAt(0).toUpperCase() + first.slice(1) + (words.length > 1 ? " " + words.slice(1).join(" ") : "");
|
|
1249
1279
|
}
|
|
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
1280
|
function pascal(name) {
|
|
1406
1281
|
return name.split(/[_-]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join("");
|
|
1407
1282
|
}
|
|
@@ -1677,11 +1552,11 @@ function readCache(home) {
|
|
|
1677
1552
|
}
|
|
1678
1553
|
return null;
|
|
1679
1554
|
}
|
|
1680
|
-
function writeCache(home,
|
|
1555
|
+
function writeCache(home, cache2) {
|
|
1681
1556
|
try {
|
|
1682
1557
|
const file = cachePath(home);
|
|
1683
1558
|
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
1684
|
-
fs8.writeFileSync(file, JSON.stringify(
|
|
1559
|
+
fs8.writeFileSync(file, JSON.stringify(cache2));
|
|
1685
1560
|
} catch {
|
|
1686
1561
|
}
|
|
1687
1562
|
}
|
|
@@ -1703,8 +1578,8 @@ function parseVer(v) {
|
|
|
1703
1578
|
}
|
|
1704
1579
|
|
|
1705
1580
|
// src/index.ts
|
|
1706
|
-
var VERSION = true ? "0.0.
|
|
1707
|
-
var CHANGELOG = true ?
|
|
1581
|
+
var VERSION = true ? "0.0.23" : "0.0.0-dev";
|
|
1582
|
+
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.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
1583
|
var HELP = `gs \u2014 GreatStore CLI (v${VERSION})
|
|
1709
1584
|
|
|
1710
1585
|
Usage:
|