luxin-cli 0.2.6 → 0.2.8

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 CHANGED
@@ -6,6 +6,42 @@ provenance; this file is the human- and agent-readable release map.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.2.8 - 2026-08-12
10
+
11
+ - New command: `luxin skill` prints the full usage guidance from bundled
12
+ package bytes, so instructions always match the CLI you are running. The
13
+ installed SKILL.md is now a ~4KB discovery stub that points here; the full
14
+ body is also served at https://luxin.sh/skill-core.md.
15
+ - Skill freshness handshake: export `LUXIN_SKILL_REVISION=<the revision in
16
+ your stub's frontmatter>` and `create --guide`, `edit --guide`, and
17
+ `luxin skill` will return `data.skill_refresh` with the current revision and
18
+ the exact refresh command (`npx skills update luxin`) whenever your installed
19
+ copy no longer matches. The field is omitted entirely when you are current.
20
+ Nothing ever rewrites your files, config, or hooks — refresh is always an
21
+ explicit command you run.
22
+ - `agent_skill.read_command` now points at `luxin skill --json` instead of
23
+ curling the stub.
24
+
25
+ ## 0.2.7 - 2026-08-12
26
+
27
+ - Release (pricing): the guide's local credit quoting for xAI models moved from
28
+ a `startsWith("xai.grok-imagine-image")` prefix test to an explicit model-id
29
+ list. The prefix test would have priced any Grok Imagine model it had never
30
+ heard of off the 1.x table — for the new `xai.grok-imagine-image-2.0` that
31
+ meant quoting 4 credits for a 10-credit create. Models outside the list now
32
+ defer to the hosted registry's published debit instead of guessing.
33
+ - Adds the `xai.grok-imagine-image-2.0` quality×resolution credit matrix to
34
+ local guide pricing (low/1k through medium/2k), matching the registry the
35
+ hosted API serves since 2026-08-12. Every tier was verified against live
36
+ provider billing before release ($0.04/$0.06/$0.07/$0.08 receipts).
37
+ - The registry behind `models show` grew from 290 to 316 models server-side
38
+ (grok-imagine-image-2.0 create+edit executable and listed; 24 summer-wave
39
+ fal models cataloged with honest tier pricing before they are wired —
40
+ reachable via `models show MODEL_ID`, not yet in the `models list` output,
41
+ which serves executable models only). No CLI change was needed for that —
42
+ noted here because agents read this file to decide whether to upgrade:
43
+ upgrade for the pricing fix, not for model availability.
44
+
9
45
  ## 0.2.6 - 2026-08-09
10
46
 
11
47
  - Release (trust): `create --guide` now says when it cannot price the request it
package/README.md CHANGED
@@ -85,9 +85,67 @@ Naming note: Luxin is a creative-media runtime for agents. It is unrelated to
85
85
  `luxon`, the datetime library; the npm package here is `luxin-cli` and the
86
86
  installed binary is `luxin`.
87
87
 
