@artblocks/abx-cli 0.1.0-alpha.1 → 0.1.0-alpha.11

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.
@@ -8,6 +8,11 @@
8
8
  * offline, in CI, or opted out (ABX_NO_UPDATE_CHECK / --no-update-check — see main.ts).
9
9
  * The check lives in the CLI binary, so it fires no matter which agent (Claude, Codex,
10
10
  * Cursor, …) drives `abx` — one implementation, every agent covered.
11
+ *
12
+ * What it asks npm: the `latest` dist-tag AND (when the running version is a prerelease) that
13
+ * version's own channel tag, taking whichever is newer. Deliberately NOT just `latest` — see
14
+ * {@link prereleaseChannel} for the silent-failure mode that would otherwise arrive the day a
15
+ * stable release ships.
11
16
  */
12
17
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
13
18
  import { dirname, join, resolve } from 'node:path';
@@ -15,7 +20,10 @@ import { fileURLToPath } from 'node:url';
15
20
  import { homedir } from 'node:os';
16
21
  const REGISTRY = 'https://registry.npmjs.org';
17
22
  const PKG = '@artblocks/abx-cli';
18
- const TTL_MS = 24 * 60 * 60 * 1000; // check npm at most once a day
23
+ // How long a check is cached. 6h rather than a day because releases land fast in the alpha line: a
24
+ // 24h cache let someone work a whole session — deploys included — against a CLI that had been
25
+ // superseded that morning and never hear about it. 6h caps that at roughly one sitting.
26
+ const TTL_MS = 6 * 60 * 60 * 1000;
19
27
  const TIMEOUT_MS = 1500; // never hang a command on a slow/offline network
