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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -34,13 +34,14 @@
34
34
  * abx storage backup-key copy the managed Turbo/Arweave key (holds credits) to a safe path
35
35
  * abx status list indexed projects + node info
36
36
  * abx doctor check environment (key, RPC, balance, factory, storage)
37
- * abx skill install install the abx agent skill into your agent (.claude/skills; --global)
37
+ * abx skill install install the version-locked abx agent skill into your agent(s)
38
+ * (default: Claude Code + the neutral .agents/skills; --agent/--global)
38
39
  *
39
40
  * Every write picks a signing lane: default hot (env key signs), `--sign` (a human
40
41
  * approves in their own wallet via a one-shot localhost page), `--unsigned` (print
41
42
  * the tx for a multisig / offline signer). The agent picks the lane; the CLI signs.
42
43
  */
43
- import { appendFileSync, copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
44
+ import { appendFileSync, copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
44
45
  import { randomBytes } from 'node:crypto';
45
46
  import { basename, extname, join as joinPath, resolve as resolvePath } from 'node:path';
46
47
  import { fileURLToPath } from 'node:url';
@@ -54,7 +55,7 @@ import { DEP_RESOLUTION, deploySeedSource, deploySeriesCodeFactory, prepareCodeS
54
55
  import { checkRegistryDeps, dependencySetupCalls, parseDepFlag, resolveDepRegistryPointer } from './deps.js';
55
56
  import { composeParamsKeys, expectedChainComplete, hasOnChainUriLane, onchainUriSetupCalls, onChainUriReport } from './onchain-uri.js';
56
57
  import { parseFlags, unknownFlags } from './flags.js';
57
- import { checkForCliUpdate, compareVersions, installedSkillVersions, readCliVersion, SKILL_VERSION_MARKER } from './update-check.js';
58
+ import { AGENT_SKILL_PARENTS, checkForCliUpdate, compareVersions, installedSkillVersions, readCliVersion, readSkillVersion, SKILL_DIR_NAME, } from './update-check.js';
58
59
  import { analyzeScript, recommendLane } from './inspect.js';
59
60
  import { parseSchemaSpecs, describeSchema } from './schema.js';
60
61
  import { parseSeriesTraits, looksPerTokenAttributes, parseSeriesTraitsById } from './series-traits.js';
@@ -4260,6 +4261,21 @@ async function cmdDoctor(flags) {
4260
4261
  const jwk = loadArweaveJwk();
4261
4262
  console.log(` ${dim('↳ Turbo/Arweave key ' + (jwk ? arweaveAddress(jwk) + ' ' : '') + 'holds your upload credits — back it up: `abx storage backup-key --out <path>`')}`);
4262
4263
  }
4264
+ // Agent skill co-versioning — the skill drives this CLI and is version-locked to it (its version
4265
+ // lives in SKILL.md frontmatter). Report any installed copy and flag drift; notify-only, so a
4266
+ // stale/absent skill never fails doctor (`abx skill install` resyncs).
4267
+ const cliVersion = readCliVersion();
4268
+ const skillVersions = installedSkillVersions();
4269
+ const staleSkills = skillVersions.filter((v) => compareVersions(cliVersion, v) > 0);
4270
+ if (skillVersions.length === 0) {
4271
+ console.log(` ${dim('↳ agent skill: not installed here — `abx skill install` so a coding agent can drive abx')}`);
4272
+ }
4273
+ else if (staleSkills.length > 0) {
4274
+ console.log(` ${c.orange}⚠${c.reset} agent skill behind CLI${dim(` installed v${staleSkills.join(', v')} · CLI v${cliVersion} — refresh: `)}${g('abx skill install')}`);
4275
+ }
4276
+ else {
4277
+ check(`agent skill in sync (v${cliVersion})`, true);
4278
+ }
4263
4279
  console.log('');
4264
4280
  }
4265
4281
  // ── shared output ─────────────────────────────────────────────────────────--
@@ -4558,12 +4574,15 @@ function keepAlive() {
4558
4574
  // ── per-command usage (printed by `<cmd> --help` / `abx help <cmd>`; read-only) ──
4559
4575
  const COMMAND_HELP = {
4560
4576
  skill: `
4561
- ${bold('abx skill')} ${dim('— install the abx agent skill so your coding agent can drive abx')}
4562
- ${g('abx skill install')} copy the skill into ${bold('.claude/skills/abx-self-host')} in the current project
4563
- ${g('--global')} install into your home dir (~/.claude/skills) instead of the project
4564
- ${g('--target <dir>')} install under a different base dir (writes <dir>/.claude/skills/abx-self-host)
4577
+ ${bold('abx skill')} ${dim('— install the version-locked abx agent skill so your coding agent can drive abx')}
4578
+ ${g('abx skill install')} copy the bundled skill (version-locked to this CLI) into your agent(s)
4579
+ ${dim('default: both')} ${bold('.claude/skills')} ${dim('(Claude Code) and')} ${bold('.agents/skills')} ${dim('(Cursor · Codex · Gemini · Copilot)')}
4580
+ ${g('--agent <name>')} only one agent: ${dim('claude | cursor | codex | gemini | copilot')}
4581
+ ${g('--global')} install into your home dir (~) instead of the current project
4582
+ ${g('--target <dir>')} install the skill folder straight under <dir> (writes <dir>/abx-self-host)
4565
4583
  ${g('abx skill path')} print the absolute path to the bundled skill (for ${g('npx skills add <path>')} or manual copy)
4566
- ${dim('Restart your agent after installing so it picks up the skill. Cross-agent install:')} ${g('npx skills add ArtBlocks/abx')}`,
4584
+ ${dim('Restart your agent after installing so it picks up the skill. Once the repo is public, the git-based')}
4585
+ ${dim('cross-agent installer also works (not version-locked):')} ${g('npx skills add ArtBlocks/abx --skill abx-self-host')}`,
4567
4586
  deploy: `
4568
4587
  ${bold('abx deploy')} ${dim('— deploy + index a 1/1 (no server). Sends a tx in the chosen lane.')}
4569
4588
  --image <path> custody your own image (png · jpg · gif · svg · webp); else generative demo art
@@ -5019,7 +5038,7 @@ function help() {
5019
5038
  ${g('abx forget')} <address> drop a project's local registration + projection (on-chain untouched)
5020
5039
  ${g('abx status')} list indexed projects + node info
5021
5040
  ${g('abx doctor')} check environment (key, RPC, balance, factory, storage)
5022
- ${g('abx skill install')} install the abx agent skill into your agent [--global] [--target <dir>] · ${g('abx skill path')} prints the bundled skill
5041
+ ${g('abx skill install')} install the version-locked abx skill into your agent(s) [--agent <name>] [--global] [--target <dir>] · ${g('abx skill path')} prints the bundled skill
5023
5042
  ${g('abx version')} print the installed CLI version
5024
5043
 
5025
5044
  ${dim('Run')} ${g('abx <command> --help')} ${dim('for per-command usage. --help / -h never executes — it only prints usage.')}
@@ -5041,8 +5060,13 @@ function parseSaltFlag(raw) {
5041
5060
  // ── skill: install the abx agent skill into the user's coding agent ───────────────────────
5042
5061
  // The skill is the agentic half of the toolkit — the CLI is only useful once an agent knows how
5043
5062
  // to drive it. It ships bundled inside the published package (packages/cli/skill/, created at
5044
- // prepack); in dev the same command reads the canonical copy straight from the repo. A separate
5045
- // cross-agent channel is `npx skills add ArtBlocks/abx` (the Agent Skills standard, git-based).
5063
+ // prepack); in dev the same command reads the canonical copy straight from the repo. This is the
5064
+ // CANONICAL install path precisely because the bundle is version-locked to the CLI it ships inside
5065
+ // (the version lives in SKILL.md frontmatter — see update-check.ts). By default it writes to BOTH
5066
+ // `.claude/skills` (Claude Code) and the neutral `.agents/skills` (Cursor · Codex · Gemini ·
5067
+ // Copilot all read it), so one command covers the whole ecosystem; `--agent` narrows it. A separate
5068
+ // git-based channel, `npx skills add ArtBlocks/abx --skill abx-self-host`, works once the repo is public but is NOT
5069
+ // version-locked to a local CLI — prefer `abx skill install`.
5046
5070
  /** Locate the skill folder: the bundled copy beside the compiled CLI (published), else the
5047
5071
  * canonical repo copy (dev). Returns null if neither is present. */
5048
5072
  function resolveBundledSkill() {
@@ -5106,37 +5130,71 @@ function cmdScaffoldRenderer(rest, flags) {
5106
5130
  console.log(` ${g('abx deploy-code --image-renderer <MyRenderer> --attributes-renderer <MyTraits> --onchain-uri --schema palette:HexColor:TokenOwner --name "…" --symbol …')}`);
5107
5131
  info(`full walkthrough: ${bold(`${basename(dir)}/README.md`)} · interface + invariants: https://abx.docs.artblocks.io/protocol/renderers/`);
5108
5132
  }
5133
+ /** Human label for each skills-parent dir — which agents pick the skill up from there. */
5134
+ const SKILL_PARENT_LABELS = {
5135
+ '.claude/skills': 'Claude Code',
5136
+ '.agents/skills': 'Cursor · Codex · Gemini · Copilot',
5137
+ };
5138
+ /** Resolve which skills-parent dirs `install` should write to. No `--agent` → the whole-ecosystem
5139
+ * default (Claude Code + the neutral `.agents/skills` everyone else reads). `--agent a,b` narrows
5140
+ * to those agents' dirs (de-duped, since several share `.agents/skills`). */
5141
+ function resolveInstallParents(agentFlag) {
5142
+ if (!agentFlag || agentFlag === 'true')
5143
+ return ['.claude/skills', '.agents/skills'];
5144
+ const parents = new Set();
5145
+ for (const raw of agentFlag.split(',')) {
5146
+ const a = raw.trim().toLowerCase();
5147
+ const parent = AGENT_SKILL_PARENTS[a];
5148
+ if (!parent) {
5149
+ throw new Error(`unknown --agent '${a}'. Use one of: ${Object.keys(AGENT_SKILL_PARENTS).join(', ')} (or omit --agent to install for every agent).`);
5150
+ }
5151
+ parents.add(parent);
5152
+ }
5153
+ return [...parents];
5154
+ }
5155
+ /** Copy the bundled skill folder to `dest`, replacing any prior copy so a re-install after an
5156
+ * upgrade never leaves stale reference files behind. */
5157
+ function installSkillTo(src, dest) {
5158
+ mkdirSync(joinPath(dest, '..'), { recursive: true });
5159
+ rmSync(dest, { recursive: true, force: true });
5160
+ cpSync(src, dest, { recursive: true });
5161
+ }
5109
5162
  async function cmdSkill(rest, flags) {
5110
5163
  const sub = rest[0] ?? 'install';
5111
5164
  const src = resolveBundledSkill();
5112
5165
  if (!src) {
5113
5166
  throw new Error('bundled skill not found (expected <pkg>/skill or .claude/skills/abx-self-host). ' +
5114
- 'Reinstall @artblocks/abx-cli, or install cross-agent with: npx skills add ArtBlocks/abx');
5167
+ 'Reinstall @artblocks/abx-cli, or install cross-agent with: npx skills add ArtBlocks/abx --skill abx-self-host');
5115
5168
  }
5116
5169
  if (sub === 'path') {
5117
5170
  console.log(src);
5118
5171
  return;
5119
5172
  }
5120
5173
  if (sub === 'install') {
5121
- const base = flags.global !== undefined ? homedir() : flags.target ?? process.cwd();
5122
- const destParent = joinPath(base, '.claude', 'skills');
5123
- const dest = joinPath(destParent, 'abx-self-host');
5124
- mkdirSync(destParent, { recursive: true });
5125
- cpSync(src, dest, { recursive: true });
5126
- // Stamp the CLI version so a later run can notice the skill has drifted behind an upgraded CLI
5127
- // (the two are co-versioned). Best-effort — a missing marker just means "no drift nudge".
5128
- try {
5129
- writeFileSync(joinPath(dest, SKILL_VERSION_MARKER), readCliVersion());
5130
- }
5131
- catch {
5132
- /* non-fatal */
5174
+ const version = readSkillVersion(joinPath(src, 'SKILL.md')) ?? readCliVersion();
5175
+ // Escape hatch: --target <dir> writes the skill folder straight under <dir> (for an agent
5176
+ // whose skills dir we don't special-case, or a bespoke location).
5177
+ if (typeof flags.target === 'string' && flags.target !== 'true') {
5178
+ const dest = joinPath(resolvePath(flags.target), SKILL_DIR_NAME);
5179
+ installSkillTo(src, dest);
5180
+ ok(`installed the abx skill v${version} ${dest}`);
5181
+ info('restart your agent so it loads the skill, then ask it to launch an NFT with abx.');
5182
+ return;
5133
5183
  }
5134
- ok(`installed the abx skill ${dest}`);
5135
- info('restart your agent (Claude Code / Cursor) so it loads the skill, then ask it to launch an NFT with abx.');
5136
- info('cross-agent alternative (git-based): npx skills add ArtBlocks/abx');
5184
+ const base = flags.global !== undefined ? homedir() : process.cwd();
5185
+ const parents = resolveInstallParents(flags.agent);
5186
+ ok(`installed the abx skill v${version}${flags.global !== undefined ? ' (global, ~)' : ''}:`);
5187
+ for (const parent of parents) {
5188
+ const dest = joinPath(base, parent, SKILL_DIR_NAME);
5189
+ installSkillTo(src, dest);
5190
+ const label = SKILL_PARENT_LABELS[parent];
5191
+ console.log(` ${g(joinPath(parent, SKILL_DIR_NAME))}${label ? dim(' → ' + label) : ''}`);
5192
+ }
5193
+ info('restart your agent so it loads the skill, then ask it to launch an NFT with abx.');
5194
+ info(`the skill is version-locked to this CLI (v${version}); re-run ${g('abx skill install')} after upgrading so the two stay in sync.`);
5137
5195
  return;
5138
5196
  }
5139
- throw new Error('usage: abx skill <install|path> [--global] [--target <dir>]');
5197
+ throw new Error('usage: abx skill <install|path> [--agent claude|cursor|codex|gemini|copilot] [--global] [--target <dir>]');
5140
5198
  }
5141
5199
  main().catch((err) => {
5142
5200
  // Keep the actionable message; strip viem's verbose boilerplate trailer (Docs:/Version: lines) so