88
+ ## Keeping The Skill Current
89
+
90
+ The installed `SKILL.md` is a ~4KB discovery stub. Everything volatile — flags,
91
+ workflows, recovery, funding — lives in the guidance the CLI itself carries, so
92
+ it always matches the code you are running:
93
+
94
+ ```bash
95
+ npm_config_update_notifier=false npx -y luxin-cli@latest skill
96
+ ```
97
+
98
+ Refresh the installed stub with the standard verb:
99
+
100
+ ```bash
101
+ npx skills update luxin
102
+ ```
103
+
104
+ The stub records its own identity as `metadata.skill_revision`. Export it and
105
+ the CLI will say when your copy no longer matches the guidance it ships,
106
+ returning `data.skill_refresh` plus one warning line on `luxin skill` and
107
+ `create --guide`:
108
+
109
+ ```bash
110
+ export LUXIN_SKILL_REVISION=<metadata.skill_revision from your installed SKILL.md>
111
+ ```
112
+
113
+ Detection is remote; mutation stays local and explicit. Luxin prints the refresh
114
+ command and never rewrites your skill files, agent config, or hook files.
115
+
116
+ ### Optional: refresh at session start (copy-paste, opt-in)
117
+
118
+ Some runtimes can run a command when a session starts. If you want an automatic
119
+ refresh, add it yourself — Luxin will not install it for you, and nothing below
120
+ runs unless you put it there. For Claude Code, in `.claude/settings.json`:
121
+
122
+ ```json
123
+ {
124
+ "hooks": {
125
+ "SessionStart": [
126
+ {
127
+ "hooks": [
128
+ {
129
+ "type": "command",
130
+ "command": "npx skills update luxin",
131
+ "timeout": 60
132
+ }
133
+ ]
134
+ }
135
+ ]
136
+ }
137
+ }
138
+ ```
139
+
140
+ Two things worth knowing before you adopt it: it adds a network call to every
141
+ session start, and it applies updates without showing you a diff first. Running
142
+ `npx skills update luxin` by hand when a nudge appears keeps the change
143
+ reviewable.
144
+
88
145
  Agent-facing contracts:
89
146
 
90
147
  - [Hosted skill contract](https://luxin.sh/skill.md)
148
+ - [Hosted skill guidance](https://luxin.sh/skill-core.md)
91
149
  - [Hosted LLM contract](https://luxin.sh/llms.txt)
92
150
  - [Hosted CLI contract](https://luxin.sh/cli.md)
93
151
  - [Public repo skill source](https://github.com/danielgwilson/luxin/tree/main/skills/luxin)
package/bin/luxin.mjs CHANGED
@@ -15,7 +15,20 @@ import { Readable } from "node:stream";
15
15
  import { pipeline } from "node:stream/promises";
16
16
  import os from "node:os";
17
17
 
18
- const VERSION = "0.2.6";
18
+ const VERSION = "0.2.8";
19
+ // Content-derived identity of the guidance this package ships (#2302). Kept in
20
+ // sync with docs/public-contract/skill.md by `pnpm skill-revision:write` and
21
+ // gated by `pnpm skill-revision:check`; the mirror cannot import from src/.
22
+ const SKILL_REVISION = "b1dc1284d18d";
23
+ // `luxin skill` prints these bundled bytes rather than fetching them, so the
24
+ // guidance can never describe a CLI other than the one printing it. Packed by
25
+ // public-cli/package.json `files`.
26
+ const SKILL_GUIDANCE_FILE = "skill-core.md";
27
+ const SKILL_GUIDANCE_SCHEMA = "image-skill.skill-guidance.v1";
28
+ const SKILL_CORE_URL = "https://luxin.sh/skill-core.md";
29
+ const SKILL_REFRESH_COMMAND = "npx skills update luxin";
30
+ const INSTALLED_SKILL_REVISION_ENV = "LUXIN_SKILL_REVISION";
31
+ const INSTALLED_SKILL_REVISION_HEADER = "x-luxin-skill-revision";
19
32
  const PACKAGE_NAME = "luxin";
20
33
  const DEFAULT_API_BASE_URL = "https://api.luxin.sh";
21
34
  const DEFAULT_DOCS_BASE_URL = "https://luxin.sh";
@@ -101,12 +114,28 @@ const ACCEPTED_PUBLIC_MEDIA_HOSTS = [
101
114
  // price. These are prices, not costs: the CLI never computes or publishes
102
115
  // provider cost or margin.
103
116
  const XAI_IMAGE_CREDIT_PRICES = {
104
- source_image: { standard: 1 / 3, quality: 5 / 3 },
117
+ source_image: { standard: 1 / 3, quality: 5 / 3, image_2: 5 / 3 },
105
118
  output_image: {
106
119
  standard: 10 / 3,
107
120
  quality: { "1k": 25 / 3, "2k": 35 / 3 },
121
+ image_2: {
122
+ low: { "1k": 20 / 3, "2k": 10 },
123
+ medium: { "1k": 10, "2k": 40 / 3 },
124
+ },
108
125
  },
109
126
  };
127
+ // Mirrors src/credit-pricing.ts XAI_IMAGE_MODEL_IDS. This is an explicit list,
128
+ // not a prefix test: a Grok Imagine image model this CLI has never heard of is
129
+ // priced by the hosted registry's published debit, not by guessing it onto the
130
+ // 1.x price table.
131
+ const XAI_LOCALLY_PRICED_IMAGE_MODEL_IDS = new Set([
132
+ "xai.grok-imagine-image",
133
+ "xai.grok-imagine-image-edit",
134
+ "xai.grok-imagine-image-quality",
135
+ "xai.grok-imagine-image-quality-edit",
136
+ "xai.grok-imagine-image-2.0",
137
+ "xai.grok-imagine-image-2.0-edit",
138
+ ]);
110
139
  const MODALITY_COMMAND_ALIASES = new Map([
111
140
  ["image", { command: "create", intent: null }],
112
141
  ["video", { command: "create", intent: "video" }],
@@ -318,7 +347,8 @@ function agentSkillHint(commandPrefix = "luxin") {
318
347
  url: AGENT_SKILL_URL,
319
348
  llms_url: AGENT_LLMS_URL,
320
349
  cli_docs_url: AGENT_CLI_DOCS_URL,
321
- read_command: "curl -fsSL https://luxin.sh/skill.md",
350
+ // #2302: point at the CLI-carried guidance, not the discovery stub.
351
+ read_command: "luxin skill --json",
322
352
  recommended_start_command: renderGuidePrefixedCommand(
323
353
  commandPrefix,
324
354
  "create --guide --prompt PROMPT --json",
@@ -371,6 +401,8 @@ async function main(rawArgv) {
371
401
  return await doctor(rest);
372
402
  case "trust":
373
403
  return await trust(rest);
404
+ case "skill":
405
+ return await skill(rest);
374
406
  case "signup":
375
407
  return await signup(rest);
376
408
  case "claim":
@@ -487,12 +519,13 @@ function commandHelpByKey(key) {
487
519
  "": {
488
520
  command: "help",
489
521
  usage:
490
- "luxin <doctor|trust|signup|claim|whoami|usage|quota|credits|capabilities|models|create|upload|edit|assets|jobs|activity|feedback> --json",
522
+ "luxin <doctor|trust|skill|signup|claim|whoami|usage|quota|credits|capabilities|models|create|upload|edit|assets|jobs|activity|feedback> --json",
491
523
  docs_url: "https://luxin.sh/cli.md",
492
524
  agent_skill: agentSkillHint(),
493
525
  commands: [
494
526
  "doctor",
495
527
  "trust",
528
+ "skill",
496
529
  "signup --agent --agent-name NAME --runtime RUNTIME",
497
530
  "claim request --contact INBOX",
498
531
  "claim code",
@@ -544,6 +577,13 @@ function commandHelpByKey(key) {
544
577
  description:
545
578
  "Return npm provenance, hosted contract hashes, API health, and model availability evidence.",
546
579
  },
580
+ skill: {
581
+ command: "luxin skill help",
582
+ usage: "luxin skill --json",
583
+ docs_url: "https://luxin.sh/cli.md#luxin-skill",
584
+ description:
585
+ "Print the full agent guidance bundled with this CLI version, so instructions always match the installed code. No network call, no auth, no spend.",
586
+ },
547
587
  signup: {
548
588
  command: "luxin signup help",
549
589
  usage: "luxin signup --agent --agent-name NAME --runtime RUNTIME --json",
@@ -864,6 +904,104 @@ function unsupportedFlagsMessage(command, flags) {
864
904
  .join(", ")}`;
865
905
  }
866
906
 
907
+ // #2302: the installed SKILL.md is a ~4KB discovery stub. Everything volatile
908
+ // -- flags, workflows, recovery, funding -- lives in the guidance body this
909
+ // command prints, so an agent working from a months-old install still reads
910
+ // instructions that match the code it is about to run.
911
+ //
912
+ // Bundled, not fetched. A network fetch could serve prose this build does not
913
+ // implement, which is the drift the split exists to remove.
914
+ async function skill(argv) {
915
+ const args = parseArgs(argv);
916
+ const unsupportedFlags = [...args.flags.keys()].filter(
917
+ (flag) => !["json"].includes(flag),
918
+ );
919
+ if (args.positionals.length > 0 || unsupportedFlags.length > 0) {
920
+ return invalid(
921
+ "luxin skill",
922
+ unsupportedFlags.length > 0
923
+ ? unsupportedFlagsMessage("skill", unsupportedFlags)
924
+ : "skill does not accept positional arguments",
925
+ );
926
+ }
927
+
928
+ let content;
929
+ try {
930
+ content = await readFile(
931
+ new URL(`../${SKILL_GUIDANCE_FILE}`, import.meta.url),
932
+ "utf8",
933
+ );
934
+ } catch (error) {
935
+ return failure(
936
+ "luxin skill",
937
+ 7,
938
+ "SKILL_GUIDANCE_UNAVAILABLE",
939
+ `bundled skill guidance could not be read: ${
940
+ error instanceof Error ? error.message : "unknown error"
941
+ }. Reinstall the CLI, or read ${SKILL_CORE_URL}`,
942
+ false,
943
+ );
944
+ }
945
+
946
+ const refresh = skillRefresh();
947
+ return success(
948
+ "luxin skill",
949
+ {
950
+ schema: SKILL_GUIDANCE_SCHEMA,
951
+ revision: SKILL_REVISION,
952
+ source: "bundled_cli_package",
953
+ format: "markdown",
954
+ bytes: Buffer.byteLength(content, "utf8"),
955
+ canonical_url: SKILL_CORE_URL,
956
+ refresh_command: SKILL_REFRESH_COMMAND,
957
+ mutation: {
958
+ provider_call: false,
959
+ credit_debit: false,
960
+ media_write: false,
961
+ local_file_write: false,
962
+ },
963
+ ...(refresh === null ? {} : { skill_refresh: refresh }),
964
+ content,
965
+ },
966
+ refresh === null ? [] : [skillRefreshWarning(refresh)],
967
+ );
968
+ }
969
+
970
+ /**
971
+ * Self-reported identity of the caller's installed skill copy. 12 lowercase hex
972
+ * or nothing: the value is echoed into a response field and forwarded as a
973
+ * header, so it must not become a channel for arbitrary text.
974
+ */
975
+ function installedSkillRevision() {
976
+ const raw = process.env[INSTALLED_SKILL_REVISION_ENV];
977
+ if (typeof raw !== "string") {
978
+ return null;
979
+ }
980
+ const trimmed = raw.trim().toLowerCase();
981
+ return /^[0-9a-f]{12}$/.test(trimmed) ? trimmed : null;
982
+ }
983
+
984
+ /**
985
+ * The compact nudge, or nothing at all. Omitted when the caller reports nothing
986
+ * or reports the revision this CLI ships. Content hashes are unordered, so this
987
+ * says "differs", never "older".
988
+ */
989
+ function skillRefresh() {
990
+ const installed = installedSkillRevision();
991
+ if (installed === null || installed === SKILL_REVISION) {
992
+ return null;
993
+ }
994
+ return {
995
+ installed_revision: installed,
996
+ current_revision: SKILL_REVISION,
997
+ refresh_command: SKILL_REFRESH_COMMAND,
998
+ };
999
+ }
1000
+
1001
+ function skillRefreshWarning(refresh) {
1002
+ return `installed skill revision ${refresh.installed_revision} does not match the current revision ${refresh.current_revision} this CLI ships; refresh with: ${refresh.refresh_command}`;
1003
+ }
1004
+
867
1005
  async function trust(argv) {
868
1006
  const args = parseArgs(argv);
869
1007
  const unsupportedFlags = [...args.flags.keys()].filter(
@@ -2326,6 +2464,7 @@ async function createGuide(args, options = {}) {
2326
2464
  },
2327
2465
  ),
2328
2466
  };
2467
+ const guideSkillRefresh = skillRefresh();
2329
2468
  const guideData = {
2330
2469
  schema:
2331
2470
  guideOperation === "edit"
@@ -2452,20 +2591,25 @@ async function createGuide(args, options = {}) {
2452
2591
  credit_debit: false,
2453
2592
  media_write: false,
2454
2593
  },
2594
+ // #2302: present only when the caller self-reported a revision that is not
2595
+ // the one this CLI ships. Survives ready_compact deliberately -- a nudge
2596
+ // that only appears in the blocked stages would never reach the agents who
2597
+ // are working fine on stale instructions, which is the whole failure mode.
2598
+ ...(guideSkillRefresh === null ? {} : { skill_refresh: guideSkillRefresh }),
2455
2599
  };
2456
2600
  for (const field of compactOmittedFields) {
2457
2601
  delete guideData[field];
2458
2602
  }
2459
- return createGuideSuccess(
2460
- command,
2461
- quota?.envelope.actor ?? null,
2462
- guideData,
2463
- plannedDebitUnavailable
2603
+ return createGuideSuccess(command, quota?.envelope.actor ?? null, guideData, [
2604
+ ...(plannedDebitUnavailable
2464
2605
  ? [
2465
2606
  "create_credit_debit_unavailable: this hosted API does not publish per-aspect-ratio create debits, so the credits above are the model's default-shape quote and may not be what create debits. Run the dry_run command in escape_hatches for the exact figure before budgeting.",
2466
2607
  ]
2467
- : [],
2468
- );
2608
+ : []),
2609
+ ...(guideSkillRefresh === null
2610
+ ? []
2611
+ : [skillRefreshWarning(guideSkillRefresh)]),
2612
+ ]);
2469
2613
  }
2470
2614
 
2471
2615
  function createGuideSuccess(command, actor, data, warnings = []) {
@@ -3039,14 +3183,16 @@ function createGuideAspectRatioCreditPricing(model, context) {
3039
3183
  }
3040
3184
 
3041
3185
  function createGuideCanPriceModelParameters(model) {
3042
- return String(model?.id ?? "").startsWith("xai.grok-imagine-image");
3186
+ return XAI_LOCALLY_PRICED_IMAGE_MODEL_IDS.has(String(model?.id ?? ""));
3043
3187
  }
3044
3188
 
3045
3189
  function createGuideXaiImagePricing(model, modelParameters, context) {
3046
3190
  const modelId = String(model?.id ?? "");
3047
3191
  const quality = modelId.includes("-quality");
3192
+ const image2 = modelId.startsWith("xai.grok-imagine-image-2.0");
3048
3193
  const edit = modelId.endsWith("-edit");
3049
3194
  const resolution = modelParameters?.resolution === "2k" ? "2k" : "1k";
3195
+ const image2Quality = modelParameters?.quality === "low" ? "low" : "medium";
3050
3196
  const outputImageCount =
3051
3197
  Number.isInteger(context?.outputCount) && context.outputCount > 0
3052
3198
  ? context.outputCount
@@ -3057,12 +3203,16 @@ function createGuideXaiImagePricing(model, modelParameters, context) {
3057
3203
  ? context.referenceAssetCount
3058
3204
  : 0;
3059
3205
  const sourceImageCount = edit ? 1 + referenceAssetCount : 0;
3060
- const sourceCredits = quality
3061
- ? XAI_IMAGE_CREDIT_PRICES.source_image.quality
3062
- : XAI_IMAGE_CREDIT_PRICES.source_image.standard;
3063
- const outputCredits = quality
3064
- ? XAI_IMAGE_CREDIT_PRICES.output_image.quality[resolution]
3065
- : XAI_IMAGE_CREDIT_PRICES.output_image.standard;
3206
+ const sourceCredits = image2
3207
+ ? XAI_IMAGE_CREDIT_PRICES.source_image.image_2
3208
+ : quality
3209
+ ? XAI_IMAGE_CREDIT_PRICES.source_image.quality
3210
+ : XAI_IMAGE_CREDIT_PRICES.source_image.standard;
3211
+ const outputCredits = image2
3212
+ ? XAI_IMAGE_CREDIT_PRICES.output_image.image_2[image2Quality][resolution]
3213
+ : quality
3214
+ ? XAI_IMAGE_CREDIT_PRICES.output_image.quality[resolution]
3215
+ : XAI_IMAGE_CREDIT_PRICES.output_image.standard;
3066
3216
  const creditsRequired = Math.max(
3067
3217
  1,
3068
3218
  Math.ceil(
@@ -3075,8 +3225,14 @@ function createGuideXaiImagePricing(model, modelParameters, context) {
3075
3225
  modelParameters?.resolution === undefined ||
3076
3226
  modelParameters?.resolution === null ||
3077
3227
  modelParameters?.resolution === "1k";
3228
+ const defaultQuality =
3229
+ !image2 ||
3230
+ modelParameters?.quality === undefined ||
3231
+ modelParameters?.quality === null ||
3232
+ modelParameters?.quality === "medium";
3078
3233
  const defaultShape =
3079
3234
  defaultResolution &&
3235
+ defaultQuality &&
3080
3236
  outputImageCount === 1 &&
3081
3237
  sourceImageCount === (edit ? 1 : 0);
3082
3238
  return {
@@ -6841,6 +6997,12 @@ async function apiRequest(input) {
6841
6997
  ...(input.token === undefined
6842
6998
  ? {}
6843
6999
  : { authorization: `Bearer ${input.token}` }),
7000
+ // #2302: self-reported identity of the installed skill copy, so the
7001
+ // hosted side can measure how stale the installed base is. Non-secret,
7002
+ // omitted entirely when unset, never guessed.
7003
+ ...(installedSkillRevision() === null
7004
+ ? {}
7005
+ : { [INSTALLED_SKILL_REVISION_HEADER]: installedSkillRevision() }),
6844
7006
  },
6845
7007
  body: input.body === undefined ? undefined : JSON.stringify(input.body),
6846
7008
  });
package/cli.md CHANGED
@@ -93,6 +93,63 @@ verified, the command still returns a packet with explicit `unreachable`,
93
93
  `not_available_yet`, `inspect_only`, or `stale_or_mismatched` states rather
94
94
  than omitting the field.
95
95
 
96
+ ### `luxin skill`
97
+
98
+ Prints the full agent guidance that shipped with this CLI version.
99
+
100
+ ```bash
101
+ luxin skill --json
102
+ ```
103
+
104
+ The published `SKILL.md` at `https://luxin.sh/skill.md` is a ~4KB discovery
105
+ stub: it carries identity, the first command, the safety invariants, and a
106
+ revision, and nothing else. Flags, workflows, model parameters, recovery, and
107
+ funding details live here, because an installed skill file is a snapshot that
108
+ never updates while `npx luxin-cli@latest` updates on every call.
109
+
110
+ The body is read from the package this command is running from, not fetched, so
111
+ the guidance always matches the installed code by construction. No network call,
112
+ no auth, no spend. The same bytes are served at
113
+ `https://luxin.sh/skill-core.md`.
114
+
115
+ `data` uses `schema: "image-skill.skill-guidance.v1"` and carries `revision`
116
+ (the 12-hex content-derived identity of the published skill), `source`,
117
+ `format`, `bytes`, `canonical_url`, `refresh_command`, `mutation`, and
118
+ `content`.
119
+
120
+ #### Skill revision and staleness
121
+
122
+ An installed skill file records its revision as `metadata.skill_revision` in its
123
+ frontmatter. Export it so the CLI can tell you when your copy has fallen behind
124
+ the guidance the CLI ships:
125
+
126
+ ```bash
127
+ export LUXIN_SKILL_REVISION=<metadata.skill_revision from your installed SKILL.md>
128
+ ```
129
+
130
+ When the value differs from what the running CLI ships, `luxin skill` and
131
+ `create --guide` / `edit --guide` add `data.skill_refresh`:
132
+
133
+ ```json
134
+ {
135
+ "skill_refresh": {
136
+ "installed_revision": "0123456789ab",
137
+ "current_revision": "b1dc1284d18d",
138
+ "refresh_command": "npx skills update luxin"
139
+ }
140
+ }
141
+ ```
142
+
143
+ They also emit one warning line naming the same command. When the revisions
144
+ match, or when `LUXIN_SKILL_REVISION` is unset, the field is omitted entirely.
145
+
146
+ Refreshing is always a command you choose to run. Luxin never rewrites your
147
+ skill files, agent config, or hooks, and never fetches instructions to follow
148
+ them. The revision is also forwarded to the hosted API as a non-secret
149
+ `x-luxin-skill-revision` header and published unauthenticated on
150
+ `GET /healthz` as `skill_revision`, so a client can compare without downloading
151
+ any contract file.
152
+
96
153
  ### `luxin signup --agent`
97
154
 
98
155
  Bootstraps restricted agent access. Signup is anonymous by default: no contact
@@ -280,6 +337,13 @@ recommended (`data.self_fund_preparation.recommended` is `true`), the
280
337
  `data.self_fund_*` fields stay in the compact response and are not listed in
281
338
  `data.output_mode.omitted_fields`.
282
339
 
340
+ `data.skill_refresh` is the other field that survives the compact response, for
341
+ the same reason: an agent whose installed skill file has gone stale is usually
342
+ an agent whose calls are otherwise succeeding. It appears only when
343
+ `LUXIN_SKILL_REVISION` is exported and differs from the revision the running CLI
344
+ ships, and carries `installed_revision`, `current_revision`, and
345
+ `refresh_command`. See [`luxin skill`](#luxin-skill) for the full handshake.
346
+
283
347
  ```bash
284
348
  IMAGE_SKILL_DISCOVERY_SOURCE=cli-md luxin create --guide --prompt "a compact field camera on a stainless workbench"
285
349
  ```
package/commands.json CHANGED
@@ -20,6 +20,13 @@
20
20
  "mutation": false,
21
21
  "spend": false
22
22
  },
23
+ {
24
+ "id": "skill",
25
+ "docs_anchor": "luxin-skill",
26
+ "surface": "client",
27
+ "mutation": false,
28
+ "spend": false
29
+ },
23
30
  {
24
31
  "id": "signup",
25
32
  "docs_anchor": "luxin-signup-agent",
@@ -148,13 +155,13 @@
148
155
  "key": "",
149
156
  "help": {
150
157
  "command": "help",
151
- "usage": "luxin <doctor|trust|signup|claim|whoami|usage|quota|credits|capabilities|models|create|upload|edit|assets|jobs|activity|feedback> --json",
158
+ "usage": "luxin <doctor|trust|skill|signup|claim|whoami|usage|quota|credits|capabilities|models|create|upload|edit|assets|jobs|activity|feedback> --json",
152
159
  "docs_url": "https://luxin.sh/cli.md",
153
160
  "agent_skill": {
154
161
  "url": "https://luxin.sh/skill.md",
155
162
  "llms_url": "https://luxin.sh/llms.txt",
156
163
  "cli_docs_url": "https://luxin.sh/cli.md",
157
- "read_command": "curl -fsSL https://luxin.sh/skill.md",
164
+ "read_command": "luxin skill --json",
158
165
  "recommended_start_command": "luxin create --guide --prompt PROMPT --json",
159
166
  "model_discovery_command": "luxin models list --available --operation image.generate --json",
160
167
  "model_inspection_command": "luxin models show MODEL_ID --json",
@@ -164,6 +171,7 @@
164
171
  "commands": [
165
172
  "doctor",
166
173
  "trust",
174
+ "skill",
167
175
  "signup --agent --agent-name NAME --runtime RUNTIME",
168
176
  "claim request --contact INBOX",
169
177
  "claim code",
@@ -220,6 +228,15 @@
220
228
  "description": "Return npm provenance, hosted contract hashes, API health, and model availability evidence."
221
229
  }
222
230
  },
231
+ {
232
+ "key": "skill",
233
+ "help": {
234
+ "command": "luxin skill help",
235
+ "usage": "luxin skill --json",
236
+ "docs_url": "https://luxin.sh/cli.md#luxin-skill",
237
+ "description": "Print the full agent guidance bundled with this CLI version, so instructions always match the installed code. No network call, no auth, no spend."
238
+ }
239
+ },
223
240
  {
224
241
  "key": "signup",
225
242
  "help": {
package/llms.txt CHANGED
@@ -3,10 +3,14 @@
3
3
  Status: live preview hosted-product contract
4
4
  Canonical homepage: https://luxin.sh
5
5
  Canonical skill URL: https://luxin.sh/skill.md
6
+ Canonical skill guidance URL: https://luxin.sh/skill-core.md
6
7
  Canonical LLM URL: https://luxin.sh/llms.txt
7
8
  Canonical CLI URL: https://luxin.sh/cli.md
8
9
  Canonical hosted API URL: https://api.luxin.sh
9
10
 
11
+ Skill freshness:
12
+ https://luxin.sh/skill.md is a ~4KB discovery stub carrying identity, the first command, the safety invariants, and metadata.skill_revision. The full guidance is https://luxin.sh/skill-core.md, and `luxin skill --json` prints those same bytes from the installed package, so guidance always matches the running CLI. Export LUXIN_SKILL_REVISION=<metadata.skill_revision of your installed copy>: when it differs from what the CLI ships, `luxin skill` and `create --guide` return data.skill_refresh with installed_revision, current_revision, and refresh_command `npx skills update luxin`, plus one warning line. When it matches or is unset the field is omitted. Refreshing is a command you run; Luxin never rewrites skill files, agent config, or hooks. GET https://api.luxin.sh/healthz publishes the current skill_revision unauthenticated.
13
+
10
14
  Purpose:
11
15
  Luxin is zero-setup creative media for agents: no provider API key, no OAuth, no install to manage, and no per-provider billing account. Agents start with the hosted guide, follow one next command, and use one thin CLI/client and API to generate and edit images, generate video and audio (music, sound), and create 3D assets (image-to-3D mesh, glb), get durable owned media URLs, recover jobs, inspect models, cap spend, pay through credits (browserless agent x402 when returned available; Stripe Checkout human handoff as fallback), and leave feedback. If the guide needs restricted agent signup before first media, it returns that command explicitly.
12
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luxin-cli",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Image generation, image editing, video, audio, and image-to-3D for agents, with no API key to obtain first. The agent signs itself up, starts on free preview credits, and gets hosted media URLs, recoverable jobs, and cost receipts.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -21,6 +21,7 @@
21
21
  "CONTRIBUTING.md",
22
22
  "CODE_OF_CONDUCT.md",
23
23
  "skill.md",
24
+ "skill-core.md",
24
25
  "llms.txt",
25
26
  "cli.md",
26
27
  "commands.json",