20
28
  /** Read the running CLI's own version from its package.json. Resolves the same in dev
21
29
  * (src/update-check.ts → packages/cli/package.json) and published (dist/update-check.js →
@@ -96,14 +104,33 @@ function parseSemver(v) {
96
104
  pre: preStr ? preStr.split('.') : [],
97
105
  };
98
106
  }
99
- /** GET the `latest` dist-tag's version from the npm registry. Aborts after {@link TIMEOUT_MS};
100
- * returns null on any failure (offline, timeout, non-200, malformed body) never throws. */
101
- export async function fetchLatestVersion(pkg = PKG) {
107
+ /**
108
+ * The npm dist-tag channel a version belongs to, or null for a stable release.
109
+ * `0.1.0-alpha.5` → `alpha`; `0.1.0` → null; `1.0.0-5` → null (a bare numeric prerelease
110
+ * identifier is a version counter, not a channel name).
111
+ *
112
+ * Why this exists: the update check must not depend on prereleases living under `latest`.
113
+ * Today they do — `ci:publish` runs `pnpm -r publish` with no `--tag`, so npm points `latest`
114
+ * at each new alpha and asking for `/latest` happens to find it. That breaks the moment a
115
+ * stable release ships and the pipeline starts publishing prereleases under `--tag alpha`:
116
+ * `/latest` would only ever report the stable, and the nudge would go SILENT for every alpha
117
+ * user with nothing erroring. Resolving the running version's own channel alongside `latest`
118
+ * makes the check correct in both eras, whichever tag the pipeline uses.
119
+ */
120
+ export function prereleaseChannel(version) {
121
+ const first = parseSemver(version).pre[0];
122
+ if (!first || /^\d+$/.test(first))
123
+ return null;
124
+ return first;
125
+ }
126
+ /** GET the version a dist-tag currently points at. Aborts after {@link TIMEOUT_MS}; returns null
127
+ * on any failure (offline, timeout, 404 for a tag that was never set, malformed body) — never throws. */
128
+ export async function fetchDistTagVersion(tag, pkg = PKG) {
102
129
  const ctrl = new AbortController();
103
130
  const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
104
131
  try {
105
- // Scoped packages encode the '/' as %2F; the /latest endpoint returns that version's document.
106
- const res = await fetch(`${REGISTRY}/${pkg.replace('/', '%2F')}/latest`, { signal: ctrl.signal });
132
+ // Scoped packages encode the '/' as %2F; the /<tag> endpoint returns that version's document.
133
+ const res = await fetch(`${REGISTRY}/${pkg.replace('/', '%2F')}/${encodeURIComponent(tag)}`, { signal: ctrl.signal });
107
134
  if (!res.ok)
108
135
  return null;
109
136
  const body = (await res.json());
@@ -116,19 +143,42 @@ export async function fetchLatestVersion(pkg = PKG) {
116
143
  clearTimeout(timer);
117
144
  }
118
145
  }
146
+ /** The greatest of a set of semver strings, skipping nulls; null if none are usable. */
147
+ export function newestOf(versions) {
148
+ const found = versions.filter((v) => typeof v === 'string' && v.length > 0);
149
+ if (!found.length)
150
+ return null;
151
+ return found.reduce((max, v) => (compareVersions(v, max) > 0 ? v : max));
152
+ }
153
+ /**
154
+ * The newest version published on any dist-tag relevant to `current`: `latest`, plus the running
155
+ * version's own prerelease channel when it has one ({@link prereleaseChannel}). Both are requested
156
+ * in parallel, so the wall clock stays one {@link TIMEOUT_MS} rather than two. Null when every
157
+ * request fails.
158
+ */
159
+ export async function fetchNewestPublished(current, pkg = PKG) {
160
+ const channel = prereleaseChannel(current);
161
+ const tags = channel ? ['latest', channel] : ['latest'];
162
+ return newestOf(await Promise.all(tags.map((t) => fetchDistTagVersion(t, pkg))));
163
+ }
119
164
  function cachePath() {
120
165
  return join(homedir(), '.cache', 'abx', 'update-check.json');
121
166
  }
122
167
  /**
123
- * Resolve the latest published version IF it is newer than `current`, else null. Reads a disk
168
+ * Resolve the newest published version IF it is newer than `current`, else null. Reads a disk
124
169
  * cache first and only hits the network when the cache is older than {@link TTL_MS}. On a network
125
170
  * failure it still stamps the cache (backing off a full TTL instead of retrying every command).
126
171
  * Swallows every error — the update check must never break or slow a real command.
127
172
  *
173
+ * The cache is keyed by release channel as well as time: which dist-tags matter depends on the
174
+ * running version, so an entry computed on another channel (an alpha → stable upgrade, or back) is
175
+ * a miss rather than a day-stale answer. Entries written before this key existed simply miss once.
176
+ *
128
177
  * `now` is injectable so tests can drive TTL behavior deterministically.
129
178
  */
130
179
  export async function checkForCliUpdate(current, now = Date.now()) {
131
180
  let latest = null;
181
+ const channel = prereleaseChannel(current) ?? '';
132
182
  try {
133
183
  const path = cachePath();
134
184
  let cache = {};
@@ -139,16 +189,17 @@ export async function checkForCliUpdate(current, now = Date.now()) {
139
189
  catch {
140
190
  cache = {};
141
191
  }
142
- const fresh = typeof cache.checkedAt === 'number' && now - cache.checkedAt < TTL_MS;
192
+ const sameChannel = (cache.channel ?? '') === channel;
193
+ const fresh = sameChannel && typeof cache.checkedAt === 'number' && now - cache.checkedAt < TTL_MS;
143
194
  if (fresh) {
144
195
  latest = typeof cache.latest === 'string' ? cache.latest : null;
145
196
  }
146
197
  else {
147
- const fetched = await fetchLatestVersion();
148
- latest = fetched ?? (typeof cache.latest === 'string' ? cache.latest : null);
198
+ const fetched = await fetchNewestPublished(current);
199
+ latest = fetched ?? (sameChannel && typeof cache.latest === 'string' ? cache.latest : null);
149
200
  try {
150
201
  mkdirSync(dirname(path), { recursive: true });
151
- writeFileSync(path, JSON.stringify({ checkedAt: now, latest }));
202
+ writeFileSync(path, JSON.stringify({ checkedAt: now, latest, channel }));
152
203
  }
153
204
  catch {
154
205
  /* a read-only HOME just means we re-check next run */
@@ -162,30 +213,70 @@ export async function checkForCliUpdate(current, now = Date.now()) {
162
213
  return null;
163
214
  return isNewer(latest, current) ? latest : null;
164
215
  }
216
+ /** The skill's folder name — matches SKILL.md `name`, per the Agent Skills rule that a skill
217
+ * directory must be named for its `name` field. */
218
+ export const SKILL_DIR_NAME = 'abx-self-host';
165
219
  /**
166
- * Versions stamped by `abx skill install` into any locally-installed skill copy (project-local
167
- * and global). Lets the notifier catch a skill that has drifted behind an upgraded CLI — the two
168
- * are co-versioned, and a separately-installed skill copy does NOT move when the CLI upgrades.
169
- * Copies installed via `npx skills add` carry no marker, so they never produce a false nudge.
220
+ * Every skills PARENT directory an ABX-capable agent scans for a `SKILL.md`, keyed by agent.
221
+ * These are the discovery locations the agents actually read (verified against each agent's docs):
222
+ * `.claude/skills` — Claude Code (and Copilot also reads it)
223
+ * `.agents/skills` the near-universal neutral dir: Cursor, Codex, Gemini, and Copilot all
224
+ * read it (Gemini/Cursor treat it as the canonical alias over their own dir)
225
+ * `abx skill install` writes the version-locked bundle into these; the drift check reads it back.
226
+ */
227
+ export const AGENT_SKILL_PARENTS = {
228
+ claude: '.claude/skills',
229
+ cursor: '.agents/skills',
230
+ codex: '.agents/skills',
231
+ gemini: '.agents/skills',
232
+ copilot: '.agents/skills',
233
+ };
234
+ /** Distinct skills-parent dirs to scan for a possibly-installed copy — the union of every agent's
235
+ * discovery dirs (including a few per-agent aliases), so drift detection finds the skill no matter
236
+ * which agent (or install route) put it there. Missing dirs are simply skipped. */
237
+ export const ALL_SKILL_PARENTS = [
238
+ '.claude/skills',
239
+ '.agents/skills',
240
+ '.cursor/skills',
241
+ '.gemini/skills',
242
+ '.github/skills',
243
+ '.copilot/skills',
244
+ ];
245
+ /**
246
+ * Parse `metadata.version` from a `SKILL.md`'s YAML frontmatter. Dependency-free on purpose: the
247
+ * frontmatter is the small, controlled block between the leading `---` fences, and the version is
248
+ * the only `version:` key we write there. Returns null if the file is missing/unreadable or
249
+ * declares no version.
250
+ */
251
+ export function readSkillVersion(skillMdPath) {
252
+ try {
253
+ const raw = readFileSync(skillMdPath, 'utf8');
254
+ const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
255
+ const front = fm ? fm[1] : '';
256
+ const vm = /(?:^|\n)\s*version:\s*["']?([\w.+-]+)["']?/.exec(front);
257
+ return vm ? vm[1] : null;
258
+ }
259
+ catch {
260
+ return null;
261
+ }
262
+ }
263
+ /**
264
+ * Versions of any locally-installed skill copy (project-local under CWD, and global under HOME),
265
+ * read from each copy's own `SKILL.md` frontmatter. The skill and CLI are co-versioned, but a
266
+ * separately-installed skill copy does NOT move when the CLI upgrades — this lets the notifier
267
+ * catch that drift. Because the version travels INSIDE `SKILL.md`, this works for every install
268
+ * route (bundled `abx skill install`, git-based `npx skills add`, or a manual copy) — not just
269
+ * the CLI's own installer.
170
270
  */
171
271
  export function installedSkillVersions() {
172
- const dirs = [
173
- join(process.cwd(), '.claude', 'skills', 'abx-self-host'),
174
- join(homedir(), '.claude', 'skills', 'abx-self-host'),
175
- ];
176
- const out = [];
177
- for (const dir of dirs) {
178
- try {
179
- const marker = join(dir, '.abx-skill-version');
180
- if (existsSync(marker))
181
- out.push(readFileSync(marker, 'utf8').trim());
182
- }
183
- catch {
184
- /* ignore an unreadable location */
272
+ const out = new Set();
273
+ for (const root of [process.cwd(), homedir()]) {
274
+ for (const parent of ALL_SKILL_PARENTS) {
275
+ const v = readSkillVersion(join(root, parent, SKILL_DIR_NAME, 'SKILL.md'));
276
+ if (v)
277
+ out.add(v);
185
278
  }
186
279
  }
187
- return out;
280
+ return [...out];
188
281
  }
189
- /** Filename of the version marker `abx skill install` writes beside an installed skill. */
190
- export const SKILL_VERSION_MARKER = '.abx-skill-version';
191
282
  //# sourceMappingURL=update-check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAC,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AACvC,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAEhC,MAAM,QAAQ,GAAG,4BAA4B,CAAC;AAC9C,MAAM,GAAG,GAAG,oBAAoB,CAAC;AACjC,mGAAmG;AACnG,8FAA8F;AAC9F,wFAAwF;AACxF,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAClC,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,iDAAiD;AAE1E;;;oFAGoF;AACpF,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAuB,CAAC;QACjG,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,0EAA0E;IAC1E,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IAC/C,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,4DAA4D;QAC5F,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACrB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACrB,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,EAAE,EAAE,CAAC;YACd,OAAO,CAAC,CAAC,CAAC,CAAC,mDAAmD;QAChE,CAAC;aAAM,IAAI,EAAE,EAAE,CAAC;YACd,OAAO,CAAC,CAAC;QACX,CAAC;aAAM,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,OAAe;IACrD,OAAO,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACzC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACnD,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE;KACrC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;0GAC0G;AAC1G,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAW,EAAE,GAAG,GAAG,GAAG;IAC9D,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;IACzD,IAAI,CAAC;QACH,8FAA8F;QAC9F,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,EAAE,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;QACpH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QACtD,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,QAAQ,CAAC,QAA0C;IACjE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzF,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC/B,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe,EAAE,GAAG,GAAG,GAAG;IACnE,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACxD,OAAO,QAAQ,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IAC/E,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;QACzB,IAAI,KAAK,GAAmE,EAAE,CAAC;QAC/E,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,IAAI,CAAC;gBAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;QACD,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;QACtD,MAAM,KAAK,GAAG,WAAW,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;QACnG,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,GAAG,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACpD,MAAM,GAAG,OAAO,IAAI,CAAC,WAAW,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC5F,IAAI,CAAC;gBACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;gBAC5C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAC,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAC,CAAC,CAAC,CAAC;YACzE,CAAC;YAAC,MAAM,CAAC;gBACP,sDAAsD;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AAED;oDACoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAA2B;IACzD,MAAM,EAAE,gBAAgB;IACxB,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE,gBAAgB;IACvB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,gBAAgB;CAC1B,CAAC;AAEF;;oFAEoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,iBAAiB;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,EAAE,GAAG,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,4CAA4C,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC;YAC3E,IAAI,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artblocks/abx-cli",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.11",
4
4
  "license": "MIT",
5
5
  "description": "ABX CLI ('abx') — the agentic UX surface of the Self-Host Toolkit (Layer 3). Deploy, index, serve, and demo a self-hosted ABX project end to end. Wraps the SDK; runs a different implementation and the protocol works identically.",
6
6
  "type": "module",
@@ -38,13 +38,16 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "viem": "^2.21.0",
41
- "@artblocks/abx-indexer": "0.1.0-alpha.0",
42
- "@artblocks/abx-sdk": "0.1.0-alpha.0",
43
- "@artblocks/abx-storage": "0.1.0-alpha.0",
44
- "@artblocks/abx-token-api": "0.1.0-alpha.0"
41
+ "@artblocks/abx-sdk": "0.1.0-alpha.4",
42
+ "@artblocks/abx-indexer": "0.1.0-alpha.5",
43
+ "@artblocks/abx-storage": "0.1.0-alpha.4",
44
+ "@artblocks/abx-token-api": "0.1.0-alpha.7"
45
45
  },
46
46
  "optionalDependencies": {
47
- "@artblocks/abx-effects": "0.1.0-alpha.0"
47
+ "@artblocks/abx-effects": "0.1.0-alpha.4"
48
+ },
49
+ "devDependencies": {
50
+ "playwright": "1.61.1"
48
51
  },
49
52
  "scripts": {
50
53
  "build": "tsc -b tsconfig.build.json"
package/skill/SKILL.md CHANGED
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: abx-self-host
3
- description: Launch and operate a self-hosted ABX NFT end to end with the ABX CLI (`abx`) on testnet — a 1/1 (`abx deploy`), a multi-token Series from a folder of media (`abx deploy-series`), or a generative/code drop (`abx deploy-code`). Covers on-chain vs off-chain metadata, storage custody (local disk, S3/R2, IPFS, Arweave), deploy + mint (now or pre-warmed at a predicted address), rendered thumbnails and on-chain traits for code art, primary sales via the shared fixed-price minter, and owner ops (transfer, refresh, re-point URIs, royalties, lock fields, pause/unpause, supply cap, delegate minting). Use when the user wants to self-host an ABX project, take an image to an NFT on testnet, deploy a collection from a folder of images, launch generative/code art, mint or run a primary sale, refresh a listing, operate a project they launched, choose a storage backend, or stand up hosting they own.
3
+ description: Launch and operate a self-hosted ABX NFT end to end with the ABX CLI (`abx`) on testnet — a 1/1 (`abx deploy`), a multi-token Series from a folder of media (`abx deploy-series`), or a generative/code drop (`abx deploy-code`). Covers on-chain vs off-chain metadata, storage custody (local disk, S3/R2, IPFS, Arweave), deploy + mint (now or pre-warmed at a predicted address), rendered thumbnails and on-chain traits for code art, primary sales via the shared fixed-price minter, and owner ops (transfer, refresh, re-point URIs, royalties, lock fields, pause/unpause, supply cap, delegate minting). Use when the user wants to self-host an ABX project, take an image to an NFT on testnet, deploy a collection from a folder of images, launch generative/code art, mint or run a primary sale, refresh a listing, operate a project they launched, choose a storage backend, stand up hosting they own, or point a project at a hosted/managed metadata provider with an API key.
4
+ compatibility: Drives the abx CLI (@artblocks/abx-cli). Co-versioned with it — install/refresh with `abx skill install` so this skill matches the CLI's `abx version`. Requires Node 22.5+.
5
+ metadata:
6
+ version: "0.1.0-alpha.11"
4
7
  ---
5
8
 
6
9
  # ABX Self-Host Toolkit (`abx`)
@@ -11,15 +14,21 @@ L3 agentic surface: image → live self-hosted NFT the creator owns — a **1/1*
11
14
 
12
15
  ## Read first (every session)
13
16
 
14
- - **YOU run the `abx` commands — never tell the creator to run one.** You have a shell; use it. Run `doctor`, `ls`, `--dry-run`, `tokenuri`, `state`, `refresh`, `balance`, etc. yourself and read the output — don't paste a command and wait for them to run it or copy back results. The creator's *only* hands-on step is approving in their **browser wallet** (`--sign`) or giving you a value you asked for (their address, a name). Even "next steps" after a deploy: **run the read-only ones** (`tokenuri` to prove it resolves) and **offer to run** the actions (`refresh`, `unpause`, `mint`) — don't hand over a list of commands to run. Exceptions: a genuinely interactive/again-in-their-env command (an OS login, `gcloud auth`), and the **in-chain Solidity lane's Foundry step** (`forge build/test/deploy` a renderer — `abx` never compiles/deploys Solidity; see [Code projects](#code-projects-generative--code-based-drops)) — then run it if you have the tool, else hand it over.
17
+ - **YOU run the `abx` commands — never tell the creator to run one.** You have a shell; use it. Run `doctor`, `ls`, `--dry-run`, `tokenuri`, `state`, `refresh`, `balance`, etc. yourself and read the output — don't paste a command and wait for them to run it or copy back results. The creator's hands-on steps are approving in their **browser wallet** (`--sign`), giving you a value you asked for (their address, a name), and **looking at the art in `abx preview`** — you run that command, but the URL it prints is theirs to open and play with ([Phase 0](#phase-0--make-the-work-first-skip-every-gate-below-until-its-good)). Even "next steps" after a deploy: **run the read-only ones** (`tokenuri` to prove it resolves) and **offer to run** the actions (`refresh`, `unpause`, `mint`) — don't hand over a list of commands to run. Exceptions: a genuinely interactive/again-in-their-env command (an OS login, `gcloud auth`), and the **in-chain Solidity lane's Foundry step** (`forge build/test/deploy` a renderer — `abx` never compiles/deploys Solidity; see [Code projects](#code-projects-generative--code-based-drops)) — then run it if you have the tool, else hand it over.
18
+ - **Version drift — act on it, never just report it.** Two checks, both yours to fix, both **before** you deploy anything:
19
+ - **Skill ⇄ CLI.** This skill is co-versioned with the CLI. Run `abx version`, compare to this file's frontmatter `metadata.version`. Differ → `abx skill install`, then reload the skill before continuing.
20
+ - **CLI ⇄ npm.** If any `abx` command prints **`update available`**, upgrade it yourself: `npm i -g @artblocks/abx-cli@latest` (global install) or `npm i --save-dev @artblocks/abx-cli@latest` (project-local — match however it was installed), then `abx skill install`, then reload. Do **not** relay the notice and carry on: a stale CLI can hold canonical addresses that have since moved, so it deploys against dead singletons, and a mid-run `… is not a function` is usually this. You have a shell — upgrading is your job, not the creator's.
21
+ - **Resolve the CLI before you install anything — local beats global.** Probe `./node_modules/.bin/abx version` (project-local), then `abx version` (global); only install if both miss, and default to the **project-local** `npm install --save-dev @artblocks/abx-cli`. The package is `@artblocks/abx-cli`; `@artblocks/abx-sdk` is the library and ships no binary. **Never probe with `npx abx`** (the bare name is a squat, and `--no-install` doesn't save you — it can serve a stale cached binary). Full ladder → [Setup](#setup--environment).
15
22
  - **`abx doctor` first, always** — full preflight (Node, pnpm, RPC, signing key, storage). Fix any ✗ before deploying ([Setup](#setup--environment)). A missing public-base-url is not a "set up IPFS" signal: for tiny art go on-chain, for larger art pick an off-chain backend — see [Quick start](#quick-start).
16
- - **Never collect secrets in chat.** Keys, `PINATA_JWT`, S3 secrets → the project's `.env`. The Arweave/Turbo key is a CLI-managed file (`.abx-self-host/arweave-key.json`) — never paste it. Name the var/file; never take the value.
23
+ - **Never collect secrets in chat, and never `cat`/`grep` `.env`.** Keys, `PINATA_JWT`, S3 secrets, provider API keys → the project's `.env`. The Arweave/Turbo key is a CLI-managed file (`.abx-self-host/arweave-key.json`) — never paste it. Name the var/file; never take the value. **To see what's configured, ask the tool, not the file:** `abx doctor` and `abx remote` report each credential as set/unset without ever printing one. Reading `.env` spills every secret in it into the transcript — irreversible, and a plain `abx doctor` tells you the same thing.
17
24
  - **Testnet only today** — every launch is on a testnet: **Base Sepolia by default** (`ABX_CHAIN` unset), with **Sepolia** also shipped (`ABX_CHAIN=sepolia`). Say "testnet"; don't imply mainnet. **Testnet IS the preview + e2e environment**: it runs the *real* wiring (renderers, generator, on-chain tokenURI assembly), so a creator should deploy there, inspect the actual result (`abx tokenuri` / the live view / `abx verify`), confirm it looks right, and only *then* go to mainnet — no separate local "preview" is as faithful as the real testnet drop, and a testnet deploy is ~free + ~minutes. **One cross-chain gotcha: on-chain library deps (`--dep p5@…`) resolve to on-chain bytes only where an Art Blocks dependency registry exists — that's Sepolia, NOT Base Sepolia.** A no-dependency script (vanilla JS/GLSL) goes fully on-chain on either; a drop that needs a registry-hosted library on-chain must target `ABX_CHAIN=sepolia` (or run the resolver lane).
18
25
  - **Scope today = ERC-721 on testnet.** The shipped token standard is **ERC-721** (a 1/1, or a **Series** for many tokens), on Base Sepolia (default) or Sepolia via `ABX_CHAIN`. There is **no `--chain` flag (pick the chain with `ABX_CHAIN`) and no `--erc1155`/`--standard` flag** — don't invent one; mainnet + ERC-1155 are roadmap, not something you flip here. Map the ask to what ships: **"an edition of N" / "N copies"** → an ERC-721 **Series** (`abx deploy-series`, N tokens; for a priced sale of one piece, a 1-token Series). If a creator needs a true ERC-1155 shared-supply edition or an unsupported chain, say plainly it's not in the toolkit today rather than fabricating a recipe.
19
- - **Two gates decide everything: (1) demo or real? (2) who signs?** Settle both first.
26
+ - **Is the work finished yet?** If the creator is still *making* the piece, you're in **[Phase 0](#phase-0--make-the-work-first-skip-every-gate-below-until-its-good)** iterate on the art and keep every deploy question off the table until they say ship. The gates below apply to launching something that already exists.
27
+ - **Two gates decide everything: (1) demo or real? (2) who signs?** Settle both first — *once there's something to launch*.
20
28
  - **Confirm the full config before any on-chain write** ([readout](#confirm-before-sending)); wait for go-ahead. Never invent a field silently (name/symbol from filename, an auto description) — show it, flag it `inferred`.
29
+ - **Never hand-build a service URL — ask the chain, then check the reference.** A contract commits its own metadata URL on-chain, so `abx tokenuri <addr>` (token) and `abx contracturi <addr>` (ERC-7572 collection) give you the answer *and* follow it — no route grammar to remember, no curl. **A 404/error on a URL you constructed is evidence about your URL, never about the service.** Don't infer a path from a similar-looking one (dropping the token id off `/t/<chain>/<addr>/<id>` does **not** give collection metadata — that's `/c/<chain>/<addr>`); look it up in [hosting.md](reference/hosting.md#token-api-the-resolver). Before telling anyone a service is broken, reproduce it with a **CLI command** — a real service miss says which of three things it is in a machine `code` (`invalid_request` = your path shape · `unknown_route` = no such route here · `not_registered` = this node doesn't index that contract), and none of those mean "down".
21
30
  - **Safe to explore:** `abx <cmd> --help` and `abx deploy --dry-run` never send. You never run a real write just to learn flags.
22
- - **`deploy` returns; `demo`/`serve` block** (they serve) — background them or warn.
31
+ - **`deploy` returns; `demo`/`serve`/`preview` block** (they serve) — background them or warn. Background `abx preview` and relay its URL, then keep working while the creator looks; `--shoot` is the one preview mode that exits on its own.
23
32
 
24
33
  ## Which command — what are you launching?
25
34
 
@@ -35,13 +44,32 @@ Route by the **content** first, then apply the gates below. The three paths diff
35
44
 
36
45
  **Planning a priced primary sale? Decide 1/1 vs Series BEFORE deploying — it's irreversible.** The shared fixed-price minter sells a **Series** (mint-on-purchase); a plain `abx deploy` **1/1 has no minter/pause/payee**, so its only post-mint move is `abx transfer` (settle an off-chain sale). To run a native fixed-price sale of even a *single* piece, deploy it as a **1-token Series** (`abx deploy-series --count 1`), not a 1/1. abx has **no secondary-listing feature** — reselling a held token means an external marketplace or a manual `transfer`. Full detail: [operating.md → Selling](reference/operating.md#selling--the-shared-fixed-price-minter).
37
46
 
47
+ ## Phase 0 — make the work first (skip every gate below until it's good)
48
+
49
+ **If the creator is still making the piece, you are in the studio, not in a deploy. Stay there until they say ship.** The gates and decisions below are for *launching* something that already exists — reaching for them while someone is still designing is the single most common way this skill feels wrong to use. A real session: the creator said *"I want to work on one with you"* and got asked about metadata resolution, hosting, and wallet ownership before a single pixel existed. They had to push back with *"let's work on actually designing the piece together first."* Don't make them.
50
+
51
+ **Which mode are you in?**
52
+ - **They handed you a finished file** (`sketch.js`, a build dir, an image folder) → skip this section, go to [Gate 1](#gate-1--demo-or-real-launch).
53
+ - **They brought an idea, a reference, a vibe, or "let's make one together"** → Phase 0. The deploy is a footnote at the end of an afternoon of work; treat it that way.
54
+
55
+ **During Phase 0, these are OFF the table** — do not ask, do not "just quickly confirm," do not pre-emptively lay out the tradeoffs: hosting/lane, thumbnails, traits-on-chain, storage permanence, wallet address, supply cap, royalties, mint count, name/symbol. Every one of them is answerable in five minutes *after* the art is right, and asking early reads as pressure to ship something half-made. The **one** exception is a constraint that changes what you'd *write*: if they want an on-chain library (`p5`), say early that on-chain deps mean `ABX_CHAIN=sepolia` — that's an authoring constraint, not a deploy decision.
56
+
57
+ **The loop** — depth + the `--shoot` details → [reference/code-projects.md → Studio loop](reference/code-projects.md#studio-loop--iterate-on-the-art-before-you-deploy-anything):
58
+
59
+ 1. **Write against the real runtime contract from the first draft** — `abx.tokenData.seed` for randomness, `abx.traits({…})` for features. Not `Math.random()` "for now": a piece prototyped on `Math.random()` looks finished and then deploys as N identical tokens, and retrofitting the seed late means re-tuning every visual you just approved.
60
+ 2. **Run `abx preview --script art.js` and give the creator the URL.** It serves the *same document the generator serves* (real `abx.js`, real tokenData) on `localhost:8788`, so they get a seed shuffle, real inputs for every `--schema` PostParam, a live traits readout, and `/grid` for N seeds at once. **This is the one place you hand over a link instead of running it for them** — the art is theirs to judge, and an animated piece cannot be judged from a screenshot. Don't hand-roll a preview page; a stub you write yourself will run a sketch that reads its seed wrong.
61
+ 3. **Edit and tell them to refresh.** The program is re-read from disk per render — no restart, no watcher. To check your own work between rounds (you have no browser), `abx preview --script art.js --shoot ./frames` renders the same document headlessly and flags the two silent killers: no traits reported, or identical traits across every seed.
62
+ 4. **Take feedback and go again.** Expect several rounds. Rounds are the point — "add faces to the shapes" is a normal Phase 0 request, not scope creep.
63
+
64
+ **Exit only on an explicit ship signal** ("let's deploy this", "I'm happy with it"). Then run `abx inspect <script>` and open the deploy decisions — and say plainly that some are irreversible, so it's worth a few minutes ([Code projects](#code-projects-generative--code-based-drops)). If *you* feel the pull to start the deploy conversation while they're still iterating: don't. Ask what they want to try next.
65
+
38
66
  ## Gate 1 — demo or real launch?
39
67
 
40
68
  | | **Demo** (`abx demo`) | **Real launch** (`abx deploy` → operate) |
41
69
  |---|---|---|
42
70
  | Art | generative-from-address | the creator's `--image` |
43
- | Storage | `fs` (throwaway) | a permanence decision |
44
- | Host URL | `localhost:8787` | a public URL baked on-chain (off-chain custody only) |
71
+ | Storage | none the SVG is inlined **on-chain** | a permanence decision |
72
+ | Host URL | none — nothing is hosted, nothing to bake | a public URL baked on-chain (off-chain custody only) |
45
73
  | Key | any funded testnet key (the active `ABX_CHAIN`) | the wallet that should **own** it |
46
74
  | Decisions | none — just run it | the framework below |
47
75
 
@@ -119,7 +147,7 @@ Files natural-sort into **tokens `0…N-1`** (`--count N` uses the first N); a t
119
147
  A **program is the content** (`abx deploy-code` → a `SeriesCode`): output is a function of live on-chain state (`tokenData`: coordinates + `seed` + PostParams), injected at view time. Everything from a [Series](#series-multi-token-drops) applies (mint order, lanes, identity, supply cap, minter, pause). This section is the **decision tree**; the operating depth — what to keep running, the resume loop, verify steps, render ops, lane internals, the arweave delay, selling — lives in **[reference/code-projects.md](reference/code-projects.md)**.
120
148
 
121
149
  **Infra fork FIRST (before any lane talk): a code project's thumbnail is *rendered* off-chain, so it ALWAYS needs a PUBLIC home you provide — there is NO zero-infrastructure code drop, and "fully on-chain" does NOT mean "nothing to run."** Settle the shape with the creator up front:
122
- - **Off-chain resolver** (`--public-base-url` + an effects runner, ~a few $/mo) — **the default for a drop you'll sell.** Auto-renders every mint + param change, serves traits with no Solidity, and stays **maneuverable** (metadata/serving evolve with no on-chain surgery) while marketplaces fetch a **small** `tokenURI`.
150
+ - **Off-chain resolver** (`--public-base-url` + rendering, ~a few $/mo self-hosted) — **the default for a drop you'll sell.** Auto-renders every mint + param change, serves traits with no Solidity, and stays **maneuverable** (metadata/serving evolve with no on-chain surgery) while marketplaces fetch a **small** `tokenURI`. A **managed provider whose descriptor says `render.attached`** covers both halves with one API key — no effects runner to stand up ([hosting.md → Managed providers](reference/hosting.md#managed-providers--a-resolver-someone-else-runs---remote-name)).
123
151
  - **Fully on-chain** (`--onchain-uri --image-base <a bucket you own>`) — maximal durability, no always-on service. Trade-offs: the whole ~200KB+ doc rides each `tokenURI` (some marketplace/indexer reads choke), **manual** stills (`abx render`), on-chain traits need a deployed renderer, later changes are on-chain re-points. Choose it deliberately when permanence outweighs maneuverability. *(The one zero-infra-AND-on-chain exception: the in-chain **Solidity** lane below.)*
124
152
 
125
153
  **Writing the program yourself (the creator brought an *idea*, not a file)? There's ONE runtime contract — get it right or the drop is silently broken** (seed never injects → every token identical; traits empty). The program reads state via **`abx.tokenData`** (a flat object: `.seed`, and each `--schema` key flat, e.g. `.palette`) and reports traits via **`abx.traits({…})`** — never an invented global (`window.tokenData`, `window.tokenTraits`) and never "defensively across variants." `abx.traits()` is the ONLY thing captured into `attributes`, on the resolver lane too. Verify with `abx inspect` (its **PostParams** + **Traits** lines reflect what the program actually reads/reports — if they're empty but you intended a param/traits, you read it the wrong way), THEN pick a lane. Full contract: [reference/code-projects.md → Authoring the program](reference/code-projects.md#authoring-the-program--the-abxjs-runtime-contract-get-this-right-first).
@@ -154,15 +182,15 @@ Master call is **custody × mutability**:
154
182
  | | **Mutable** (name/desc/traits may change) | **Immutable** (never changes) |
155
183
  |---|---|---|
156
184
  | **Tiny static** (≲ 24 KB/file, ≲ 256 KB total) | **on-chain renderer** — `--onchain-image --compress fastlz`. No host, mutable via `set-field`, permanent. | on-chain renderer + `lock-field` + `lock-uri` once it resolves. |
157
- | **Bigger / dynamic** (most PNG/JPEG) | **image off-chain, JSON on-chain, no server** — `--onchain-uri --backend arweave` (or `ipfs`). Renderer assembles JSON pointing at the bytes; many files → one `url-template` (O(1)). For metadata you edit often, a **hosted resolver** instead (`abx deploy-resolver`, [hosting.md](reference/hosting.md)). Not fully on-chain (~200 gas/byte). | image off-chain (Arweave = permanent) + on-chain renderer + `lock-field`/`lock-uri`. Or a frozen `ipfs://` override + `lock-uri`. |
185
+ | **Bigger / dynamic** (most PNG/JPEG) | **image off-chain, JSON on-chain, no server** — `--onchain-uri --backend arweave` (or `ipfs`). Renderer assembles JSON pointing at the bytes; many files → one `url-template` (O(1)). For metadata you edit often, a **resolver** instead — a managed provider or your own (`abx deploy-resolver`), [hosting.md](reference/hosting.md). Not fully on-chain (~200 gas/byte). | image off-chain (Arweave = permanent) + on-chain renderer + `lock-field`/`lock-uri`. Or a frozen `ipfs://` override + `lock-uri`. |
158
186
 
159
187
  **Four patterns, by where bytes live × how `tokenURI` resolves:**
160
188
  1. **Fully on-chain** (`--onchain-image`) — bytes + JSON on-chain. Tiny art only.
161
189
  2. **Image off-chain, JSON on-chain, no server** (`--onchain-uri --backend arweave|ipfs|cloud`) — the sweet spot for static art. Arweave/IPFS (permanent, content-addressed) or your S3/CDN (`--backend cloud --public-base <url>`; centralized, mutable). Many files → one `url-template`.
162
- 3. **Hosted resolver** (`--public-base-url` + `abx deploy-resolver`) — for mutable/dynamic metadata; you run a node.
190
+ 3. **Remote resolver** (`--public-base-url` + a node) — for mutable/dynamic metadata; **self-hosted** (`abx deploy-resolver`, you run it) or a **managed provider** (an API key, they run it). Same interface — swap with one re-point.
163
191
  4. **Inline SVG on-chain** — self-contained vector art inlined into `tokenURI`. For a **1/1** that's `abx deploy … --onchain-uri`; for a **Series** of tiny SVGs use `abx deploy-series … --onchain-image --compress fastlz` (bare `--onchain-uri` on a folder does NOT inline the images — it's the image-custody flag `--onchain-image` that puts SVG bytes on-chain per token).
164
192
 
165
- **Picking IPFS (or Arweave) does NOT mean running a server.** The `--onchain-uri --backend ipfs|arweave` path (pattern 2) bakes the image's public **gateway** URL into on-chain JSON — a pinning service's read endpoint (a *dedicated* Pinata gateway for IPFS), not a resolver you host. So when a creator chooses IPFS, **default to this no-server path** — image on IPFS, JSON on-chain, nothing to keep running (just keep the pin alive). You only need a **hosted resolver** (pattern 3) if they want *freely editable* metadata. Never present IPFS as blocked on "a public URL" or "a server always online": the gateway belongs to the pinning service and the JSON lives on-chain. (The one real input IPFS needs is `PINATA_JWT` in `.env` for pinning — that's an API upload, not a host.)
193
+ **Picking IPFS (or Arweave) does NOT mean running a server.** The `--onchain-uri --backend ipfs|arweave` path (pattern 2) bakes the image's public **gateway** URL into on-chain JSON — a pinning service's read endpoint (a *dedicated* Pinata gateway for IPFS), not a resolver you host. So when a creator chooses IPFS, **default to this no-server path** — image on IPFS, JSON on-chain, nothing to keep running (just keep the pin alive). You only need a **resolver** (pattern 3 — managed or self-hosted) if they want *freely editable* metadata. Never present IPFS as blocked on "a public URL" or "a server always online": the gateway belongs to the pinning service and the JSON lives on-chain. (The one real input IPFS needs is `PINATA_JWT` in `.env` for pinning — that's an API upload, not a host.)
166
194
 
167
195
  **No-server tradeoff (patterns 1, 2, 4):** with the on-chain renderer only the *image* is off-chain — any **description / traits / animation_url live on-chain** (gas to write, permanent, lockable), vs a hosted resolver where they're free to edit. Cheap (a shared value is **one collection-scope field**, not one per token — the renderer falls back token→collection), but the creator should choose "no server" knowing their text metadata is on-chain.
168
196
 
@@ -173,13 +201,15 @@ Get decisions 1–2 right before deploy (image commitment + resolver URL are wri
173
201
  - **`arweave` is nearly as easy as `fs` for small art** — Turbo default: **under 100 KB free, no setup** (a managed `.abx-self-host/arweave-key.json` minted on first upload; back it up with `abx storage backup-key`). Choose per command with `--backend` (stateless, no config file); a backend missing its secret falls back to `fs`.
174
202
  - **Who pays is a lane (`--storage-signer`)** — Turbo credits attach to an identity (managed key · `.env` key · browser wallet). **Before any top-up, check BOTH balances** (`abx storage balance --backend arweave` shows the managed key AND the wallet — spend the wallet's credits if present). On an upload error surface it verbatim — `…already been uploaded…` is *success* (dedup); don't reflexively top-up or switch to IPFS. Full lanes + failure playbook → [hosting.md](reference/hosting.md#arweave-via-turbo--the-easy-permanent-path-read-before-quoting-setup).
175
203
 
176
- **2. Public host URL**where the resolver runs (**off-chain custody only**). Baked into `tokenURI` at deploy, so the CLI **refuses an off-chain deploy without a public URL** (`ABX_PUBLIC_BASE_URL` or `--public-base-url https://…`) and **never bakes localhost** (that token resolves for no one). No exceptions.
204
+ **2. Public host URL — and who runs the resolver** (**off-chain custody only**). Baked into `tokenURI` at deploy, so the CLI **refuses an off-chain deploy without a public URL** (`ABX_PUBLIC_BASE_URL` or `--public-base-url https://…`) and **never bakes localhost** (that token resolves for no one). No exceptions.
177
205
  - **First ask whether you need a host at all** — tiny art is cheaper and more durable on-chain (no host). For bigger art, Arweave (no host to run) beats a resolver unless you need mutability or serve many files.
178
- - **Tunnels (ngrok/cloudflared) are preview-onlynever bake one on-chain** (dies on sleep, rotates on restart). A real launch puts the resolver on a host you control under your own domain (move = a DNS re-point).
206
+ - **A named remote is already configured (`ABX_REMOTE_<NAME>_URL` in `.env`)? Use it.** The creator already chose a provider don't stand up new infrastructure beside it. **Run `abx remote <name>` FIRST, before registering anything**: it prints the provider's chain coverage + whether rendering is managed, and it *validates the key* (`401` = the token in `ABX_REMOTE_<NAME>_TOKEN` is stale/wrong → they replace the value in `.env`; `403` = the key is fine but not authorized for this contract/chain → provider-side scoping, don't touch the key). Then register: `abx add <addr> --remote <name>`. Testing a replacement key without editing `.env` first: `abx remote <name> --remote-token <new-key>`.
207
+ - **Otherwise, two equal ways to have a resolver, one config change apart.** A **managed provider** — one base URL + one API key, no cloud account, nothing to keep alive; often **managed rendering** too, so a code drop needs no effects runner (**lead with this when the creator doesn't already run infrastructure or doesn't want to** — [hosting.md → Managed providers](reference/hosting.md#managed-providers--a-resolver-someone-else-runs---remote-name)). Or **self-host** (`abx deploy-resolver`, [hosting.md](reference/hosting.md)) — the creator owns the node and the cloud account. Same interface, same commands; a project moves between them with one re-point + re-register. **No provider key in hand and none to get? Self-host is the fully-supported path today** — the provider market is only starting to form; never invent or recommend a provider that isn't in front of you.
208
+ - **Tunnels (ngrok/cloudflared) are preview-only — never bake one on-chain** (dies on sleep, rotates on restart). A real launch puts the resolver on a host you control under your own domain (move = a DNS re-point), or behind a provider.
179
209
 
180
210
  **3. Identity** — `--name`, `--symbol`, `--royalty-bps` (default 500 = 5%), `--description "…"`, `--external-url <url>` (both served in the metadata — set them or the description is boilerplate). Owner + royalty receiver = the deploying wallet. These default to off-chain operator metadata (editable via `abx add <addr> --description "…"`). For a description that should outlast any node, add `--description-onchain` (or later `abx set-field <addr> --field description --text "…"`) → on-chain, freezable via `lock-field`; the resolver prefers the on-chain value. This is the per-field on-chain model — any field on-chain or off, one active `representation` (inline · reader · keccak256 · arweave · ipfs · url). Background: [metadata model](https://abx.docs.artblocks.io/protocol/metadata/).
181
211
  - **Credit + license** — deploy flags `--artist "…"` · `--license "…"` (also `--display-notes`, `--artist-links`) bake authorship + rights ON-CHAIN in the deploy tx (all three deploy commands); or set/change them later with `abx set-field <addr> --collection --field artist|license --text "…"`. Reserved collection fields served in `contractURI`, on any type (1/1 · Series · code). Detail: [operating.md → Authorship + rights](reference/operating.md#authorship--rights-credit--license).
182
- - **Propose a real name/symbol and confirm — never silently bake a generic folder-name guess.** A folder called `series`/`images`/`photos` infers junk ("Series" / "SRS"), and the CLI *refuses* demo defaults without `--name`/`--symbol` precisely because on-chain identity is effectively permanent. Suggest a specific title + a short ticker-style symbol drawn from the actual work, and get an explicit yes before deploying. Inference is a suggestion to confirm, not a default to ship — if the folder name is generic, say so and ask rather than proposing it.
212
+ - **Propose a real name/symbol and confirm — never silently bake a generic folder-name guess.** A folder called `series`/`images`/`photos` infers junk ("Series" / "SRS"), and on all three deploy commands the CLI *refuses* a real send that would bake its own placeholder identity (`--name`/`--symbol` missing) because on-chain identity is effectively permanent. **In `--dry-run` the same check only warns** (so a preview still runs before you have the creator's title); don't read that warning as "the CLI allows it" — the real deploy stops. Suggest a specific title + a short ticker-style symbol drawn from the actual work, and get an explicit yes before deploying. Inference is a suggestion to confirm, not a default to ship — if the folder name is generic, say so and ask rather than proposing it.
183
213
 
184
214
  **4. Image placement** — `--image <path>` (png · jpg · gif · svg · webp). The on-chain keccak256 (`image` field) anchors integrity; size is bounded by the backend, not the chain.
185
215
  - *Off-chain:* the served `image` is the backend's **gateway HTTPS URL** (`https://<gateway>/ipfs/<cid>`), not raw `ipfs://` (wallets/marketplaces can't render that). So off-chain needs a pinning service + a **public** gateway — with Pinata use a **dedicated** gateway (`--gateway https://<you>.mypinata.cloud`); a local kubo gateway is preview-only. The keccak stays the anchor → move gateways without a tx.
@@ -199,6 +229,8 @@ Get decisions 1–2 right before deploy (image commitment + resolver URL are wri
199
229
 
200
230
  Run `abx deploy --dry-run` for real values, present **this exact shape** — one row per on-chain value — then wait for go-ahead. **Mirror the dry-run's values; don't compose your own.**
201
231
 
232
+ **No signing key in `.env` yet? `--dry-run` still needs a deployer address — pass `--for 0x<the creator's wallet>`.** The address is a pure function of (factory, salt, deployer), so a preview can't compute it from nothing; it signs nothing, so no key is involved. Ask the creator for their wallet address once, up front — it's also what the real wallet-lane deploy takes (`--sign --for 0x…`).
233
+
202
234
  ```
203
235
  Deploy config — confirm before I send (everything below is written on-chain):
204
236
 
@@ -275,24 +307,48 @@ Once it's live, cover these in plain language; don't wait to be asked.
275
307
 
276
308
  ## Setup + environment
277
309
 
278
- `abx` is installed and on your PATH; it needs **Node ≥ 22.5** at runtime (the projection store uses built-in SQLite). Start with `abx doctor` (preflight: signing key/wallet, RPC, canonical factory, storage).
310
+ `abx` needs **Node ≥ 22.5** at runtime (the projection store uses built-in SQLite).
311
+
312
+ ### Find `abx` before you install it — local first, then global
313
+
314
+ **Resolve in this order and use the first hit.** Don't jump to a global install; a project-local CLI is pinned in the creator's `package.json` (reproducible, and what `abx skill install` version-locks against), so it wins whenever it exists:
315
+
316
+ ```bash
317
+ ./node_modules/.bin/abx version # 1. project-local — PREFER this (invoke as `npx abx <cmd>` once you know it's there)
318
+ abx version # 2. a global install already on PATH
319
+ ```
320
+
321
+ - **Probe the local binary by PATH, never with `npx abx`.** The bare `abx` name on npm is an **unrelated squatted package**, so a plain `npx abx` with nothing local *downloads that*. And `--no-install` does **not** make the probe safe or trustworthy: it only skips a *fresh download*, and npm will still happily run any `abx` binary sitting in the npx cache from an earlier run — which can be a stale version (a real sweep saw `--no-install` report a months-old build as the project's CLI) or, if a plain `npx abx` was ever run on that machine, the squatted package itself. `./node_modules/.bin/abx` is unambiguous: it exists in THIS project or it doesn't.
322
+ - In the **abx source repo** (contributor), neither applies: use `pnpm abx <cmd>`, which runs the live `tsx` source.
323
+
324
+ **Nothing found → install.** The package is **`@artblocks/abx-cli`** (not `@artblocks/abx-sdk` — that's the library, and installing it gets you no `abx` binary; a real session lost a cycle to exactly that mistake):
325
+
326
+ | Situation | Install | Then invoke as |
327
+ |---|---|---|
328
+ | The creator has a project dir (a `package.json`) — **default** | `npm install --save-dev @artblocks/abx-cli` | `npx abx …` |
329
+ | No project, or they asked for a machine-wide tool | `npm install -g @artblocks/abx-cli` | `abx …` |
330
+
331
+ Ask before installing **globally** — it's a machine-wide change to their PATH, and the per-project install is the reversible one. A project install needs no permission beyond the usual.
332
+
333
+ Whichever you land on, **keep using that same invocation for every command in the session** (`npx abx …` vs `abx …`) — don't mix them, or you'll silently drive two different CLI versions. Then run `abx doctor` (preflight: signing key/wallet, RPC, canonical factory, storage).
279
334
 
280
335
  `.env` (in the creator's project dir) = **secrets only**:
281
336
  - **Signing:** a key (`SEPOLIA_FUNDED_PK` / `ABX_DEPLOYER_PK` / `SEPOLIA_WALLET_PK`) is needed ONLY for hot/unattended signing. If the creator owns a wallet, prefer **`--sign`** — no key in `.env`. `doctor`'s missing-key ✗ is **not fatal** on the `--sign` path.
282
- - `ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL` (hosted-resolver custody only), optional `OPENSEA_API_KEY`, optional `ABX_RESOLVER_ADMIN_TOKEN` (`deploy-resolver` generates it), plus any backend secret.
337
+ - `ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL` (remote-resolver custody only), optional `OPENSEA_API_KEY`, plus any backend secret.
338
+ - **Two resolver credentials — don't mix them up.** `ABX_RESOLVER_ADMIN_TOKEN` = **a node you run** (`deploy-resolver` generates it; it's the operator secret, and bare `--remote` uses it). `ABX_REMOTE_<NAME>_TOKEN` (+ `_URL`) = **a managed provider's per-account API key** for `--remote <name>` (same name normalization as `ABX_RPC_URLS_<CHAIN>`). A named remote deliberately never falls back to the node-admin token, so putting a provider key in `ABX_RESOLVER_ADMIN_TOKEN` silently won't work. Only `_URL`/`_TOKEN` are read — `ABX_REMOTE_<NAME>_KEY` is ignored (the CLI now flags a near-miss name).
283
339
 
284
340
  <sub>Working from the abx **source repo** (contributor)? `pnpm install`, then `pnpm abx <cmd>` or `pnpm sandbox`. Every command below is identical.</sub>
285
341
 
286
- **Indexing reads the event log via `eth_getLogs` from the contract's deploy block** — the CLI records it at deploy and forwards it to a resolver on `add` (discovering it on-chain for a contract it didn't deploy here). So a normal deploy→index scans a small, recent window and is fast on **any** RPC, and re-index is incremental (resumes from the last block). It **auto-chunks**, so a range cap never yields *wrong* state — but don't wave a cap away: a **from-genesis** or long-span reconstruction, or a resolver serving many code projects under load, is slow and rate-limit-prone on a getLogs-**range-capped** endpoint. `abx doctor` rates each endpoint and flags a capped one — treat that as a real infra signal, and give a resolver you'll run under load a range-generous **archive** RPC. **If indexing is slow, or a hosted resolver won't serve, check the scan floor FIRST (is it scanning from block 0?), not the RPC tier** — that mis-diagnosis is a known trap. RPC deep-dive + troubleshooting → [reference/setup.md](reference/setup.md).
342
+ **Indexing reads the event log via `eth_getLogs` from the contract's deploy block** — the CLI records it at deploy and forwards it to a resolver on `add` (discovering it on-chain for a contract it didn't deploy here). So a normal deploy→index scans a small, recent window and is fast on **any** RPC, and re-index is incremental (resumes from the last block). It **auto-chunks**, so a range cap never yields *wrong* state — but don't wave a cap away: a **from-genesis** or long-span reconstruction, or a resolver serving many code projects under load, is slow and rate-limit-prone on a getLogs-**range-capped** endpoint. `abx doctor` rates each endpoint and flags a capped one — treat that as a real infra signal, and give a resolver you'll run under load a range-generous **archive** RPC. **If indexing is slow, or a hosted resolver won't serve, ASK IT: `abx status <addr> [--remote <name>]`** reports the lifecycle (`queued`→`backfilling`→`live`, plus `stale`/`failed` with a machine-readable cause) — so "still catching up" and "broken" stop looking alike. Then **check the scan floor (is it scanning from block 0?) before the RPC tier** — that mis-diagnosis is a known trap. RPC deep-dive + troubleshooting → [reference/setup.md](reference/setup.md).
287
343
 
288
344
  ## Reference files
289
345
 
290
346
  - **Public docs — the human-facing companion** at **https://abx.docs.artblocks.io** (quickstart, guides, the CLI/SDK reference, the protocol model). This skill is YOUR operating manual and stays authoritative for how to drive the CLI; the docs site is what you **link the creator to** for background/onboarding, and a place you can read if you want the protocol rationale behind a command. Don't send the creator commands to run (you run them) — send them the docs to *read*.
291
347
  - **Code projects — operating depth** (what to keep running, the resume loop, verify-it-resolves, render ops, `--onchain-uri`/`--image-base`/traits internals, arweave delay, `deploy-code` flags, mint timing/pause/supply) → **[reference/code-projects.md](reference/code-projects.md)**
292
348
  - **Operating an existing project** (owner ops, **artist credit + license fields**, **attaching files / the data plane**, selling via the shared minter, `abx mint-page`, moving hosting, resolver→resolver `migrate`) → **[reference/operating.md](reference/operating.md)**
293
- - **Hosting infrastructure** (storage backends, Turbo lanes + failure playbook, `deploy-resolver`, `deploy-effects`, local-vs-remote stores, token API routes, Docker) → **[reference/hosting.md](reference/hosting.md)**
349
+ - **Hosting infrastructure** (storage backends, Turbo lanes + failure playbook, **managed providers + named remotes + the service descriptor**, `deploy-resolver`, `deploy-effects`, local-vs-remote stores, token API routes, Docker) → **[reference/hosting.md](reference/hosting.md)**
294
350
  - **Environment detail** (RPC selection + failover, multi-chain, troubleshooting) → **[reference/setup.md](reference/setup.md)**
295
- - **Troubleshooting — "my NFT looks wrong"** (gray placeholder, stale-on-marketplace, tokenURI reverts, localhost baked, "not registered") — diagnose before acting → **[reference/troubleshooting.md](reference/troubleshooting.md)**
351
+ - **Troubleshooting — "my NFT looks wrong"** (gray placeholder, stale-on-marketplace, tokenURI reverts, localhost baked, "not registered", **registered-but-serving-nothing / indexing status**) — diagnose before acting → **[reference/troubleshooting.md](reference/troubleshooting.md)**
296
352
 
297
353
  ## Guarantees
298
354
 
@@ -45,6 +45,41 @@ When a creator arrives with an *idea* and you write the program, it must read it
45
45
 
46
46
  **`abx inspect <script>` is your author-time check** — iterate the script against it before picking a lane: its **PostParams** list must show every collector key you intend (if it says "none detected" but you meant `palette` to be collector-set, you're reading it the wrong way), and its **Traits** line must not say "no traits reported" if you want filterable traits. (A Solidity in-chain renderer is a *different* contract — see [In-chain Solidity SVG](#in-chain-solidity-svg--the-zero-dependency-lane); the `abx.js` contract above is for a JS `--script`/`--code-dir` program.)
47
47
 
48
+ ## Studio loop — iterate on the art before you deploy anything
49
+
50
+ [← Phase 0 in SKILL.md](../SKILL.md#phase-0--make-the-work-first-skip-every-gate-below-until-its-good). When the creator is still designing, your job is to make the work **visible, interactive, and fast to change**. One command does it:
51
+
52
+ ```bash
53
+ abx preview --script art.js --schema "palette:HexColor:TokenOwner" # → http://localhost:8788
54
+ ```
55
+
56
+ **Give the creator the URL and let them drive.** This is the one place in the toolkit where handing over a link is right — the art is theirs to judge, and a browser they control is the only honest way to judge it. The studio gives them a seed shuffle, real inputs for every PostParam they declared, a live traits readout, and `/grid` for N seeds at once. `/view` is the bare document.
57
+
58
+ **Why a server and not a screenshot sweep:** a still flattens every time-based piece. Plenty of generative work animates, and `abx.done()` exists *because* stills need a settle point — so a proof sheet of an animated piece is a set of arbitrary frozen frames presented as the work. The server also makes PostParams tangible (a color picker that re-renders beats any explanation of governed params), and it costs no Chromium download.
59
+
60
+ **It serves the same document the generator serves** — the real `abx.js`, the real canonical tokenData shape, the real dependency tags — with a synthetic seed in place of a minted one. So what they approve is what deploys. (This is why you should not hand-roll a preview page: a stub you write yourself defines its own `abx` surface, and will happily run a sketch that reads its seed the wrong way.)
61
+
62
+ **The program is re-read from disk on every render**, so the loop is: edit `art.js` → tell them to refresh → take feedback → edit again. No restart, no watcher, no rebuild.
63
+
64
+ **When you need to see it yourself** — you have no browser, and "how does it look?" every round is a bad experience for them:
65
+
66
+ ```bash
67
+ abx preview --script art.js --shoot ./frames --count 9 # PNGs + traits.json, then exits
68
+ ```
69
+
70
+ Same server, same document, headless. Needs Playwright + Chromium (`npm i -D playwright && npx playwright install chromium`); the interactive lane needs neither. **Read the PNGs** — don't report on art you haven't looked at. `--shoot` also flags the two silent killers for you: no frame reporting traits (⇒ no marketplace `attributes` on any lane), and identical traits across every seed (⇒ the sketch isn't reading `abx.tokenData.seed`, so the drop mints N identical tokens).
71
+
72
+ Use both: `--shoot` to check your own work between rounds, the live URL as what the creator actually looks at.
73
+
74
+ **What preview is NOT.** It injects the token data itself, so it will run a sketch that reads its seed the wrong way — and a piece that only ever renders one seed correctly still looks fine here. Neither check that follows is optional:
75
+
76
+ ```bash
77
+ abx inspect art.js # the wiring check: are traits + PostParams actually read/reported?
78
+ abx deploy-code --script art.js --onchain-uri --dry-run # the lane + surfaces check
79
+ ```
80
+
81
+ And a **testnet deploy remains the faithful end-to-end** (the real generator, the real assembled document, the real seed from the chain). Both come after the art is settled.
82
+
48
83
  ## What a code project requires you to run — and keep running (say this up front)
49
84
 
50
85
  A code project's art depends on live on-chain state (the per-token `seed`, mutable PostParams), and *something* must read that state and inject it at view time. That something is a **resolver you run** — **unless** you take the fully-on-chain lanes (`--onchain-uri` for the tokenURI+animation, `--image-base` for a deterministic off-chain thumbnail, `--attributes-renderer` for on-chain traits), which can eliminate the metadata resolver entirely. When a resolver *is* in play, it's three pieces of ongoing infrastructure — lay them out plainly before they commit: